co2minimon

Get temperature and CO2 concentration data from a CO2Mini sensor.
git clone git://git.amin.space/co2minimon.git
Log | Files | Refs | README | LICENSE

commit 99702eb036df0ebe9e83ef4f91f8e84f0b9fb0d8
parent 1d29753b1e32b940260e386a0c5d3c50ade416cc
Author: amin <dev@aminmesbah.com>
Date:   Sat, 28 May 2022 22:51:45 +0000

Cleanup when error; don't try to handle SIGKILL

FossilOrigin-Name: 220ac21a183fe03f6efb2fe48c51c4a8edc7b3300e49fff0dd93a815e7a894c6
Diffstat:
Mmain.c | 180+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 93 insertions(+), 87 deletions(-)

diff --git a/main.c b/main.c @@ -13,7 +13,6 @@ // TODO: Use syslog.h for logging // TODO: Make a systemd unit for this -// TODO: cleanup on error // https://www.kernel.org/doc/Documentation/hid/hidraw.txt #include <linux/hidraw.h> @@ -31,33 +30,35 @@ static void stop_running(int signal) int main(void) { { - struct sigaction act = { + struct sigaction act = + { .sa_handler = stop_running, }; - sigaction(SIGINT, &act, NULL); - sigaction(SIGKILL, &act, NULL); - sigaction(SIGTERM, &act, NULL); + int failure = sigaction(SIGINT, &act, NULL); + failure |= sigaction(SIGTERM, &act, NULL); + if (failure) + { + printf("ERROR: Failed to register signal handler.\n"); + return 1; + } } + // NOTE: Included udev rules create this symlink to the appropriate hidraw // entry. const char *hid_file_path = "/dev/co2mini0"; const char *temperature_file_path = "/tmp/co2minimon_temp"; const char *co2_file_path = "/tmp/co2minimon_co2"; + bool error_occurred = false; int device_handle = -1; + bool need_send_feature_report = true; + fd_set read_fds; while (running) { if (access(hid_file_path, F_OK) != 0) { - { - fd_set read_fds; - FD_ZERO(&read_fds); - FD_SET(device_handle, &read_fds); - int ready = select(device_handle + 1, &read_fds, NULL, NULL, &(struct timeval){ .tv_sec = 15 }); - assert(ready != -1); - } if (close(device_handle) == 0) { device_handle = -1; @@ -66,7 +67,7 @@ int main(void) unlink(co2_file_path); unlink(temperature_file_path); - sleep(5); + sleep(30); continue; } @@ -76,110 +77,115 @@ int main(void) if (device_handle < 0) { printf("ERROR: Failed to open HID.\n"); - return 1; + goto error; } - // TODO: Try with just a 0 + FD_ZERO(&read_fds); + FD_SET(device_handle, &read_fds); + need_send_feature_report = true; + } + + if (need_send_feature_report) { uint8_t key[8] = {0}; int result = ioctl(device_handle, HIDIOCSFEATURE(sizeof(key)), key); if (result < 0 || result != sizeof(key)) { printf("ERROR: Failed to send feature report.\n"); - return 1; + goto error; } + need_send_feature_report = false; } - fd_set read_fds; - FD_ZERO(&read_fds); - FD_SET(device_handle, &read_fds); errno = 0; int ready = select(device_handle + 1, &read_fds, NULL, NULL, &(struct timeval){ .tv_sec = 15 }); - int bytes_read = 0; - uint8_t data[8] = {0}; if (ready == -1 && errno != EINTR) { - assert(false); - break; + printf("ERROR: Failed to select() device handle.\n"); + goto error; } else if (ready == 0) { - // If we go too long with an empty pipe, the device may - // have stopped sending data. Resend the feature report to - // get it to send data again. A situation where I've - // observed this happening is when I hibernate and resume - // the computer. Upon resumption, all calls to read() will - // block until we resend the feature report. + // select() timed out // - // I'm not sure why this happens, but my guess would be - // that the device stops sending new data if the data - // hasn't been read in a while. - uint8_t key[8] = {0}; - int result = ioctl(device_handle, HIDIOCSFEATURE(sizeof(key)), key); - if (result < 0 || result != sizeof(key)) - { - printf("ERROR: Failed to send feature report.\n"); - return 1; - } + // If we go too long with an empty pipe, the device may have + // stopped sending data. One situation where I've observed this can + // happen is when the system resumes from hibernation. In this + // case, all calls to read() will block until we resend the feature + // report. + // + // I'm not sure why this happens, but my guess would be that the + // device stops sending new data if the data hasn't been read in a + // while. + need_send_feature_report = true; } else { - bytes_read = read(device_handle, &data, sizeof(data)); - } - - uint8_t item = data[0]; - uint8_t msb = data[1]; - uint8_t lsb = data[2]; - uint8_t checksum = data[3]; - uint8_t end = data[4]; - - if (bytes_read == sizeof(data) - && end == 0x0d - && (item == 0x42 || item == 0x50) - && ((item + msb + lsb) & 0xFF) == checksum) - { - printf("write\n"); - uint16_t value = (((uint16_t)msb) << 8) | lsb; - char buf[1024] = {0}; - int str_len = 0; - mode_t create_mode = S_IRUSR | S_IWUSR; - int open_mode = O_WRONLY | O_CREAT | O_TRUNC; - - switch(item) + uint8_t data[8] = {0}; + int bytes_read = read(device_handle, &data, sizeof(data)); + + uint8_t item = data[0]; + uint8_t msb = data[1]; + uint8_t lsb = data[2]; + uint8_t checksum = data[3]; + uint8_t end = data[4]; + + if (bytes_read == sizeof(data) + && end == 0x0d + && (item == 0x42 || item == 0x50) + && ((item + msb + lsb) & 0xFF) == checksum) { - case 0x42: + need_send_feature_report = false; + uint16_t value = (((uint16_t)msb) << 8) | lsb; + char buf[1024] = {0}; + int str_len = 0; + mode_t create_mode = S_IRUSR | S_IWUSR; + int open_mode = O_WRONLY | O_CREAT | O_TRUNC; + + switch(item) { - printf("bytes T\n"); - double t_celsius = value / 16.0 - 273.15; - str_len = snprintf(buf, sizeof(buf), "%.2f", t_celsius); - assert(str_len > 0); - int f = open(temperature_file_path, open_mode, create_mode); - if (f == -1) + case 0x42: { - return 1; - } - write(f, buf, str_len); - close(f); - } break; - - case 0x50: - { - printf("bytes C\n"); - str_len = snprintf(buf, sizeof(buf), "%d", value); - assert(str_len > 0); - int f = open(co2_file_path, open_mode, create_mode); - if (f == -1) + printf("bytes T\n"); + double t_celsius = value / 16.0 - 273.15; + str_len = snprintf(buf, sizeof(buf), "%.2f", t_celsius); + assert(str_len > 0); + int f = open(temperature_file_path, open_mode, create_mode); + if (f == -1) + { + printf("ERROR: Failed to open output file for temperature.\n"); + goto error; + } + write(f, buf, str_len); + close(f); + } break; + + case 0x50: { - return 1; - } - write(f, buf, str_len); - close(f); - } break; + printf("bytes C\n"); + str_len = snprintf(buf, sizeof(buf), "%d", value); + assert(str_len > 0); + int f = open(co2_file_path, open_mode, create_mode); + if (f == -1) + { + printf("ERROR: Failed to open output file for CO2.\n"); + goto error; + } + write(f, buf, str_len); + close(f); + } break; + } } } } + goto cleanup; + +error: + error_occurred = true; +cleanup: unlink(co2_file_path); unlink(temperature_file_path); close(device_handle); - return 0; + + return error_occurred; }