Jaafar Benabdallah / Mbed OS DISCO-IOT01_HomeEnv

Dependencies:   HTS221 LIS3MDL LPS22HB LSM303AGR LSM6DSL VL53L0X picojson

Fork of HelloWorld_ST_Sensors by ST

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*------------------------------------------------------------------------------
00002                 Home Environment Monitoring with STM32 Discovery IOT Node
00003                                 Disco_HomeEnv
00004 Features:
00005   - Monitor temperature, relative humidity and air pressure
00006   - Connects to a local TSDB server (over http) to log this data
00007   - Display the data on a Nextion smart display with 30s autosleep and proximity
00008     or touch detection wake up
00009 Next:
00010   - add RTC sync and time display --> done
00011   - add light sensor to monitor illuminance --> low priority
00012   - improve reading by either: reduce power on time of WiFi module (batch logging)
00013     or by compensating for board own temperature --> WiP
00014   - use a local toolchain to compile mbed projects (with trueStudio) --> WiP 
00015   - add option to choose location from a list and update location tag
00016   - incrementally convert the project to electric energy monitoring:
00017         - read 2x simultaneous adc channels using DMA
00018         - save daily power readings to QSP mem for batch logging and recovey?
00019         - add screen to display current power drawing, daily and monthly cumulatives
00020 ------------------------------------------------------------------------------*/
00021 
00022 #include "main.h"
00023 
00024 /* ---------- SETUP ------------- */
00025 void setup()
00026 {
00027     printf("\n***************************************************************\n");
00028     printf("***      Home Env on STM32 IoT Discovery                    ***\n");
00029     printf("***************************************************************\n");
00030 
00031     printf("> Initializing sensors and WiFi... \n");
00032     
00033     int range_status;
00034 
00035     /* Init all sensors with default params */
00036     hum_temp.init(NULL);
00037     press_temp.init(NULL);
00038     magnetometer.init(NULL);
00039     acc_gyro.init(NULL);
00040     range_status = range.init_sensor(VL53L0X_DEFAULT_ADDRESS);
00041 
00042     /* enable sensors */
00043     hum_temp.enable();
00044     press_temp.enable();
00045     // acc_gyro.enable_x();
00046     // acc_gyro.enable_g();
00047     
00048     uint8_t id;
00049     hum_temp.read_id(&id);
00050     printf("HTS221  humidity & temperature id  = 0x%X\r\n", id);
00051     press_temp.read_id(&id);
00052     printf("LPS22HB pressure & temperature id  = 0x%X\r\n", id);
00053     magnetometer.read_id(&id);
00054     printf("LIS3MDL magnetometer id            = 0x%X\r\n", id);
00055     acc_gyro.read_id(&id);
00056     printf("LSM6DSL accelerometer&gyroscope id = 0x%X\r\n", id);
00057     printf("VL53L0x status                     = %d \r\n", range_status);
00058 
00059     sense_enabled = true;
00060 
00061     /* Setup display */
00062     WAKE_UP_DISPLAY;
00063     WIFI_ICON_OFF;
00064     UPLOAD_ICON_OFF;
00065     NO_WAKEUP_ON_SERIALIN;// no wakeup on serial in
00066     SLEEP_ON_NOTOUCH_30S;// sleep after 30sec no touch
00067     WAKEUP_ON_TOUCH;// wake up on touch
00068 
00069 
00070     /*Initialize WIFI module */
00071     WiFi_on = connectWiFi();
00072     if (WiFi_on) {
00073         led3 = 1;
00074         //WiFi_led_ticker.attach(&toggle_led3_cb, 0.25);
00075         rtc_synced = sync_rtc();
00076         // enter power save mode
00077         /*if (WIFI_SetPowerSaveMode(1, (UPLOAD_PERIOD_S - 1)*1000) == WIFI_STATUS_OK) {
00078             printf("> es-wifi entered power save mode\n");
00079         } else {
00080             printf("> ERROR: es-wifi did not enter power save mode\n");
00081         }*/
00082     }
00083 
00084     // start event tickers
00085     getMeasurementsTicker.attach(&time_to_sense_cb, SENSE_PERIOD_S);
00086     sendMeasurementsTicker.attach(&time_to_send_cb, UPLOAD_PERIOD_S);
00087     checkProximityTicker.attach(&time_to_check_distance_cb, CHECK_PROXIMITY_PERIOD_S);
00088 
00089 } // Setup
00090 
00091 
00092 /* ---------- LOOP ------------- */
00093 void loop() {
00094     time_t rawtime;
00095     tm *pTime;
00096     
00097     if (take_measurements) {
00098         // Environment data
00099         hum_temp.get_temperature(&tempC_val);
00100         tempF_val = tempC_val*(9.0/5.0) + 32;
00101         hum_temp.get_humidity(&RH_val);
00102 
00103         press_temp.get_pressure(&Patm_val);
00104         press_temp.get_temperature(&tempC_val2);
00105         
00106 
00107         // Inertial data
00108         /*
00109         magnetometer.get_m_axes(axes);
00110         printf("LIS3MDL [mag/mgauss]:    %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
00111 
00112         acc_gyro.get_x_axes(axes);
00113         printf("LSM6DSL [acc/mg]:        %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
00114 
00115         acc_gyro.get_g_axes(axes);
00116         printf("LSM6DSL [gyro/mdps]:     %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
00117         */
00118 
00119         // print results to terminal
00120         /*
00121         printf("HTS221:  temp= %.2f C, hum= %.2f%%\r\n", tempC_val, RH_val);
00122         printf("         temp= %.2f F\n", tempF_val);
00123         printf("LPS22HB: patm= %.2f mbar, temp= %.2f C\r\n", Patm_val, tempC_val2);
00124         printf("VL53L0X [mm] = %6ld\r\n", distance_val);
00125         */
00126         // refresh screen with updated measurements
00127         nextion.printf("valC.txt=\"%.1f\"\xff\xff\xff", tempC_val);
00128         nextion.printf("valF.txt=\"%.1f\"\xff\xff\xff", tempF_val);
00129         nextion.printf("valRH.txt=\"%.1f\"\xff\xff\xff", RH_val);
00130         nextion.printf("valAtm.val=%.0f\xff\xff\xff", Patm_val);
00131         nextion.printf("valRange.val=%d\xff\xff\xff", distance_val);
00132 
00133         take_measurements = false;
00134     }
00135 
00136     if (send_measurements) {
00137 
00138         printf("HTS221:  temp= %.2f C, hum= %.2f%%\r\n", tempC_val, RH_val);
00139 
00140         // body of the request
00141         char request_body[256];
00142         char *InfluxServerUrl = INFLUX_SERVER_URL;
00143         sprintf(request_body, "disco_iot,loc=%s temperature=%.2f,humidity=%.1f,pressure=%.1f \n","playroom", tempC_val, RH_val, Patm_val);
00144 
00145         // build header of the request
00146         sprintf((char *)http_request, "POST %s HTTP/1.1\r\nHost: %s \r\n", INFLUX_WRITE_EP, InfluxServerUrl);
00147         strcat((char *)http_request, "Accept: */*\r\n");
00148         strcat((char *)http_request, "User-agent: ES-WIFI TcpClient\r\n");
00149         strcat((char *)http_request, "Connection: Close\r\n"); //"Connection: Keep-Alive\r\n"
00150         char buffer[64];
00151         sprintf(buffer, "Content-Length: %d \r\n\r\n", strlen(request_body));
00152         strcat((char *)http_request, buffer);
00153 
00154         // append body to the header of the request
00155         strcat((char *)http_request, request_body);
00156         reqLen = strlen((char *)http_request);
00157 
00158         // Establish a connection to DB server
00159         uint8_t socketid = 1;
00160         if (checkWiFi()) {
00161             led3 = 1;
00162             if (connectToServer(INFLUX_SERVER_URL, INFLUX_SERVER_PORT, socketid)) {
00163                 ledhttp =1;
00164                 // submit POST request
00165                 // printf("> Sending a POST request with length=%d including a body length=%d\n", reqLen, strlen(request_body));
00166                 // printf((char *)http_request);
00167                 uint16_t dataLen;
00168                 if (WIFI_SendData(socketid, http_request, reqLen, &dataLen, WIFI_WRITE_TIMEOUT) != WIFI_STATUS_OK) {
00169                     printf("> ERROR: Could not send request to %s", InfluxServerUrl);
00170                     UPLOAD_ICON_OFF;
00171                 } else {
00172                     request_sent++;
00173                     UPLOAD_ICON_ON;
00174                 }
00175                 // get server response
00176                 // memset(&http_resp[0], 0, sizeof(http_resp));
00177                 WIFI_ReceiveData(socketid, http_resp, sizeof(http_resp), &dataLen, WIFI_READ_TIMEOUT);
00178                 if(dataLen > 0) {
00179                     /*
00180                     printf("> Server response:\n");
00181                     printf((char *)http_resp);
00182                     printf("\n> Response length: %d \n", dataLen);
00183                     */
00184                     memset(&buffer[0], 0, sizeof(buffer));
00185                     // get the first line of the response
00186                     strcpy(buffer, strtok((char *)http_resp, "\r\n"));
00187                     // extract the response code
00188                     int response_code = 0;
00189                     sscanf(buffer, "HTTP/1.1 %d", &response_code);
00190                     // printf("> Response code: %d \n", response_code);
00191                     /* common response codes from InfluxDB API:
00192                     HTTP/1.1 204 No Content
00193                     HTTP/1.1 400 Bad Request
00194                     HTTP/1.1 404 Not Found
00195                     HTTP/1.1 500 Internal Server Error
00196                     */
00197                     if (response_code == 204) request_acked++;
00198                 } else {
00199                     UPLOAD_ICON_OFF;
00200                     printf("> Error: No response from the server %s.\n", InfluxServerUrl);
00201                 }
00202                 printf("> Requests sent: %d, ack'ed: %d\n", request_sent, request_acked);
00203                 ledhttp = 0;
00204                 WIFI_CloseClientConnection(socketid);
00205             } else {
00206                 printf("> ERROR: Could not open connection to %s \n", InfluxServerUrl);
00207             }
00208             
00209             // enter power save mode for UPLOAD_PERIOD_S - 1 sec, beacon interval = 100ms
00210             if (WIFI_SetPowerSaveMode(1, (UPLOAD_PERIOD_S - 1)*1000) == WIFI_STATUS_OK) {
00211                printf("> es-wifi entered power save mode\n");
00212             } else {
00213                 printf("> ERROR: es-wifi did not enter power save mode\n");
00214             }
00215             
00216         } else {
00217             printf("> ERROR: Could not connect to WiFi \n");
00218             led3 = 0;
00219         }
00220         
00221         if (rtc_synced) {
00222             time(&rawtime);
00223             pTime = localtime(&rawtime);
00224             // printf ("Current local time and date: %s", asctime(pTime));
00225             // printf("> %d:%d:%d\n", pTime->tm_hour, pTime->tm_min, pTime->tm_sec);
00226         } else {
00227             rtc_synced = sync_rtc();
00228         }   
00229         
00230         send_measurements = false;
00231     }
00232 
00233     if (check_proximity) {
00234         // make sure display is awake when somebody get close to the screen
00235         // Proximity value
00236         uint32_t dist_val = 0;
00237         int status = range.get_distance(&dist_val);
00238         if (status == VL53L0X_ERROR_NONE) {
00239             if (dist_val < 500) {
00240                 nextion.printf("sleep=0\xff\xff\xff");// send wake up command
00241             }
00242             distance_val = dist_val;
00243         } else {
00244             distance_val = 9999;
00245         }
00246         
00247         // since this is a 1-sec period ticker, we do clock display refresh in here too
00248         // get a formatted local time to be displayed
00249         if (rtc_synced) {
00250             time(&rawtime);
00251             pTime = localtime(&rawtime);
00252             nextion.printf("hour.txt=\"%02d\"\xff\xff\xff", pTime->tm_hour);
00253             nextion.printf("minute.txt=\"%02d\"\xff\xff\xff", pTime->tm_min);
00254             nextion.printf("vis sec,1\xff\xff\xff");
00255             //nextion.printf("vis sec,%d\xff\xff\xff", pTime->tm_sec % 2 == 0 ? 1 : 0);
00256         }
00257         
00258         check_proximity = false;
00259     }       
00260 } // Loop
00261 
00262 
00263 int main() {
00264     setup();
00265     while(1) {
00266         loop();
00267     }
00268 }
00269 
00270 
00271 /*                                         */
00272 /* Interrupts servicing callback functions */
00273 /*                                         */
00274 
00275 // ISR for flashing WiFi LED
00276 /*
00277 void toggle_led3_cb()
00278 {
00279     led3 = !led3;
00280 }*/
00281 
00282 // ISR to enable taking measurements (and refresh display)
00283 void time_to_sense_cb(void)
00284 {
00285     if (sense_enabled) {
00286         take_measurements = true;
00287     }
00288 }
00289 
00290 // ISR to enable handling sending data
00291 void time_to_send_cb(void)
00292 {
00293     if (sense_enabled) {
00294         send_measurements = true;
00295     }
00296 }
00297 
00298 // ISR to enable checking distance
00299 void time_to_check_distance_cb(void)
00300 {
00301     check_proximity = true;
00302 }
00303 
00304 /**
00305   * @brief  Connect to the WiFi network with credentials in mbed_app.json
00306   * @param  None
00307   * @retval True for WiFi connected, False if there was an error
00308   */
00309 bool connectWiFi(void)
00310 {
00311     uint8_t  IP_Addr[4];
00312     uint8_t  MAC_Addr[6];
00313 
00314     if(WIFI_Init() ==  WIFI_STATUS_OK) {
00315         // printf("> WiFi module initialized.\n");
00316         char fwVer[32];
00317         if(WIFI_GetModuleFwRevision(fwVer) == WIFI_STATUS_OK) {
00318           printf("> Firmware version: %s\n", fwVer);
00319         }
00320         /*
00321         if(WIFI_GetMAC_Address(MAC_Addr) == WIFI_STATUS_OK) {
00322             printf("> WiFi module MAC Address : %X:%X:%X:%X:%X:%X\n",
00323                    MAC_Addr[0],
00324                    MAC_Addr[1],
00325                    MAC_Addr[2],
00326                    MAC_Addr[3],
00327                    MAC_Addr[4],
00328                    MAC_Addr[5]);
00329         } else {
00330             printf("> ERROR : CANNOT get MAC address\n");
00331         }*/
00332         
00333         if( WIFI_Connect(MBED_CONF_APP_WIFI_SSID, MBED_CONF_APP_WIFI_PASSWORD, WIFI_ECN_WPA2_PSK) == WIFI_STATUS_OK) {
00334             if(WIFI_GetIP_Address(IP_Addr) == WIFI_STATUS_OK) {
00335                 printf("> IP Address : %d.%d.%d.%d\n",
00336                        IP_Addr[0],
00337                        IP_Addr[1],
00338                        IP_Addr[2],
00339                        IP_Addr[3]);
00340                 WIFI_ICON_ON;
00341                 UPLOAD_ICON_OFF; // reset db sync status if (re-)connect happened
00342                 return true;
00343             } else {
00344                 printf("> ERROR : es-wifi module CANNOT get IP address\n");
00345             }
00346         } else {
00347             printf("> ERROR : es-wifi module NOT connected\n");
00348         }
00349     } else {
00350         printf("> ERROR : WIFI Module cannot be initialized.\n");
00351     }
00352     WIFI_ICON_OFF;
00353     UPLOAD_ICON_OFF;
00354     return false;
00355 }
00356 
00357 
00358 /**
00359   * @brief  check WiFi connection status, retry once if it's offline
00360   * @param  None
00361   * @retval True for WiFi (re)connected, False if reconnection failed
00362   */
00363 
00364 bool checkWiFi(void)
00365 {
00366     uint8_t  ip_addr[4];
00367     if(WIFI_GetIP_Address(ip_addr) == WIFI_STATUS_OK) {
00368         WIFI_ICON_ON;
00369         return true;
00370     } else {
00371         return connectWiFi();
00372     }
00373 }
00374 
00375 /**
00376   * @brief  create a TCP session on a server
00377   * @param  serverUrl : TCP server URL
00378   * @param  serverPort : port TCP server is listening on
00379   * @retval true if connection established
00380   */
00381 bool connectToServer(char *serverUrl, uint16_t serverPort, uint8_t socket_id)
00382 {
00383     uint8_t  serverIP[] = {0, 0, 0, 0};
00384     uint16_t trials = CONNECTION_TRIAL_MAX;
00385 
00386     while (trials--) {
00387         // get the server IP through a (local) DNS resolve
00388         if (WIFI_GetHostAddress(serverUrl, serverIP) == WIFI_STATUS_OK) {
00389             
00390             printf("> %s resolved to: %d.%d.%d.%d\n", serverUrl,
00391                    serverIP[0],
00392                    serverIP[1],
00393                    serverIP[2],
00394                    serverIP[3]);
00395             // establish TCP connection to server
00396             if( WIFI_OpenClientConnection(socket_id, WIFI_TCP_PROTOCOL, "TCP_Client", serverIP, serverPort, 0) == WIFI_STATUS_OK) {
00397                 printf("> Connected to %s .\n", serverUrl);
00398                 return true;
00399             }
00400         } else {
00401             printf("> ERROR : Cannot resolve URL: %s\n", serverUrl);
00402             printf("> Trials left: %d\n", trials);
00403         }
00404     }
00405     
00406     if(!trials) {
00407         printf("> ERROR : Cannot establish connection\n");
00408     }
00409     return false;
00410 }
00411 
00412 
00413 /**
00414   * @brief  get current unix timestamp and use it to sync rtc
00415   * @retval True if successful, False if failed
00416   */
00417 bool sync_rtc() {
00418     // 1- establish connection to time server (socket 2)
00419     uint8_t socketid = 2;
00420     uint16_t dataLen;
00421     if (checkWiFi()) {
00422         if (connectToServer(TIME_SERVER_URL, TIME_SERVER_PORT, socketid)) {
00423             sprintf((char *)http_request, "GET %s HTTP/1.1\r\nHost: %s \r\n", TIME_SERVER_EP, TIME_SERVER_URL);
00424             strcat((char *)http_request, "Connection: Close\r\n\r\n");
00425             reqLen = strlen((char *)http_request);
00426             if (WIFI_SendData(socketid, http_request, reqLen, &dataLen, WIFI_WRITE_TIMEOUT) != WIFI_STATUS_OK) {
00427                 printf("> ERROR: Could not send request to %s\n", TIME_SERVER_URL);
00428             } else {
00429                 printf("> TS: Sent %d bytes\n", dataLen);
00430             }
00431             
00432         // 2- get server response and parse the json
00433             WIFI_ReceiveData(socketid, http_resp, sizeof(http_resp), &dataLen, WIFI_READ_TIMEOUT);
00434             if(dataLen > 0) {
00435                 printf("> TS: Received %d bytes\n", dataLen);
00436                 // extract the body (json payload) from the response
00437                 char *resp_body = strstr((char *)http_resp, "\r\n\r\n"); // get the body + "OK"
00438                 resp_body = strtok(resp_body, "\r\n"); // get rid of 'OK'
00439                 picojson::value json_val;
00440                 string err = picojson::parse(json_val, resp_body,  resp_body + strlen(resp_body));
00441                 if(err.empty()) {
00442                     
00443                     // 3- convert timestamp  to local time and sync rtc            
00444                     uint32_t timestamp_val = (unsigned)json_val.get("timestamp").get<double>();
00445                     printf("> utc_ts = %u \n",timestamp_val); // print utc
00446                     timestamp_val -= 3600*5; // EST: utc - 5h
00447                     set_time(timestamp_val);
00448                     WIFI_CloseClientConnection(socketid); // close socket on success
00449                     return true;
00450                 } else { 
00451                     printf("> ERROR: TS json parsing\n");
00452                 }  
00453             } else {
00454                 printf("> ERROR: Did not receive data from TS server\n");
00455             }
00456             WIFI_CloseClientConnection(socketid); // close socket on failure to retrieve time
00457         } else {
00458             printf("> ERROR: Cannot establish connection to %s \n", TIME_SERVER_URL);
00459         }
00460     }
00461     return false;
00462 }
00463