Created
November 10, 2023 18:19
-
-
Save henkman/b8d8ab12afe8adfdf6b5414aba7bb2a0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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