Air Quality Monitor
Portable C application for collecting, storing, and analyzing air quality sensor readings
Loading...
Searching...
No Matches
mhz19_plugin.c
1#include "../include/sensor/sensor.h"
2#include <errno.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <time.h>
7#ifndef _WIN32
8#include <fcntl.h>
9#include <termios.h>
10#include <unistd.h>
11#ifndef CRTSCTS
12#define CRTSCTS 0
13#endif
14#endif
15
16#ifdef _WIN32
17#define SENSOR_PLUGIN_EXPORT __declspec(dllexport)
18#else
19#define SENSOR_PLUGIN_EXPORT
20#endif
21
22static void fill_timestamp(char *buf, size_t len) {
23 time_t now = time(NULL);
24 const struct tm *ptm = localtime(&now);
25 if (!ptm) {
26 snprintf(buf, len, "1970-01-01 00:00:00");
27 return;
28 }
29 if (strftime(buf, len, "%Y-%m-%d %H:%M:%S", ptm) == 0) {
30 snprintf(buf, len, "1970-01-01 00:00:00");
31 }
32}
33
34static int mhz19_init(void) {
35#ifndef _WIN32
36 printf("MH-Z19 plugin initialized (real UART on Linux/macOS if available; fallback mock).\n");
37#else
38 printf("MH-Z19 plugin initialized (mock on Windows).\n");
39#endif
40 return 0;
41}
42
43#ifndef _WIN32
44static int open_serial_9600(const char *device) {
45 int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);
46 if (fd < 0)
47 return -1;
48 struct termios tty;
49 memset(&tty, 0, sizeof(tty));
50 if (tcgetattr(fd, &tty) != 0) {
51 close(fd);
52 return -1;
53 }
54 cfsetospeed(&tty, B9600);
55 cfsetispeed(&tty, B9600);
56 tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
57 tty.c_iflag &= ~(IGNBRK | IXON | IXOFF | IXANY);
58 tty.c_lflag = 0;
59 tty.c_oflag = 0;
60 tty.c_cc[VMIN] = 9;
61 tty.c_cc[VTIME] = 2;
62 tty.c_cflag |= (CLOCAL | CREAD);
63 tty.c_cflag &= ~(PARENB | PARODD | CSTOPB | CRTSCTS);
64 if (tcsetattr(fd, TCSANOW, &tty) != 0) {
65 close(fd);
66 return -1;
67 }
68 tcflush(fd, TCIOFLUSH);
69 return fd;
70}
71
72static unsigned char mhz19_checksum(const unsigned char *buf) {
73 unsigned int sum = 0;
74 for (int i = 1; i < 8; i++)
75 sum += buf[i];
76 return (unsigned char)(0xFF - (sum & 0xFF) + 1);
77}
78
79static int read_mhz19_ppm(float *ppm_out) {
80 const char *dev = getenv("MHZ19_DEVICE");
81 if (!dev || !dev[0])
82 dev = "/dev/ttyS0";
83 int fd = open_serial_9600(dev);
84 if (fd < 0)
85 return -1;
86
87 unsigned char cmd[9] = {0xFF, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79};
88 if (write(fd, cmd, sizeof(cmd)) != (ssize_t)sizeof(cmd)) {
89 close(fd);
90 return -1;
91 }
92
93 unsigned char resp[9];
94 ssize_t got = read(fd, resp, sizeof(resp));
95 close(fd);
96 if (got != (ssize_t)sizeof(resp))
97 return -1;
98 if (resp[0] != 0xFF || resp[1] != 0x86)
99 return -1;
100 if (resp[8] != mhz19_checksum(resp))
101 return -1;
102 int ppm = ((int)resp[2] << 8) | resp[3];
103 if (ppm < 0 || ppm > 10000)
104 return -1;
105 *ppm_out = (float)ppm;
106 return 0;
107}
108#endif
109
110static int mhz19_read_sample(AirQualityData *out) {
111 if (!out)
112 return -1;
113 memset(out, 0, sizeof(*out));
114 out->sensor_id = 1900;
115 snprintf(out->model, sizeof(out->model), "MH-Z19");
116 fill_timestamp(out->timestamp, sizeof(out->timestamp));
117
118 // co stores CO2 ppm from MH-Z19 (or mock fallback).
119 out->pm25 = 0.0f;
120 out->pm10 = 0.0f;
121 out->co = 400.0f + (float)(rand() % 2600);
122#ifndef _WIN32
123 float real_ppm = 0.0f;
124 if (read_mhz19_ppm(&real_ppm) == 0)
125 out->co = real_ppm;
126#endif
127 out->no2 = 0.0f;
128 out->o3 = 0.0f;
129 out->so2 = 0.0f;
130 return 0;
131}
132
133static int mhz19_shutdown(void) {
134 printf("MH-Z19 plugin shutdown.\n");
135 return 0;
136}
137
138SENSOR_PLUGIN_EXPORT SensorPlugin sensor_plugin = {
139 .api_version = SENSOR_PLUGIN_API_VERSION,
140 .name = "MH-Z19",
141 .plugin_version = "1.0.0",
142 .description = "Simulated MH-Z19 CO2 ppm readings mapped to co field.",
143 .sensor_id = 1900,
144 .init = mhz19_init,
145 .read_sample = mhz19_read_sample,
146 .shutdown = mhz19_shutdown,
147};
148
One air quality reading: pollutant values plus sensor/timestamp metadata.
Definition globals.h:27
char timestamp[32]
Reading timestamp, formatted "%Y-%m-%d %H:%M:%S".
Definition globals.h:31
char model[64]
Sensor model name, e.g.
Definition globals.h:33
int sensor_id
Numeric sensor identifier, matches SensorPlugin::sensor_id.
Definition globals.h:29
Runtime contract every sensor plugin (.so/.dylib/.dll) must implement.
Definition sensor.h:35
int api_version
Must equal SENSOR_PLUGIN_API_VERSION at build time.
Definition sensor.h:37