Skip to content

Instantly share code, notes, and snippets.

@henkman
Created November 10, 2023 18:19
Show Gist options
  • Save henkman/b8d8ab12afe8adfdf6b5414aba7bb2a0 to your computer and use it in GitHub Desktop.
Save henkman/b8d8ab12afe8adfdf6b5414aba7bb2a0 to your computer and use it in GitHub Desktop.
// g++ -s -O2 -o co2monitor main.cc -lfltk -lhidapi
#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Tile.H>
#include <hidapi/hidapi.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#if _WIN32
#define WIN32_LEAN_AND_MEAN
#include <process.h>
#include <windows.h>
typedef unsigned long Fl_Thread;
extern "C" {
typedef void *(__cdecl Fl_Thread_Func)(void *);
}
static int fl_create_thread(Fl_Thread &t, Fl_Thread_Func *f, void *p) {
return t = (Fl_Thread)_beginthread((void(__cdecl *)(void *))f, 0, p);
}
#elif HAVE_PTHREAD_H
typedef pthread_t Fl_Thread;
extern "C" {
typedef void *(Fl_Thread_Func)(void *);
}
static int fl_create_thread(Fl_Thread &t, Fl_Thread_Func *f, void *p) {
return pthread_create((pthread_t *)&t, 0, f, p);
}
#else
#error "Install pthreads"
#endif
typedef struct {
double TemperatureKelvin;
uint16_t CO2PPM;
} Reading;
static double ReadingTemperatureCelcius(Reading &r) {
return r.TemperatureKelvin - 273.15;
}
typedef struct {
hid_device *dev;
unsigned char key[9];
unsigned char buf[8];
} CO2Monitor;
static void CO2MonitorInit(CO2Monitor *cm) {
cm->dev = hid_open(0x04d9, 0xa052, NULL);
hid_send_feature_report(cm->dev, cm->key, 9);
}
static void CO2MonitorClose(CO2Monitor *cm) { hid_close(cm->dev); }
static void CO2MonitorRead(CO2Monitor *cm, Reading *r) {
bool readTemp = false;
bool readCO2 = false;
while (hid_read(cm->dev, cm->buf, sizeof(char) * 8) > 0) {
uint8_t first = cm->buf[2] ^ cm->key[0];
uint8_t last = cm->buf[3] ^ cm->key[7];
uint8_t unit = ((first >> 3) | (last << 5)) - 0x84;
if (unit == 0x50) {
uint8_t second = cm->buf[4] ^ cm->key[1];
uint8_t third = cm->buf[0] ^ cm->key[2];
uint8_t high = ((second >> 3) | (first << 5)) - 0x47;
uint8_t low = ((third >> 3) | (second << 5)) - 0x56;
uint16_t value = (((uint16_t)high) << 8) | ((uint16_t)low);
r->CO2PPM = value;
if (readTemp)
break;
readCO2 = true;
} else if (unit == 0x42) {
uint8_t second = cm->buf[4] ^ cm->key[1];
uint8_t third = cm->buf[0] ^ cm->key[2];
uint8_t high = ((second >> 3) | (first << 5)) - 0x47;
uint8_t low = ((third >> 3) | (second << 5)) - 0x56;
uint16_t value = (((uint16_t)high) << 8) | ((uint16_t)low);
r->TemperatureKelvin = ((double)(value)) / 16.0;
if (readCO2)
break;
readTemp = true;
}
}
}
static Fl_Thread thread;
static Fl_Window *win;
static Fl_Box *tempBox;
static Fl_Box *co2Box;
extern "C" {
static void *readFunc(void *p) {
CO2Monitor cm = {0};
Reading r;
hid_init();
CO2MonitorInit(&cm);
for (;;) {
CO2MonitorRead(&cm, &r);
char fullString[21]; // -20.16 C | 12345 PPM
sprintf(fullString, "%0.2f C | %d PPM", ReadingTemperatureCelcius(r),
r.CO2PPM);
win->label(fullString);
char *tempString = fullString;
char *co2String = strchr(fullString, '|');
tempString[(co2String - fullString) - 1] = 0;
co2String++;
Fl::lock();
co2Box->label(co2String);
if (r.CO2PPM < 400)
co2Box->labelcolor(fl_rgb_color(0x29, 0xAB, 0x87));
else if (r.CO2PPM < 600)
co2Box->labelcolor(fl_rgb_color(0x22, 0x8B, 0x22));
else if (r.CO2PPM < 800)
co2Box->labelcolor(fl_rgb_color(0x56, 0x82, 0x03));
else if (r.CO2PPM < 1000)
co2Box->labelcolor(fl_rgb_color(0xFD, 0xFF, 0x00));
else if (r.CO2PPM < 1200)
co2Box->labelcolor(fl_rgb_color(0xFD, 0x32, 0x0C));
else
co2Box->labelcolor(fl_rgb_color(0xFF, 0x00, 0x00));
tempBox->label(tempString);
Fl::unlock();
Fl::awake();
}
}
}
int main(int argc, char **argv) {
const int W = 560;
const int H = 350;
win = new Fl_Double_Window(W, H, "0.0 C | XXX PPM");
{
tempBox = new Fl_Box(10, 30, W - 20, 160, "0.0 C");
tempBox->labelsize(120);
co2Box = new Fl_Box(10, 170, W - 20, 160, "XXX PPM");
co2Box->labelsize(120);
}
win->color(fl_rgb_color(0xcc, 0xcc, 0xcc));
win->position((Fl::w() - win->w()) / 2, (Fl::h() - win->h()) / 2);
Fl::lock();
fl_create_thread(thread, readFunc, NULL);
win->show(argc, argv);
return Fl::run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment