#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bme280.h" struct data_entry { time_t time; double temperature; double humidity; double pressure; double dew_point; }; struct bme280 { struct bme280_dev *dev; uint8_t dev_addr; int8_t fd; struct data_entry data[8192]; pthread_mutex_t lock; int epollfd; }; struct event_handler; typedef int (handle_event_fn_t)(struct event_handler *); struct event_handler { struct epoll_event ev; handle_event_fn_t *handle_event; char *name; }; int exit_all = 0; #define EXTRA_DEBUG 0 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) int register_event_handler(struct bme280 *bme, struct event_handler *handler) { struct epoll_event ev; int ret; bzero(&ev, sizeof(ev)); if (handler->ev.data.fd <= 0) { printf("Invalid file descriptor of %d\n", handler->ev.data.fd); return -1; } if (!handler->handle_event) { printf("Handler callback missing\n"); return -1; } if (EXTRA_DEBUG) printf("Registering handler for %s, fd %d\n", handler->name, handler->ev.data.fd); ev.data.fd = handler->ev.data.fd; ev.data.ptr = handler; ev.events = handler->ev.events; ret = epoll_ctl(bme->epollfd, EPOLL_CTL_ADD, handler->ev.data.fd, &ev); if (ret) { printf("Failed to add epoll_fd: %m\n"); return -1; } return 0; } int update_event_handler(struct bme280 *bme, struct event_handler *handler) { struct epoll_event ev; int ret; bzero(&ev, sizeof(ev)); ev.data.fd = handler->ev.data.fd; ev.data.ptr = handler; ev.events = handler->ev.events; ret = epoll_ctl(bme->epollfd, EPOLL_CTL_MOD, handler->ev.data.fd, &ev); if (ret) { printf("Failed to add epoll_fd: %m\n"); return -1; } return 0; } struct connection_state { struct event_handler ev; char buf[128]; int len; struct bme280 *bme; int fd; time_t min_timestamp; }; static int handle_connection_state(struct event_handler *ptr) { struct connection_state *conn = (struct connection_state *)ptr; struct bme280 *bme = conn->bme; int len = 0; int ret, i; if (conn->min_timestamp == -1) { if (!conn->ev.ev.events & EPOLLIN) { printf("%s: No incoming data\n", __func__); return 0; } ret = read(conn->fd, conn->buf + conn->len, sizeof(conn->buf) - conn->len); if (ret < 0) { printf("%s: read: %m\n", __func__); return -1; } conn->buf[min((signed)sizeof(conn->buf) - 1, ret)] = '\0'; if (ret == 0) goto out_free; for (i = 0; i < ret; i++) if (conn->buf[i] == '\n') break; /* Did we get newline? */ if (i == ret) return 0; /* Not yet */ if (i == sizeof(conn->buf)) { printf("%s Data overflow\n", __func__); goto out_free; } if (EXTRA_DEBUG) printf("%s: Got %s", __func__, conn->buf); conn->min_timestamp = atoi(conn->buf); } /* Switch to sending data only mode only */ conn->ev.ev.events = EPOLLOUT; conn->ev.ev.data.fd = conn->fd; update_event_handler(bme, &conn->ev); for (i = 0; i < (signed)ARRAY_SIZE(bme->data); i++) { pthread_mutex_lock(&bme->lock); /* Skip empty */ if (!bme->data[i].time) { pthread_mutex_unlock(&bme->lock); continue; } /* Skip non-interesting or already sent items */ if (bme->data[i].time <= conn->min_timestamp) { pthread_mutex_unlock(&bme->lock); continue; } len = snprintf(conn->buf, sizeof(conn->buf), "%ld:%0.4lf:%0.4lf:%0.4lf:%.4lf\n", bme->data[i].time, bme->data[i].temperature, bme->data[i].pressure, bme->data[i].humidity, bme->data[i].dew_point); ret = send(conn->fd, conn->buf, len, MSG_NOSIGNAL); if (ret < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { pthread_mutex_unlock(&bme->lock); return 0; } printf("%s: send(): %m\n", __func__); pthread_mutex_unlock(&bme->lock); goto out_free; } conn->min_timestamp = bme->data[i].time; pthread_mutex_unlock(&bme->lock); } out_free: if (EXTRA_DEBUG) printf("%s: Closing socket %d\n", __func__, conn->fd); close(conn->fd); free(conn); return 0; } struct listening_socket { struct event_handler ev; int fd; struct bme280 *bme; }; static int handle_incoming_connection(struct event_handler *ptr) { struct sockaddr_in peer; socklen_t peerlen = 0; struct listening_socket *listener = (struct listening_socket *)ptr; struct connection_state *conn; struct bme280 *bme = listener->bme; int fd; bzero(&peer, sizeof(peer)); fd = accept4(listener->fd, (struct sockaddr *)&peer, &peerlen, SOCK_NONBLOCK); if (fd < 0) { printf("Error while accept(): %m\n"); return -1; } conn = calloc(sizeof(*conn), 1); conn->ev.ev.data.fd = fd; conn->fd = fd; conn->ev.ev.events = EPOLLIN; conn->ev.handle_event = handle_connection_state; conn->ev.name = "socket"; conn->bme = bme; conn->min_timestamp = -1; conn->len = 0; register_event_handler(bme, &conn->ev); return 0; } static void *event_handler(void *arg) { struct bme280 *bme = arg; struct sockaddr_in addr; struct listening_socket incoming; int sockfd, ret; int enable = 1; bzero(&addr, sizeof(addr)); bzero(&incoming, sizeof(incoming)); sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if (sockfd < 0) { printf("Failed to create socket: %m\n"); goto out; } ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); if (ret < 0) printf("Error setting SO_REUSEADDR: %m\n"); addr.sin_family = AF_INET; addr.sin_port = htons(8347); addr.sin_addr.s_addr = INADDR_ANY; ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { printf("Failed to bind: %m\n"); goto close_socket; } ret = listen(sockfd, 5); if (ret < 0) { printf("Failed to listen(): %m\n"); goto close_socket; } bme->epollfd = epoll_create(1); if (bme->epollfd == -1) { printf("Failed to epoll_create(): %m\n"); goto close_socket; } incoming.bme = bme; incoming.ev.ev.data.fd = sockfd; incoming.ev.ev.events = EPOLLIN; incoming.ev.handle_event = handle_incoming_connection; incoming.ev.name = "listener"; incoming.fd = sockfd; ret = register_event_handler(bme, &incoming.ev); while(1) { struct epoll_event ev; struct event_handler *h; if (EXTRA_DEBUG) printf("%s: Waiting for events..\n", __func__); ret = epoll_wait(bme->epollfd, &ev, 1, -1); if (ret == -1) { if (errno != EINTR) { printf("epoll_wait: %m\n"); goto close_socket; } continue; } if (ret == 0) { printf("Timed out\n"); continue; } h = ev.data.ptr; if (EXTRA_DEBUG) printf("Handling %s %s event for %s\n", ev.events & EPOLLIN ? "incoming" : "", ev.events & EPOLLOUT ? "outgoing" : "", h->name); h->ev = ev; h->handle_event(h); } close_socket: close(sockfd); out: exit_all = 1; return NULL; } static int8_t i2c_read(uint8_t reg_addr, uint8_t *data, uint32_t len, void *intf_ptr) { struct bme280 id; int ret; id = *((struct bme280 *)intf_ptr); ret = write(id.fd, ®_addr, 1); if (ret < 0) return ret; ret = read(id.fd, data, len); if (ret < 0) return ret; return 0; } static void delay_us(uint32_t period, void *unused) { (void)unused; usleep(period); } static int8_t i2c_write(uint8_t reg_addr, const uint8_t *data, uint32_t len, void *intf_ptr) { uint8_t *buf; struct bme280 id; id = *((struct bme280 *)intf_ptr); buf = malloc(len + 1); buf[0] = reg_addr; memcpy(buf + 1, data, len); if (write(id.fd, buf, len + 1) < (uint16_t)len) { return BME280_E_COMM_FAIL; } free(buf); return BME280_OK; } static double dp(double RH, double T) { double gm, dp; /* * Constants and equation taken from: * https://en.wikipedia.org/wiki/Dew_point#Calculating_the_dew_point */ /* double a = 6.112;*/ double b, c; double d = 234.5; if (T > 0) { b = 17.368; c = 238.88; } else { b = 17.966; c = 247.15; } gm = log(RH / 100.0 * exp((b - T / d) * (T / (c + T)))); dp = c * gm / (b - gm); return dp; } static int stream_sensor_data_forced_mode(struct bme280 *bme) { int ret; uint8_t settings_sel = 0; uint32_t req_delay; struct bme280_data comp_data; struct bme280_dev *dev = bme->dev; double t_sum = 0, h_sum = 0, p_sum = 0; int num = 0, first_run = 1, last_min; /* Recommended mode of operation: Indoor navigation */ dev->settings.osr_h = BME280_OVERSAMPLING_1X; dev->settings.osr_p = BME280_OVERSAMPLING_16X; dev->settings.osr_t = BME280_OVERSAMPLING_2X; dev->settings.filter = BME280_FILTER_COEFF_16; settings_sel = BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL; ret = bme280_set_sensor_settings(settings_sel, dev); if (ret) { fprintf(stderr, "Failed to set sensor settings (code %+d).", ret); return ret; } printf("Temperature, Pressure, Humidity\n"); /* * Calculate the minimum delay required between consecutive * measurement based upon the sensor enabled and the * oversampling configuration. */ req_delay = bme280_cal_meas_delay(&dev->settings); while (1) { struct tm *now; time_t t; ret = 0; if (exit_all) break; ret = bme280_set_sensor_mode(BME280_FORCED_MODE, dev); if (ret) { fprintf(stderr, "Failed to set sensor mode (code %+d).", ret); break; } /* Wait for the measurement to complete and print data */ dev->delay_us(req_delay * 1000, dev->intf_ptr); ret = bme280_get_sensor_data(BME280_ALL, &comp_data, dev); if (ret) { fprintf(stderr, "Failed to get sensor data (code %+d).", ret); break; } t_sum += comp_data.temperature; p_sum += comp_data.pressure; h_sum += comp_data.humidity; num++; t = time(NULL); now = localtime(&t); if (first_run || now->tm_min != last_min) { double temp, press, hum, dew; char s[64]; long unsigned int i; first_run = 0; last_min = now->tm_min; temp = t_sum / (double)num; press = p_sum * 0.01 / (double)num; hum = h_sum / (double)num; strftime(s, sizeof(s), "%Y.%m.%d %H:%M:%S", now); dew = dp(hum, temp); printf("%s %0.4lf deg C, %0.4lf hPa, %0.4lf%%, dp: %.3f C\n", s, temp, press, hum, dew); fflush(stdout); pthread_mutex_lock(&bme->lock); for (i = 0; i < ARRAY_SIZE(bme->data) - 1; i++) bme->data[i] = bme->data[i + 1]; bme->data[i].time = t; bme->data[i].temperature = temp; bme->data[i].pressure = press; bme->data[i].humidity = hum; bme->data[i].dew_point = dew; pthread_mutex_unlock(&bme->lock); num = 0; t_sum = p_sum = h_sum = 0; } } return ret; } int main(int argc, char *argv[]) { pthread_t thread; struct bme280_dev dev; struct bme280 id; int ret; bzero(&id, sizeof(id)); if (argc < 3) { fprintf(stderr, "Missing argument for i2c bus and address.\n" "Usage: %s /dev/i2c-0 0x76\n", argv[0]); exit(1); } bzero(&id, sizeof(id)); if ((id.fd = open(argv[1], O_RDWR)) < 0) { fprintf(stderr, "Failed to open the i2c bus %s\n", argv[1]); exit(1); } id.dev_addr = strtol(argv[2], NULL, 0); if (ioctl(id.fd, I2C_SLAVE, id.dev_addr) < 0) { fprintf(stderr, "Failed to acquire bus access and/or talk to slave.\n"); exit(1); } dev.intf = BME280_I2C_INTF; dev.read = i2c_read; dev.write = i2c_write; dev.delay_us = delay_us; dev.intf_ptr = &id; id.dev = &dev; ret = bme280_init(&dev); if (ret) { fprintf(stderr, "Failed to initialize the device (code %+d).\n", ret); exit(1); } pthread_mutex_init(&id.lock, NULL); pthread_create(&thread, NULL, event_handler, &id); ret = stream_sensor_data_forced_mode(&id); if (ret) { fprintf(stderr, "Failed to stream sensor data (code %+d).\n", ret); exit(1); } }