a sensor hub for monitoring home environment
Dependencies: HTS221 LIS3MDL LPS22HB LSM303AGR LSM6DSL VL53L0X picojson
Fork of HelloWorld_ST_Sensors by
Diff: main.cpp
- 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>© 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); - } -}