Update comments in main.c header
[soft/lpc82x/exanh] / host / exanh_datalog / main.c
1 /****************************************************************************
2  *  Get data from sensors and decode
3  *
4  *   main.c
5  *
6  * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  ****************************************************************************/
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <termios.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <getopt.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/select.h>
37 #include <sys/time.h>
38 #include <arpa/inet.h>
39 #include <time.h>
40 #include "serial_utils.h"
43 #define BUF_SIZE  100
45 #define PROG_NAME  "Sensors polling and decode"
46 #define VERSION  "0.1"
48 #define MIN_SECS_SINCE_EPOCH 1580000000
49 #define MAX_RETRY  10
51 /* Set to 0, 1 or 2 to change debug level */
52 int debug = 0;
54 struct sensor_info {
55         uint8_t addr;
56         uint8_t got_tsl;
57         uint8_t got_veml;
58         uint8_t got_bme;
59         uint16_t raw_humidity; /* Soil */
60         uint16_t lux;
61         uint16_t ir;
62         uint16_t uv;
63         uint16_t pressure;
64         int16_t comp_temp;
65         uint16_t humidity; /* From BME */
66         uint16_t seqnum;
67 };
69 void help(char *prog_name)
70 {
71         fprintf(stderr, "---------------- "PROG_NAME" --------------------------------\n");
72         fprintf(stderr, "Usage: %s [options]\n" \
73                 "  Available options:\n" \
74                 "  \t -d | --device : Serial device to use for serial communication with the module\n" \
75                 "  \t -c | --chain : Sensor Chain number (used to store data to the right directory)\n" \
76                 "  \t -p | --period : delay in seconds between two sensors data request\n" \
77                 "  \t -V | --verbose : be more verbose\n" \
78                 "  \t -D | --debug : add full debug\n" \
79                 "  \t -h | --help : Print this help\n" \
80                 "  \t -v | --version : Print programm version\n" \
81                 "  All other arguments are data for the command, handled depending on the command.\n", prog_name);
82         fprintf(stderr, "-----------------------------------------------------------------------\n");
83 }
86 /*****************************************************************************/
87 /* Address handling */
89 #define NB_ADDRESS 8
90 uint8_t addr_list[NB_ADDRESS + 1];
91 uint8_t addr_retry[NB_ADDRESS + 1];
92 uint16_t last_seq_num[NB_ADDRESS + 1];
93 uint8_t probe_sensors[NB_ADDRESS + 1];
94 int pkt_count[NB_ADDRESS];
96 int get_next_free_addr(void)
97 {
98         int i = 0;
99         /* Start at 1, do not use address 0 */
100         for (i = 1; i < NB_ADDRESS; i++) {
101                 if (addr_list[i] == 0) {
102                         addr_list[i] = i;
103                         return i;
104                 }
105         }
106         return -1;
109 /*****************************************************************************/
110 /* Requests to sensors */
112 void send_request(int slave_fd, int addr_index)
114         char buf[4];
116         buf[0] = '#';
117         buf[1] = addr_index;
118         buf[2] = 'a'; /* Request all sensor info */
120         write(slave_fd, buf, 3);
121         if (debug) {
122                 printf("Sent request to %d(%d)\n", addr_index, addr_list[addr_index]);
123         }
126 void send_new_address(int slave_fd)
128         char buf[4];
130         buf[0] = '#';
131         buf[1] = 0xFF;
132         buf[2] = get_next_free_addr();
134         write(slave_fd, buf, 3);
135         if (debug) {
136                 printf("New address sent: %d\n", buf[2]);
137         }
140 void set_led(int slave_fd, struct sensor_info* info)
142         uint8_t buf[6];
143         int red = 0;
144         int green = 0;
146         red = (info->raw_humidity - 1500) / 10;
147         green = 200 - red;
148         buf[0] = '#';
149         buf[1] = info->addr;
150         buf[2] = 'l';
151         buf[3] = red; /* Red */
152         buf[4] = green; /* Green */
153         buf[5] = 0; /* Blue */
155         write(slave_fd, buf, 6);
156         if (debug) {
157                 printf("Led color sent: %d : %d - %d\n", buf[1], buf[3], buf[4]);
158         }
161 void clear_leds(int slave_fd)
163         char buf[6];
165         buf[0] = '#';
166         buf[1] = 0xFF;
167         buf[2] = 'l';
168         buf[3] = 0; /* Red */
169         buf[4] = 0; /* Green */
170         buf[5] = 0; /* Blue */
172         write(slave_fd, buf, 6);
173         if (debug) {
174                 printf("Leds cleared\n");
175         }
178 /*****************************************************************************/
179 /* Packets handling */
181 #define PKT_SIZE 20
182 uint8_t data[150];
183 int data_idx = 0;
184 int bad_checksums = 0;
186 int packet_slice(char c)
188         if (data_idx == 0) {
189                 if (c == '#') {
190                         data[0] = c;
191                         data_idx = 1;
192                 } else if (c == '$') {
193                         data[0] = c;
194                         /* Set packet length to 19 so that next character received (request type)
195                                 will terminate the packet */
196                         data_idx = PKT_SIZE - 1;
197                 }
198                 return 0;
199         }
200         if (data_idx > 0) {
201                 data[data_idx] = c;
202                 if (data_idx == (PKT_SIZE - 1)) {
203                         data_idx = 0;
204                         return 1;
205                 }
206                 data_idx++;
207         }
208         return 0;
211 int packet_check(void)
213         switch (data[0]) {
214                 case '#' : { /* Data packet */
215                         int i = 0;
216                         uint32_t sum = 0;
217                         for (i = 0; i < (PKT_SIZE - 1); i++) {
218                                 sum += data[i];
219                         }
220                         if ((sum & 0xFF) == data[(PKT_SIZE - 1)]) {
221                                 printf("Packet OK\n");
222                                 return 1; /* Checksum OK */
223                         }
224                         printf("Bad packet (cksum)\n");
225                         bad_checksums++; /* Count bad checksum for current address */
226                         return 2;
227                 }
228                 break;
229                 case '$' : /* Request from device */
230                         printf("Request received: %c\n", data[(PKT_SIZE - 1)]);
231                         if (data[(PKT_SIZE - 1)] == 'c') {
232                                 /* Request for a new address */
233                                 return 3;
234                         }
235                 break;
236         }
237         return 0;
240 /*****************************************************************************/
241 void parse_packet(struct sensor_info* info)
243         uint16_t* vars = (uint16_t*)data;
244         
245         /* Decode data */
246         info->addr = data[1] & 0x1F;
247         info->raw_humidity = (uint16_t)htons(vars[1]);
248         info->lux = (uint16_t)htons(vars[2]);
249         info->ir = (uint16_t)htons(vars[3]);
250         info->uv = (uint16_t)htons(vars[4]);
251         info->pressure = (uint16_t)htons(vars[5]);
252         info->comp_temp = (int16_t)htons(vars[6]);
253         info->humidity = (uint16_t)htons(vars[7]);
254         info->seqnum = (uint16_t)htons(vars[8]);
256         pkt_count[info->addr]++;
259 void display_info(struct sensor_info* info)
261         if (debug) {
262                 /* Display data */
263                 if (debug == 3) {
264                         printf("\e[H"); /* Goto terminal home (top left) */
265                 }
266                 printf("\e[KSensor %d, pkt %d, seq %d\n", info->addr, pkt_count[info->addr], info->seqnum);
267                 if (debug == 3) {
268                         printf("\e[K\tSoil: %d\n", info->raw_humidity);
269                         printf("\e[K\tLux: %d, IR: %d, UV: %d\n", info->lux, info->ir, info->uv);
270                         printf("\e[K\tPatm: %d hPa, Temp: %d,%02d degC, Humidity: %d,%d rH\n\n",
271                                 info->pressure,
272                                 info->comp_temp / 10,  (info->comp_temp > 0) ? (info->comp_temp % 10) : ((-info->comp_temp) % 10),
273                                 info->humidity / 10, info->humidity % 10);
274                         printf("\e[K\n");
275                 }
276         }
279 #define LOGFILE_NAME_LEN 100
280 #define LINE_LEN 200
281 void store_info(struct sensor_info* info, int chain, time_t seconds)
283         char logfile_name[LOGFILE_NAME_LEN];
284         struct tm time;
285         struct stat statbuf;
286         int logfile = 0, ret = 0;
288         gmtime_r(&seconds, &time);
289         snprintf(logfile_name, LOGFILE_NAME_LEN, "chain%d/sensor%d/%d-%02d-%02d.log",
290                                                  chain, info->addr,
291                                                  (time.tm_year + 1900), (time.tm_mon + 1), time.tm_mday);
293         /* Upon each day change, create a new file with it's header */
294         if (stat(logfile_name, &statbuf) == -1) {
295                 logfile = open(logfile_name, (O_CREAT | O_WRONLY), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
296                 if (logfile > 0) {
297                         /* Write header to file */
298                         char header[LINE_LEN];
299                         int nb = 0;
300                         nb = snprintf(header, LINE_LEN, "# Data logs for sensor %d on chain %d - %s#\n",
301                                                  info->addr, chain, ctime(&seconds));
302                         ret = write(logfile, header, nb);
303                         if (ret != nb) {
304                                 perror("Write error for hearder");
305                                 fprintf(stderr, "Error while writting first part of header to log file %s (%d) for sensor %d\n",
306                                                         logfile_name, logfile, info->addr);
307                                 close(logfile);
308                                 return;
309                         }
310                         nb = snprintf(header, LINE_LEN,
311                                  "# Format : seconds(epoch), seq number, raw soil, raw lux, raw ir, raw uv, press (hPa), temp(1/10°C), air hum(1/10%%)\n#\n\n");
312                         ret = write(logfile, header, nb);
313                         if (ret != nb) {
314                                 perror("Write error for hearder");
315                                 fprintf(stderr, "Error while writting second part of header to log file %s (%d) for sensor %d\n",
316                                                         logfile_name, logfile, info->addr);
317                                 close(logfile);
318                                 return;
319                         }
320                 } else {
321                         perror("Create logfile error");
322                         fprintf(stderr, "Unable to create log file %s for sensor %d\n", logfile_name, info->addr);
323                         return;
324                 }
325                 close(logfile);
326         }
328         logfile = open(logfile_name, (O_APPEND | O_WRONLY));
329         if (logfile > 0) {
330                 char line[LINE_LEN];
331                 int nb = 0;
332                 nb = snprintf(line, LINE_LEN, "%ld, %d, %d, %d, %d, %d, %d, %d, %d\n",
333                                                         seconds, info->seqnum, info->raw_humidity,
334                                                         info->lux, info->ir, info->uv,
335                                                         info->pressure, info->comp_temp, info->humidity);
336                 ret = write(logfile, line, nb);
337                 if (ret != nb) {
338                         perror("Write error for data");
339                         fprintf(stderr, "Error while writting data line to log file %s (%d) for sensor %d\n",
340                                                 logfile_name, logfile, info->addr);
341                 }
342                 close(logfile);
343         } else {
344                 perror("Error openning logfile for data");
345                 fprintf(stderr, "Unable to open logfile %s for sensor %d\n", logfile_name, info->addr);
346         }
349 /*****************************************************************************/
350 /* Main part of programm */
352 int main(int argc, char* argv[])
354         /* Serial */
355         char* device = NULL;
356         int slave_fd = 0;
357         int chain = 0;
358         int period = 1;
359         struct timeval start_tstamp;
361         while(1) {
362                 int option_index = 0;
363                 int c = 0;
365                 struct option long_options[] = {
366                         {"device", required_argument, 0, 'd'},
367                         {"chain", required_argument, 0, 'c'},
368                         {"period", required_argument, 0, 'p'},
369                         {"debug", no_argument, 0, 'D'},
370                         {"verbose", no_argument, 0, 'V'},
371                         {"help", no_argument, 0, 'h'},
372                         {"version", no_argument, 0, 'v'},
373                         {0, 0, 0, 0}
374                 };
376                 /* If time is not yet setup, wait for system to fix this */
377                 do {
378                         gettimeofday(&start_tstamp, NULL);
379                 } while (start_tstamp.tv_sec < MIN_SECS_SINCE_EPOCH);
381                 c = getopt_long(argc, argv, "d:c:p:DVhv", long_options, &option_index);
383                 /* no more options to parse */
384                 if (c == -1) break;
385                 switch (c) {
387                         /* d, device */
388                         case 'd':
389                                 device = optarg;
390                                 break;
392                         /* p, period */
393                         case 'p':
394                                 period = strtoul(optarg, NULL, 10);
395                                 break;
397                         /* c, chain */
398                         case 'c':
399                                 chain = strtoul(optarg, NULL, 10);
400                                 break;
402                         /* V, verbose */
403                         /* D, debug */
404                         case 'D':
405                                 debug++;
406                                 __attribute__ ((fallthrough));
407                         case 'V':
408                                 debug++;
409                                 break;
411                         /* v, version */
412                         case 'v':
413                                 printf("%s Version %s\n", PROG_NAME, VERSION);
414                                 return 0;
415                                 break;
417                         /* h, help */
418                         case 'h':
419                         default:
420                                 help(argv[0]);
421                                 return 0;
422                 }
423         }
426         /* Need Serial port as parameter */
427         if (device == NULL) {
428                 printf("Error, need serial (tty) device\n");
429                 help(argv[0]);
430                 return -1;
431         }
433         memset(addr_list, 0, NB_ADDRESS);
434         memset(pkt_count, 0, NB_ADDRESS * sizeof(int));
436         /* Open tty */
437         slave_fd = serial_setup(device);
438         if (slave_fd < 0) {
439                 printf("Unable to open specified serial port %s\n", device);
440                 return -2;
441         }
443         printf("Logs started on %s\n", ctime(&(start_tstamp.tv_sec)));
445         /* ************************************************* */
446         /* And never stop handling data ! */
447         while (1) {
448                 static int addr_index = 0;
449                 static time_t last = 0;
450                 int len = 0, ret = 0;
451                 struct timeval now;
452                 char buf[BUF_SIZE];
454                 /* Handle time */
455                 gettimeofday(&now, NULL); /* Get seconds since epoch */
457                 /* Send periodic requests for data */
458                 if (now.tv_sec >= (last + period)) {
459                         addr_index = NB_ADDRESS - 1;
460                         last = now.tv_sec;
461                         if (debug) {
462                                 printf("Here\n");
463                         }
464                         clear_leds(slave_fd);
465                         /* Reset all retry counters */
466                         memset(addr_retry, MAX_RETRY, NB_ADDRESS);
467                 }
468                 if (addr_index > 0) {
469                         send_request(slave_fd, addr_index);
470                         data_idx = 0; /* Moving to next sensor, reset data index for packet Rx */
471                         bad_checksums = 0; /* Reset bad checksums for current address */
472                         addr_index--;
473                 }
474                 usleep(100 * 1000); /* Leave some time for the sensor to reply, and for the system */
476                 memset(buf, 0, BUF_SIZE);
477                 /* Get serial data and try to build a packet */
478                 len = read(slave_fd, buf, BUF_SIZE);
479                 /* FIXME */ //printf("read: len: %d, idx: %d\n", len, data_idx);
480                 if (len < 0) {
481                         if (errno == EAGAIN) {
482                                 /* Nothing to do, we're in non-blocking mode */
483                         } else {
484                                 if (len == 0) {
485                                         printf("\nError, got activity on serial link, but no data ... End of file.\n");
486                                 } else {
487                                         perror("serial read error");
488                                 }
489                                 close(slave_fd);
490                                 len = 0;
491                                 do {
492                                         slave_fd = serial_setup(device);
493                                         usleep(10 * 1000);
494                                 } while (slave_fd < 0);
495                         }
496                 } else {
497                         int idx = 0;
498                         /* We are receiving something, log to stderr */
499                         if (debug == 2) {
500                                 write(2, buf, len);
501                         }
502                         while (idx < len) {
503                                 ret = packet_slice(buf[idx]);
504                                 /* Check return code to know if we have enough data for a packet */
505                                 if (ret == 1) {
506                                         /* check that the packet is valid */
507                                         ret = packet_check();
508                                 }
509                                 switch (ret) {
510                                         case 1: {
511                                                         struct sensor_info info;
512                                                         /* Valid packet received, parse data */
513                                                         parse_packet(&info);
514                                                         if (debug == 2) {
515                                                                 printf("Packet from sensor %d\n", info.addr);
516                                                         }
517                                                         if (info.seqnum == last_seq_num[info.addr]) {
518                                                                 /* Ignore duplicated packet */
519                                                                 if (debug == 2) {
520                                                                         printf("Dup packet from sensor %d (seq: %d)\n", info.addr, info.seqnum);
521                                                                 }
522                                                                 break;
523                                                         }
524                                                         /* Check data validity */
525                                                         if ((info.raw_humidity < 1000) || (info.raw_humidity > 4000)) {
526                                                                 if (debug == 2) {
527                                                                         printf("Invalid raw soil humidity [1000 .. 4000] : %d\n", info.raw_humidity);
528                                                                 }
529                                                                 break;
530                                                         }
531                                                         if ((info.pressure < 800) || (info.pressure > 1200)) {
532                                                                 if (debug == 2) {
533                                                                         printf("Invalid pressure [800 .. 1200] : %d\n", info.pressure);
534                                                                 }
535                                                                 break;
536                                                         }
537                                                         if ((info.comp_temp < -400) || (info.comp_temp > 1000)) {
538                                                                 if (debug == 2) {
539                                                                         printf("Invalid temperature [-400 .. 1000] : %d\n", info.comp_temp);
540                                                                 }
541                                                                 break;
542                                                         }
543                                                         if (info.humidity > 1000) {
544                                                                 if (debug == 2) {
545                                                                         printf("Invalid air rel humidity [0 .. 1000] : %d\n", info.humidity);
546                                                                 }
547                                                                 break;
548                                                         }
550                                                         /* New packet received */
551                                                         last_seq_num[info.addr] = info.seqnum;
552                                                         display_info(&info);
554                                                         /* New device ? store address */
555                                                         if ((info.addr < NB_ADDRESS) && (addr_list[info.addr] == 0)) {
556                                                                 addr_list[info.addr] = info.addr;
557                                                                 if (debug == 2) {
558                                                                         printf("New device detected : %d\n", info.addr);
559                                                                 }
560                                                         }
561                 
562                                                         /* Store data to log file */
563                                                         store_info(&info, chain, now.tv_sec);
565                                                         /* Update leds */
566                                                         set_led(slave_fd, &info);
567                                                 }
568                                                 break;
569                                         case 2:
570                                                 /* Checksum error for all three packets, retry */
571                                                 if ((addr_retry[addr_index + 1] > 0) && (bad_checksums == 3)) {
572                                                         addr_index++;
573                                                         addr_retry[addr_index]--;
574                                                 }
575                                                 break;
576                                         case 3:
577                                                 /* Done handling received data, need to send a new address to a sensor ? */
578                                                 send_new_address(slave_fd);
579                                                 break;
580                                 }
581                                 idx++;
582                         }
583                 }
584         } /* End of infinite loop */
586         close(slave_fd);
587         return 0;