Air Quality Monitor
Portable C application for collecting, storing, and analyzing air quality sensor readings
Loading...
Searching...
No Matches
bme680_plugin.c
1#include "../include/sensor/sensor.h"
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <time.h>
6#ifndef _WIN32
7#include <dirent.h>
8#endif
9
10#ifdef _WIN32
11#define SENSOR_PLUGIN_EXPORT __declspec(dllexport)
12#else
13#define SENSOR_PLUGIN_EXPORT
14#endif
15
16static void fill_timestamp(char *buf, size_t len) {
17 time_t now = time(NULL);
18 const struct tm *ptm = localtime(&now);
19 if (!ptm) {
20 snprintf(buf, len, "1970-01-01 00:00:00");
21 return;
22 }
23 snprintf(buf, len, "%04d-%02d-%02d %02d:%02d:%02d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
24 ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
25}
26
27static int bme680_init(void) {
28#ifndef _WIN32
29 printf("BME680 plugin initialized (real IIO/sysfs on Linux if available; fallback mock).\n");
30#else
31 printf("BME680 plugin initialized (mock on Windows).\n");
32#endif
33 return 0;
34}
35
36#ifndef _WIN32
37static int read_float_file(const char *path, float *out) {
38 FILE *f = fopen(path, "r");
39 if (!f)
40 return -1;
41 float v = 0.0f;
42 int ok = fscanf(f, "%f", &v);
43 fclose(f);
44 if (ok != 1)
45 return -1;
46 *out = v;
47 return 0;
48}
49
50static int try_read_bme680_iio(float *temp_c, float *hum_pct, float *gas_kohm) {
51 const char *forced = getenv("BME680_IIO_PATH");
52 if (forced && forced[0]) {
53 char path[512];
54 float t = 0.0f, h = 0.0f, g = 0.0f;
55 snprintf(path, sizeof(path), "%s/in_temp_input", forced);
56 if (read_float_file(path, &t) != 0)
57 return -1;
58 snprintf(path, sizeof(path), "%s/in_humidityrelative_input", forced);
59 if (read_float_file(path, &h) != 0)
60 return -1;
61 snprintf(path, sizeof(path), "%s/in_resistance_input", forced);
62 if (read_float_file(path, &g) != 0)
63 g = 0.0f;
64 *temp_c = t / 1000.0f;
65 *hum_pct = h / 1000.0f;
66 *gas_kohm = g / 1000.0f;
67 return 0;
68 }
69
70 DIR *d = opendir("/sys/bus/iio/devices");
71 if (!d)
72 return -1;
73 const struct dirent *ent;
74 while ((ent = readdir(d)) != NULL) {
75 if (strncmp(ent->d_name, "iio:device", 10) != 0)
76 continue;
77 char base[512], namef[640], name[128];
78 snprintf(base, sizeof(base), "/sys/bus/iio/devices/%s", ent->d_name);
79 snprintf(namef, sizeof(namef), "%s/name", base);
80 FILE *f = fopen(namef, "r");
81 if (!f)
82 continue;
83 name[0] = '\0';
84 fgets(name, sizeof(name), f);
85 fclose(f);
86 if (strstr(name, "bme680") == NULL && strstr(name, "BME680") == NULL)
87 continue;
88
89 float t = 0.0f, h = 0.0f, g = 0.0f;
90 char p[640];
91 snprintf(p, sizeof(p), "%s/in_temp_input", base);
92 if (read_float_file(p, &t) != 0)
93 continue;
94 snprintf(p, sizeof(p), "%s/in_humidityrelative_input", base);
95 if (read_float_file(p, &h) != 0)
96 continue;
97 snprintf(p, sizeof(p), "%s/in_resistance_input", base);
98 if (read_float_file(p, &g) != 0)
99 g = 0.0f;
100 *temp_c = t / 1000.0f;
101 *hum_pct = h / 1000.0f;
102 *gas_kohm = g / 1000.0f;
103 closedir(d);
104 return 0;
105 }
106 closedir(d);
107 return -1;
108}
109#endif
110
111static int bme680_read_sample(AirQualityData *out) {
112 if (!out)
113 return -1;
114 memset(out, 0, sizeof(*out));
115 out->sensor_id = 680;
116 snprintf(out->model, sizeof(out->model), "BME680");
117 fill_timestamp(out->timestamp, sizeof(out->timestamp));
118
119 // Mapping: pm25=temperature(C), pm10=humidity(%), co=gas resistance(kOhm).
120 out->pm25 = 20.0f + (float)(rand() % 1200) / 100.0f; // 20..31.99 C
121 out->pm10 = 35.0f + (float)(rand() % 5000) / 100.0f; // 35..84.99 %
122 out->co = 0.5f + (float)(rand() % 400) / 100.0f; // 0.5..4.49 proxy
123#ifndef _WIN32
124 float rt = 0.0f, rh = 0.0f, rg = 0.0f;
125 if (try_read_bme680_iio(&rt, &rh, &rg) == 0) {
126 out->pm25 = rt;
127 out->pm10 = rh;
128 if (rg > 0.0f)
129 out->co = rg;
130 }
131#endif
132 out->no2 = 0.0f;
133 out->o3 = 0.0f;
134 out->so2 = 0.0f;
135 return 0;
136}
137
138static int bme680_shutdown(void) {
139 printf("BME680 plugin shutdown.\n");
140 return 0;
141}
142
143SENSOR_PLUGIN_EXPORT SensorPlugin sensor_plugin = {
144 .api_version = SENSOR_PLUGIN_API_VERSION,
145 .name = "BME680",
146 .plugin_version = "1.0.0",
147 .description = "Simulated BME680 readings (temperature/humidity/VOC proxy).",
148 .sensor_id = 680,
149 .init = bme680_init,
150 .read_sample = bme680_read_sample,
151 .shutdown = bme680_shutdown,
152};
153
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