a sensor hub for monitoring home environment
Dependencies: HTS221 LIS3MDL LPS22HB LSM303AGR LSM6DSL VL53L0X picojson
Fork of HelloWorld_ST_Sensors by
main.cpp
- Committer:
- jaafaryn
- Date:
- 2018-01-29
- Revision:
- 14:0c8967e20f93
- Parent:
- 13:f8e84b12665c
File content as of revision 14:0c8967e20f93:
/*------------------------------------------------------------------------------
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 --> done
- add light sensor to monitor illuminance --> low priority
- improve reading by either: reduce power on time of WiFi module (batch logging)
or by compensating for board own temperature --> WiP
- use a local toolchain to compile mbed projects (with trueStudio) --> WiP
- 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);
/* enable 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);
sense_enabled = true;
/* Setup display */
WAKE_UP_DISPLAY;
WIFI_ICON_OFF;
UPLOAD_ICON_OFF;
NO_WAKEUP_ON_SERIALIN;// no wakeup on serial in
SLEEP_ON_NOTOUCH_30S;// sleep after 30sec no touch
WAKEUP_ON_TOUCH;// wake up on touch
/*Initialize WIFI module */
WiFi_on = connectWiFi();
if (WiFi_on) {
led3 = 1;
//WiFi_led_ticker.attach(&toggle_led3_cb, 0.25);
rtc_synced = sync_rtc();
// enter power save mode
/*if (WIFI_SetPowerSaveMode(1, (UPLOAD_PERIOD_S - 1)*1000) == WIFI_STATUS_OK) {
printf("> es-wifi entered power save mode\n");
} else {
printf("> ERROR: es-wifi did not enter power save mode\n");
}*/
}
// 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);
} // Setup
/* ---------- LOOP ------------- */
void loop() {
time_t rawtime;
tm *pTime;
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_val);
*/
// 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_val);
take_measurements = false;
}
if (send_measurements) {
printf("HTS221: temp= %.2f C, hum= %.2f%%\r\n", tempC_val, RH_val);
// body of the request
char request_body[256];
char *InfluxServerUrl = INFLUX_SERVER_URL;
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_WRITE_EP, 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);
// Establish a connection to DB server
uint8_t socketid = 1;
if (checkWiFi()) {
led3 = 1;
if (connectToServer(INFLUX_SERVER_URL, INFLUX_SERVER_PORT, 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);
/* common 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 open connection to %s \n", InfluxServerUrl);
}
// enter power save mode for UPLOAD_PERIOD_S - 1 sec, beacon interval = 100ms
if (WIFI_SetPowerSaveMode(1, (UPLOAD_PERIOD_S - 1)*1000) == WIFI_STATUS_OK) {
printf("> es-wifi entered power save mode\n");
} else {
printf("> ERROR: es-wifi did not enter power save mode\n");
}
} else {
printf("> ERROR: Could not connect to WiFi \n");
led3 = 0;
}
if (rtc_synced) {
time(&rawtime);
pTime = localtime(&rawtime);
// printf ("Current local time and date: %s", asctime(pTime));
// printf("> %d:%d:%d\n", pTime->tm_hour, pTime->tm_min, pTime->tm_sec);
} else {
rtc_synced = sync_rtc();
}
send_measurements = false;
}
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) {
if (dist_val < 500) {
nextion.printf("sleep=0\xff\xff\xff");// send wake up command
}
distance_val = dist_val;
} else {
distance_val = 9999;
}
// since this is a 1-sec period ticker, we do clock display refresh in here too
// get a formatted local time to be displayed
if (rtc_synced) {
time(&rawtime);
pTime = localtime(&rawtime);
nextion.printf("hour.txt=\"%02d\"\xff\xff\xff", pTime->tm_hour);
nextion.printf("minute.txt=\"%02d\"\xff\xff\xff", pTime->tm_min);
nextion.printf("vis sec,1\xff\xff\xff");
//nextion.printf("vis sec,%d\xff\xff\xff", pTime->tm_sec % 2 == 0 ? 1 : 0);
}
check_proximity = false;
}
} // Loop
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;
}
}
// 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");
char fwVer[32];
if(WIFI_GetModuleFwRevision(fwVer) == WIFI_STATUS_OK) {
printf("> Firmware version: %s\n", fwVer);
}
/*
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");
}*/
if( WIFI_Connect(MBED_CONF_APP_WIFI_SSID, MBED_CONF_APP_WIFI_PASSWORD, WIFI_ECN_WPA2_PSK) == WIFI_STATUS_OK) {
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]);
WIFI_ICON_ON;
UPLOAD_ICON_OFF; // reset db sync status if (re-)connect happened
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");
}
WIFI_ICON_OFF;
UPLOAD_ICON_OFF;
return false;
}
/**
* @brief check WiFi connection status, retry once if it's offline
* @param None
* @retval True for WiFi (re)connected, False if reconnection failed
*/
bool checkWiFi(void)
{
uint8_t ip_addr[4];
if(WIFI_GetIP_Address(ip_addr) == WIFI_STATUS_OK) {
WIFI_ICON_ON;
return true;
} else {
return connectWiFi();
}
}
/**
* @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;
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, "TCP_Client", serverIP, serverPort, 0) == WIFI_STATUS_OK) {
printf("> Connected to %s .\n", serverUrl);
return true;
}
} else {
printf("> ERROR : Cannot resolve URL: %s\n", serverUrl);
printf("> Trials left: %d\n", trials);
}
}
if(!trials) {
printf("> ERROR : Cannot establish connection\n");
}
return false;
}
/**
* @brief get current unix timestamp and use it to sync rtc
* @retval True if successful, False if failed
*/
bool sync_rtc() {
// 1- establish connection to time server (socket 2)
uint8_t socketid = 2;
uint16_t dataLen;
if (checkWiFi()) {
if (connectToServer(TIME_SERVER_URL, TIME_SERVER_PORT, socketid)) {
sprintf((char *)http_request, "GET %s HTTP/1.1\r\nHost: %s \r\n", TIME_SERVER_EP, TIME_SERVER_URL);
strcat((char *)http_request, "Connection: Close\r\n\r\n");
reqLen = strlen((char *)http_request);
if (WIFI_SendData(socketid, http_request, reqLen, &dataLen, WIFI_WRITE_TIMEOUT) != WIFI_STATUS_OK) {
printf("> ERROR: Could not send request to %s\n", TIME_SERVER_URL);
} else {
printf("> TS: Sent %d bytes\n", dataLen);
}
// 2- get server response and parse the json
WIFI_ReceiveData(socketid, http_resp, sizeof(http_resp), &dataLen, WIFI_READ_TIMEOUT);
if(dataLen > 0) {
printf("> TS: Received %d bytes\n", dataLen);
// extract the body (json payload) from the response
char *resp_body = strstr((char *)http_resp, "\r\n\r\n"); // get the body + "OK"
resp_body = strtok(resp_body, "\r\n"); // get rid of 'OK'
picojson::value json_val;
string err = picojson::parse(json_val, resp_body, resp_body + strlen(resp_body));
if(err.empty()) {
// 3- convert timestamp to local time and sync rtc
uint32_t timestamp_val = (unsigned)json_val.get("timestamp").get<double>();
printf("> utc_ts = %u \n",timestamp_val); // print utc
timestamp_val -= 3600*5; // EST: utc - 5h
set_time(timestamp_val);
WIFI_CloseClientConnection(socketid); // close socket on success
return true;
} else {
printf("> ERROR: TS json parsing\n");
}
} else {
printf("> ERROR: Did not receive data from TS server\n");
}
WIFI_CloseClientConnection(socketid); // close socket on failure to retrieve time
} else {
printf("> ERROR: Cannot establish connection to %s \n", TIME_SERVER_URL);
}
}
return false;
}
