2026-07-24 16:39:26 +02:00

136 lines
6.1 KiB
C

#ifndef MPS_SENSOR_H
#define MPS_SENSOR_H
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "config.h"
// =====================================================
// MPS SENSOR COMMAND IDs
// =====================================================
#define MPS_CMD_ANSWER 0x01 // Returns a complete answer in one read
#define MPS_CMD_CONC 0x03 // Returns flammable gas concentration [%LEL]
#define MPS_CMD_ID 0x04 // Returns flammable gas ID
#define MPS_CMD_TEMP 0x21 // Returns ambient temperature (C)
#define MPS_CMD_PRES 0x22 // Returns ambient pressure (kPa)
#define MPS_CMD_REL_HUM 0x23 // Returns ambient relative humidity (%RH)
#define MPS_CMD_ABS_HUM 0x24 // Returns ambient absolute humidity (g/m3)
#define MPS_CMD_STATUS 0x41 // Returns MPS status code
#define MPS_CMD_VERSION 0x42 // Returns SW / HW / protocol versions
#define MPS_CMD_SENSOR_INFO 0x43 // Returns sensor serial number and info
#define MPS_CMD_MEAS 0x61 // Sets sensing mode and concentration unit
#define MPS_CMD_RESET 0x62 // Soft reset — clears all data and states
// =====================================================
// MPS MEASUREMENT MODE (payload for MPS_CMD_MEAS)
// =====================================================
// CONFIRMED from NevadaNano MPS 5.0 User Manual, Table 12.
// Only two modes are documented; there is no "idle" or "single" mode.
#define MPS_MEAS_MODE_CONT 0x2 // MPS_CONT — continuous autonomous mode
#define MPS_MEAS_MODE_STOP 0x3 // MPS_STOP — stop measurement
// CONFIRMED from User Manual, Table 11.
#define MPS_CONC_UNIT_LEL_ISO 0x0 // %LEL per ISO 10156
#define MPS_CONC_UNIT_LEL_IEC 0x2 // %LEL per IEC 60079-20-1
// =====================================================
// MPS STATUS CODES
// =====================================================
#define MPS_STATUS_OK 0x00
#define MPS_STATUS_CRC_FAILED 0x01
#define MPS_STATUS_BAD_PARAMETER 0x02
#define MPS_STATUS_EXECUTION_FAILED 0x03
#define MPS_STATUS_NO_MEMORY 0x04
#define MPS_STATUS_UNKNOWN_COMMAND 0x05
#define MPS_STATUS_INCOMPLETE_COMMAND 0x07
#define MPS_STATUS_HW_ERR_AO 0x20
#define MPS_STATUS_HW_ERR_VDD 0x21
#define MPS_STATUS_HW_ERR_VREF 0x22
#define MPS_STATUS_HW_ENV_XCD_RANGE 0x23
#define MPS_STATUS_HW_ENV_SNSR_MALFUNCTION 0x24
#define MPS_STATUS_HW_ERR_MCU 0x25
#define MPS_STATUS_SENSOR_INITIALIZATION 0x26
#define MPS_STATUS_SENSOR_STARTUP 0x27
#define MPS_STATUS_SENSOR_NEGATIVE 0x30
#define MPS_STATUS_CONDENSATION_DETECTED 0x31
#define MPS_STATUS_HW_SENSOR_MALFUNCTION 0x32
#define MPS_STATUS_GAS_DETECTED_DURING_STARTUP 0x33
#define MPS_STATUS_SLOW_GAS_ACCUMULATION 0x34
#define MPS_STATUS_BREATH_OR_HUMIDITY_SURGE 0x35
#define MPS_STATUS_WATCHDOG_MCU_RESET 0x36
#define MPS_STATUS_HW_ERR_WATCHDOG 0x37
#define MPS_STATUS_HW_ERR_DAC_ADC_XCD_RANGE 0x38
// =====================================================
// ALARM LEVELS
// =====================================================
typedef enum {
MPS_ALARM_NONE = 0,
MPS_ALARM_WARNING = 1,
MPS_ALARM_ALARM = 2,
MPS_ALARM_HIGH = 3
} MpsAlarmLevel_t;
// =====================================================
// PACKET STRUCTURES
// =====================================================
// CONFIRMED from NevadaNano MPS 5.0 User Manual, Tables 2 & 3.
// All multi-byte integers are Little-Endian on the wire.
//
// Request header (8 bytes): CmdID(2) + Length(2) + Reserved(2) + Checksum(2)
// Reply header (6 bytes): CmdID(1) + Status(1) + Length(2) + Checksum(2)
//
// NOTE: CmdID in the REQUEST is 2 bytes (high byte always 0x00) for alignment,
// even though only 1 byte is meaningful. This differs from the REPLY, where
// CmdID is 1 byte. Mixing these up breaks the CRC and the sensor will return
// CRC_FAILED (0x01) or simply not respond as expected.
typedef struct __attribute__((packed)) {
uint16_t cmdID; // Command ID, low byte significant, high byte = 0x00
uint16_t length; // Payload length in bytes (0 if no payload)
uint16_t reserved; // Reserved for future use, zero-filled
uint16_t checksum; // CRC-16/CCITT over entire packet (this field zeroed during calc)
uint8_t payload[MPS_PACKET_MAX_PAYLOAD];
} MpsRequest_t;
typedef struct __attribute__((packed)) {
uint8_t cmdID; // Echo of command ID (1 byte in replies)
uint8_t status; // MPS_STATUS_* code
uint16_t length; // Payload length in bytes (0 if no payload)
uint16_t checksum; // CRC-16/CCITT over entire packet (this field zeroed during calc)
uint8_t payload[MPS_PACKET_MAX_PAYLOAD];
} MpsReply_t;
// =====================================================
// SENSOR DATA
// =====================================================
typedef struct {
uint32_t cycle_count; // Measurement cycle number; compare to detect repeats
float concentration_lel;
uint8_t gas_id;
float temperature_c;
float pressure_kpa;
float rel_humidity_pct;
float abs_humidity_gm3;
uint8_t sensor_status;
MpsAlarmLevel_t alarm_level;
bool data_valid;
} MpsSensorData_t;
// =====================================================
// FUNCTION DECLARATIONS
// =====================================================
bool MPS_Init(void);
bool MPS_Reset(void);
bool MPS_SetMeasurementMode(uint8_t mode, uint8_t unit);
bool MPS_ReadStatus(uint8_t *status);
bool MPS_ReadAll(MpsSensorData_t *data);
bool MPS_ReadConcentration(float *conc_lel);
bool MPS_ReadEnvironmental(MpsSensorData_t *data);
void MPS_EvaluateAlarm(MpsSensorData_t *data);
const char *MPS_StatusString(uint8_t status);
uint16_t MPS_ComputeCRC(const uint8_t *data, uint16_t len);
#endif // MPS_SENSOR_H