#ifndef MPS_SENSOR_H #define MPS_SENSOR_H #pragma once #include #include #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