a sensor hub for monitoring home environment

Dependencies:   HTS221 LIS3MDL LPS22HB LSM303AGR LSM6DSL VL53L0X picojson

Fork of HelloWorld_ST_Sensors by ST

Revision:
11:1e0b9a529ee0
Parent:
8:39ecd15538f9
Child:
12:058b012dbebe
--- a/main.cpp	Wed Oct 18 12:59:14 2017 +0000
+++ b/main.cpp	Tue Dec 12 03:01:51 2017 +0000
@@ -1,215 +1,363 @@
-/**
- ******************************************************************************
- * @file    main.cpp
- * @author  CLab
- * @version V1.0.0
- * @date    5-September-2017
- * @brief   Simple Example application for using X_NUCLEO_IKS01A2  
- *          MEMS Inertial & Environmental Sensor Nucleo expansion and 
- *          B-L475E-IOT01A2 boards.
- ******************************************************************************
- * @attention
- *
- * <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *   1. Redistributions of source code must retain the above copyright notice,
- *      this list of conditions and the following disclaimer.
- *   2. Redistributions in binary form must reproduce the above copyright notice,
- *      this list of conditions and the following disclaimer in the documentation
- *      and/or other materials provided with the distribution.
- *   3. Neither the name of STMicroelectronics nor the names of its contributors
- *      may be used to endorse or promote products derived from this software
- *      without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- ******************************************************************************
-*/ 
+/*------------------------------------------------------------------------------
+                Home Environment Monitoring with STM32 Discovery IOT Node
+                                Disco_HomeEnv
+Features:
+  - Monitor temperature, relative humidity and air pressure
+  - Connects to a local TSDB server (over http) to log this data
+  - Display the data on a Nextion smart display with 30s autosleep and proximity
+    or touch detection wake up
+Next:
+  - add RTC sync and time display
+  - add light sensor to monitor illuminance
+  - improve reading by either: reduce power on time of WiFi module (batch logging)
+    or by compensating for board own temperature
+  - add option to choose location from a list and update location tag
+  - incrementally convert the project to electric energy monitoring:
+        - read 2x simultaneous adc channels using DMA
+        - save daily power readings to QSP mem for batch logging and recovey?
+        - add screen to display current power drawing, daily and monthly cumulatives
+------------------------------------------------------------------------------*/
+
+#include "main.h"
+
+/* ---------- SETUP ------------- */
+void setup()
+{
+    printf("\n***************************************************************\n");
+    printf("***      Home Env on STM32 IoT Discovery                    ***\n");
+    printf("***************************************************************\n");
+
+    printf("> Initializing sensors and WiFi... \n");
+    
+    int range_status;
+
+    /* Init all sensors with default params */
+    hum_temp.init(NULL);
+    press_temp.init(NULL);
+    magnetometer.init(NULL);
+    acc_gyro.init(NULL);
+    range_status = range.init_sensor(VL53L0X_DEFAULT_ADDRESS);
 
-/* Includes */
-#include "mbed.h"
-#include "HTS221Sensor.h"
-#include "LPS22HBSensor.h"
-#include "LSM6DSLSensor.h"
+    /* Enable all sensors */
+    hum_temp.enable();
+    press_temp.enable();
+    acc_gyro.enable_x();
+    acc_gyro.enable_g();
+    
+    uint8_t id;
+    hum_temp.read_id(&id);
+    printf("HTS221  humidity & temperature id  = 0x%X\r\n", id);
+    press_temp.read_id(&id);
+    printf("LPS22HB pressure & temperature id  = 0x%X\r\n", id);
+    magnetometer.read_id(&id);
+    printf("LIS3MDL magnetometer id            = 0x%X\r\n", id);
+    acc_gyro.read_id(&id);
+    printf("LSM6DSL accelerometer&gyroscope id = 0x%X\r\n", id);
+    printf("VL53L0x status                     = %d \r\n", range_status);
 
-#ifdef TARGET_DISCO_L475VG_IOT01A
-
-#include "lis3mdl_class.h"
-#include "VL53L0X.h"
+    sense_enabled = true;
 
-#else // Nucleo-XXX + X-Nucleo-IKS01A2 or SensorTile
+    /* Setup display */
+    WIFI_ICON_OFF;
+    UPLOAD_ICON_OFF;
+    nextion.printf("usup=0\xff\xff\xff");// no wakeup on serial in
+    nextion.printf("thsp=30\xff\xff\xff");// sleep after 30sec no touch
+    nextion.printf("thup=1\xff\xff\xff");// wake up on touch
+
 
-#ifdef TARGET_NUCLEO_L476RG
-#define TARGET_SENSOR_TILE   // comment out to use actual NUCLEO-L476RG instead of SensorTile
-#endif
+    /*Initialize WIFI module */
+    WiFi_on = connectWiFi();
+    if (WiFi_on) {
+        led3 = 1;
+        WIFI_ICON_ON;
+        //WiFi_led_ticker.attach(&toggle_led3_cb, 0.25);
+        // sync_time();
+    }
 
-#include "LSM303AGRMagSensor.h"
-#include "LSM303AGRAccSensor.h"
+    // start event tickers
+    getMeasurementsTicker.attach(&time_to_sense_cb, SENSE_PERIOD_S);
+    sendMeasurementsTicker.attach(&time_to_send_cb, UPLOAD_PERIOD_S);
+    checkProximityTicker.attach(&time_to_check_distance_cb, CHECK_PROXIMITY_PERIOD_S);
 
-#endif
+} // Setup
 
 
-/* Retrieve the composing elements of the expansion board */
+/* ---------- LOOP ------------- */
+void loop() {
+    if (take_measurements) {
+        // Environment data
+        hum_temp.get_temperature(&tempC_val);
+        tempF_val = tempC_val*(9.0/5.0) + 32;
+        hum_temp.get_humidity(&RH_val);
+
+        press_temp.get_pressure(&Patm_val);
+        press_temp.get_temperature(&tempC_val2);
+
+        // Inertial data
+        /*
+        magnetometer.get_m_axes(axes);
+        printf("LIS3MDL [mag/mgauss]:    %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+
+        acc_gyro.get_x_axes(axes);
+        printf("LSM6DSL [acc/mg]:        %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+
+        acc_gyro.get_g_axes(axes);
+        printf("LSM6DSL [gyro/mdps]:     %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+        */
+
+        // print results to terminal
+        /*
+        printf("HTS221:  temp= %.2f C, hum= %.2f%%\r\n", tempC_val, RH_val);
+        printf("         temp= %.2f F\n", tempF_val);
+        printf("LPS22HB: patm= %.2f mbar, temp= %.2f C\r\n", Patm_val, tempC_val2);
+        printf("VL53L0X [mm] = %6ld\r\n", distance);
+        */
 
-/* Interface definition */
-#ifdef TARGET_DISCO_L475VG_IOT01A
-static DevI2C devI2c(PB_11,PB_10);
-#else // X-Nucleo-IKS01A2 or SensorTile
-#ifdef TARGET_SENSOR_TILE
-#define SPI_TYPE_LPS22HB   LPS22HBSensor::SPI3W
-#define SPI_TYPE_LSM6DSL   LSM6DSLSensor::SPI3W
-SPI devSPI(PB_15, NC, PB_13);  // 3-wires SPI on SensorTile  
-static Serial ser(PC_12,PD_2); // Serial with SensorTile Cradle Exp. Board + Nucleo   
-#define printf(...) ser.printf(__VA_ARGS__)     
-#else  // Nucleo-XXX + X-Nucleo-IKS01A2 
-static DevI2C devI2c(D14,D15);
-#endif
-#endif
+        // refresh screen with updated measurements
+        nextion.printf("valC.txt=\"%.1f\"\xff\xff\xff", tempC_val);
+        nextion.printf("valF.txt=\"%.1f\"\xff\xff\xff", tempF_val);
+        nextion.printf("valRH.txt=\"%.1f\"\xff\xff\xff", RH_val);
+        nextion.printf("valAtm.val=%.0f\xff\xff\xff", Patm_val);
+        nextion.printf("valRange.val=%d\xff\xff\xff", distance);
+
+        take_measurements = false;
+    }// take measurements
+
+    if (send_measurements) {
+
+        //printf("HTS221:  temp= %.2f C, hum= %.2f%%\r\n", tempC_val, RH_val);
 
-/* Environmental sensors */
-#ifdef TARGET_SENSOR_TILE
-static LPS22HBSensor press_temp(&devSPI, PA_3, NC, SPI_TYPE_LPS22HB); 
-#else  // Nucleo-XXX + X-Nucleo-IKS01A2 or B-L475E-IOT01A2
-static LPS22HBSensor press_temp(&devI2c);
-static HTS221Sensor hum_temp(&devI2c);
-#endif
+        // body of the request
+        char request_body[256];
+        sprintf(request_body, "disco_iot,loc=%s temperature=%.2f,humidity=%.1f,pressure=%.1f \n","playroom", tempC_val, RH_val, Patm_val);
+
+        // build header of the request
+        sprintf((char *)http_request, "POST %s HTTP/1.1\r\nHost: %s \r\n", influx_query, InfluxServerUrl);
+        strcat((char *)http_request, "Accept: */*\r\n");
+        strcat((char *)http_request, "User-agent: ES-WIFI TcpClient\r\n");
+        strcat((char *)http_request, "Connection: Close\r\n"); //"Connection: Keep-Alive\r\n"
+        char buffer[64];
+        sprintf(buffer, "Content-Length: %d \r\n\r\n", strlen(request_body));
+        strcat((char *)http_request, buffer);
+
+        // append body to the header of the request
+        strcat((char *)http_request, request_body);
+        reqLen = strlen((char *)http_request);
 
-/* Motion sensors */
-#ifdef TARGET_DISCO_L475VG_IOT01A
-static LSM6DSLSensor acc_gyro(&devI2c,LSM6DSL_ACC_GYRO_I2C_ADDRESS_LOW,PD_11); // low address
-static LIS3MDL magnetometer(&devI2c);
-#else // X-NUCLEO-IKS01A2 or SensorTile
-#if defined (TARGET_SENSOR_TILE)
-static LSM6DSLSensor acc_gyro(&devSPI,PB_12, NC, PA_2, SPI_TYPE_LSM6DSL); 
-static LSM303AGRMagSensor magnetometer(&devSPI, PB_1);
-static LSM303AGRAccSensor accelerometer(&devSPI, PC_4);
-#else
-static LSM6DSLSensor acc_gyro(&devI2c,LSM6DSL_ACC_GYRO_I2C_ADDRESS_HIGH,D4,D5); // high address
-static LSM303AGRMagSensor magnetometer(&devI2c);
-static LSM303AGRAccSensor accelerometer(&devI2c);
-#endif
-#endif
-
-/* Range sensor - B-L475E-IOT01A2 only */
-#ifdef TARGET_DISCO_L475VG_IOT01A
-static DigitalOut shutdown_pin(PC_6);
-static VL53L0X range(&devI2c, &shutdown_pin, PC_7);
-#endif
+        // Establish a connection to DB server
+        uint8_t socketid = 1;
+        if (checkWiFi()) {
+            led3 = 1;
+            WIFI_ICON_ON;
+            if (connectToServer(InfluxServerUrl, InfluxServerPort, socketid)) {
+                ledhttp =1;
+                // submit POST request
+                printf("> Sending a POST request with length=%d including a body length=%d\n", reqLen, strlen(request_body));
+                printf((char *)http_request);
+                uint16_t dataLen;
+                if (WIFI_SendData(socketid, http_request, reqLen, &dataLen, WIFI_WRITE_TIMEOUT) != WIFI_STATUS_OK) {
+                    printf("> ERROR: Could not send request to %s", InfluxServerUrl);
+                    UPLOAD_ICON_OFF;
+                } else {
+                    request_sent++;
+                    UPLOAD_ICON_ON;
+                }
+                // get server response
+                // memset(&http_resp[0], 0, sizeof(http_resp));
+                WIFI_ReceiveData(socketid, http_resp, sizeof(http_resp), &dataLen, WIFI_READ_TIMEOUT);
+                if(dataLen > 0) {
+                    /*
+                    printf("> Server response:\n");
+                    printf((char *)http_resp);
+                    printf("\n> Response length: %d \n", dataLen);
+                    */
+                    memset(&buffer[0], 0, sizeof(buffer));
+                    // get the first line of the response
+                    strcpy(buffer, strtok((char *)http_resp, "\r\n"));
+                    // extract the response code
+                    int response_code = 0;
+                    sscanf(buffer, "HTTP/1.1 %d", &response_code);
+                    printf("> Response code: %d \n", response_code);
+                    /* c ommon response codes from InfluxDB API:
+                    HTTP/1.1 204 No Content
+                    HTTP/1.1 400 Bad Request
+                    HTTP/1.1 404 Not Found
+                    HTTP/1.1 500 Internal Server Error
+                    */
+                    if (response_code == 204) request_acked++;
+                } else {
+                    UPLOAD_ICON_OFF;
+                    printf("> Error: No response from the server %s.\n", InfluxServerUrl);
+                }
+                printf("> Requests sent: %d, ack'ed: %d\n", request_sent, request_acked);
+                ledhttp = 0;
+                WIFI_CloseClientConnection(socketid);
+            } else {
+                printf("> ERROR: Could not connect to %s \n", InfluxServerUrl);
+            }
+        } else {
+            printf("> ERROR: Could not connect to WiFi \n");
+            led3 = 0;
+            WIFI_ICON_OFF;
+        }
+        send_measurements = false;
+    } // sendMeasurement()
 
-/* Simple main function */
-int main() {
-  uint8_t id;
-  float value1, value2;
-//  char buffer1[32], buffer2[32];
-  int32_t axes[3];
-  
-  /* Init all sensors with default params */
-#ifndef TARGET_SENSOR_TILE
-  hum_temp.init(NULL);
-#endif  
-
-  press_temp.init(NULL);
-  magnetometer.init(NULL);
-  acc_gyro.init(NULL);
-  
-#ifdef TARGET_DISCO_L475VG_IOT01A
-  range.init_sensor(VL53L0X_DEFAULT_ADDRESS);
-#else // X-NUCLEO-IKS01A2 or SensorTile
-  accelerometer.init(NULL);
-#endif
+    if (check_proximity) {
+        // make sure display is awake when somebody get close to the screen
+        // Proximity value
+        uint32_t dist_val = 0;
+        int status = range.get_distance(&dist_val);
+        if (status == VL53L0X_ERROR_NONE) {
+            distance = (int)dist_val;
+        } else {
+            distance = -1;
+        }
+        if (distance < 400) {
+            nextion.printf("sleep=0\xff\xff\xff");// send wake up command
+        }
+        check_proximity = false;
+    } // check proximity
+} // Loop
 
 
-  
-  /* Enable all sensors */
-#ifndef TARGET_SENSOR_TILE  
-  hum_temp.enable();
-#endif  
-  press_temp.enable();
-#ifndef TARGET_DISCO_L475VG_IOT01A // X-NUCLEO-IKS01A2
-  magnetometer.enable();
-  accelerometer.enable();
-#endif
-  acc_gyro.enable_x();
-  acc_gyro.enable_g();
-  
-  printf("\033[2J\033[20A");
-  printf ("\r\n--- Starting new run ---\r\n\r\n");
+int main() {
+    setup();
+    while(1) {
+        loop();
+    }
+}
+
+
+/*                                         */
+/* Interrupts servicing callback functions */
+/*                                         */
+
+// ISR for flashing WiFi LED
+/*
+void toggle_led3_cb()
+{
+    led3 = !led3;
+}*/
+
+// ISR to enable taking measurements (and refresh display)
+void time_to_sense_cb(void)
+{
+    if (sense_enabled) {
+        take_measurements = true;
+    }
+}
 
-#ifndef TARGET_SENSOR_TILE
-  hum_temp.read_id(&id);
-  printf("HTS221  humidity & temperature    = 0x%X\r\n", id);
-#endif  
-  press_temp.read_id(&id);
-  printf("LPS22HB pressure & temperature    = 0x%X\r\n", id);
-  magnetometer.read_id(&id);
-#ifdef TARGET_DISCO_L475VG_IOT01A
-  printf("LIS3MDL magnetometer              = 0x%X\r\n", id);
-#else // X-NUCLEO-IKS01A2 or SensorTile
-  printf("LSM303AGR magnetometer            = 0x%X\r\n", id);
-  accelerometer.read_id(&id);
-  printf("LSM303AGR accelerometer           = 0x%X\r\n", id); 
-#endif
-  acc_gyro.read_id(&id);
-  printf("LSM6DSL accelerometer & gyroscope = 0x%X\r\n", id);
-  
-  printf("\n\r--- Reading sensor values ---\n\r"); ;
- 
-  while(1) {
-    printf("\r\n");
+// ISR to enable handling sending data
+void time_to_send_cb(void)
+{
+    if (sense_enabled) {
+        send_measurements = true;
+    }
+}
+
+// ISR to enable checking distance
+void time_to_check_distance_cb(void)
+{
+    check_proximity = true;
+}
+
+/**
+  * @brief  Connect to the WiFi network with credentials in mbed_app.json
+  * @param  None
+  * @retval True for WiFi connected, False if there was an error
+  */
+bool connectWiFi(void)
+{
+    uint8_t  IP_Addr[4];
+    uint8_t  MAC_Addr[6];
+
+    if(WIFI_Init() ==  WIFI_STATUS_OK) {
+        // printf("> WiFi module initialized.\n");
+        //if(WIFI_GetModuleName(moduleName) == WIFI_STATUS_OK) {
+        //  printf("> Module name: %s\n", moduleName);
+        //}
+        if(WIFI_GetMAC_Address(MAC_Addr) == WIFI_STATUS_OK) {
+            printf("> WiFi module MAC Address : %X:%X:%X:%X:%X:%X\n",
+                   MAC_Addr[0],
+                   MAC_Addr[1],
+                   MAC_Addr[2],
+                   MAC_Addr[3],
+                   MAC_Addr[4],
+                   MAC_Addr[5]);
+        } else {
+            printf("> ERROR : CANNOT get MAC address\n");
+        }
 
-#ifndef TARGET_SENSOR_TILE    
-    hum_temp.get_temperature(&value1);
-    hum_temp.get_humidity(&value2);
-    printf("HTS221:  [temp] %.2f C, [hum]   %.2f%%\r\n", value1, value2);
-#endif    
-    value1=value2=0;    
-    press_temp.get_temperature(&value1);
-    press_temp.get_pressure(&value2);
-    printf("LPS22HB: [temp] %.2f C, [press] %.2f mbar\r\n", value1, value2);
+        if( WIFI_Connect(MBED_CONF_APP_WIFI_SSID, MBED_CONF_APP_WIFI_PASSWORD, WIFI_ECN_WPA2_PSK) == WIFI_STATUS_OK) {
+            // printf("> WiFi connected \n");
+            if(WIFI_GetIP_Address(IP_Addr) == WIFI_STATUS_OK) {
+                printf("> IP Address : %d.%d.%d.%d\n",
+                       IP_Addr[0],
+                       IP_Addr[1],
+                       IP_Addr[2],
+                       IP_Addr[3]);
+                return true;
+            } else {
+                printf("> ERROR : es-wifi module CANNOT get IP address\n");
+            }
+        } else {
+            printf("> ERROR : es-wifi module NOT connected\n");
+        }
+    } else {
+        printf("> ERROR : WIFI Module cannot be initialized.\n");
+    }
+    return false;
+}
 
-    printf("---\r\n");
 
-    magnetometer.get_m_axes(axes);
-#ifdef TARGET_DISCO_L475VG_IOT01A
-    printf("LIS3MDL [mag/mgauss]:    %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
-#else // X-NUCLEO-IKS01A2 or SensorTile
-    printf("LSM303AGR [mag/mgauss]:  %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
-    accelerometer.get_x_axes(axes);
-    printf("LSM303AGR [acc/mg]:      %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);    
-#endif    
+/**
+  * @brief  check WiFi connection status, retry once if it's offline
+  * @param  None
+  * @retval True for WiFi (re)connected, False if reconnection failed
+  */
 
-    acc_gyro.get_x_axes(axes);
-    printf("LSM6DSL [acc/mg]:        %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+bool checkWiFi(void)
+{
+    uint8_t  ip_addr[4];
+    if(WIFI_GetIP_Address(ip_addr) == WIFI_STATUS_OK) return true;
+    else {
+        return connectWiFi();
+    }
+}
 
-    acc_gyro.get_g_axes(axes);
-    printf("LSM6DSL [gyro/mdps]:     %6ld, %6ld, %6ld\r\n", axes[0], axes[1], axes[2]);
+/**
+  * @brief  create a TCP session on a server
+  * @param  serverUrl : TCP server URL
+  * @param  serverPort : port TCP server is listening on
+  * @retval true if connection established
+  */
+bool connectToServer(char *serverUrl, uint16_t serverPort, uint8_t socket_id)
+{
+    uint8_t  serverIP[] = {0, 0, 0, 0};
+    uint16_t trials = CONNECTION_TRIAL_MAX;
 
-#ifdef TARGET_DISCO_L475VG_IOT01A
-    uint32_t distance;
-    int status = range.get_distance(&distance);
-    if (status == VL53L0X_ERROR_NONE) {
-        printf("VL53L0X [mm]:            %6ld\r\n", distance);
-    } else {
-        printf("VL53L0X [mm]:                --\r\n");
+    while (trials--) {
+        // get the server IP through a (local) DNS resolve
+        if (WIFI_GetHostAddress(serverUrl, serverIP) == WIFI_STATUS_OK) {
+
+            printf("> %s resolved to: %d.%d.%d.%d\n", serverUrl,
+                   serverIP[0],
+                   serverIP[1],
+                   serverIP[2],
+                   serverIP[3]);
+            // establish TCP connection to server
+            if( WIFI_OpenClientConnection(socket_id, WIFI_TCP_PROTOCOL, "InfluxDB", serverIP, serverPort, 0) == WIFI_STATUS_OK) {
+                printf("> Connected to %s .\n", serverUrl);
+                return true;
+            }
+        } else {
+            printf("> ERROR : Cannot resolve URL: %s\n", serverUrl);
+        }
     }
-#endif
+    if(!trials) {
+        printf("> ERROR : Cannot establish connection\n");
+    }
+    return false;
+}
 
-#if defined (TARGET_SENSOR_TILE)  
-    printf("\033[7A");
-#else    
-    printf("\033[8A");
-#endif
-    wait(0.5);
-  }
-}