A Telegram BOT for this awesome all-in-one board.
Dependencies: BSP_B-L475E-IOT01 mbed es_wifi jsmn
Telegram Bot for DISCO_L475VG_IOT01
This application embeds aTelegram chatbot into the DISCO_L475VG_IOT01 board.
The Bot answers to the users queries about:
- Real time environmental data taken from the on board sensors.
- Environmental data history of the latest 24 hours stored on board.
- Camera images taken from the Arducam-mini-2mp (optional).
This software uses:
- A modified version of the wifi library provided by ST in order to enable the TCP-SSL connection.
- An open source json parser jsmn (https://github.com/zserge/jsmn).
- A web service (http://now.http.org) to initialize the RTC.
- A web service (https://image-charts.com) to publish temperature,humidity and pressure charts.
- A modified version of the Arducam driver (https://os.mbed.com/users/dflet/)
Compilation
Import in your compiler and modify the following defines:
- WIFI_SSID
- WIFI_PASSWORD
- TELEGRAM_BOT_APIKEY
Please follow the Telegram bots documentation (https://core.telegram.org/bots) to better understand how the Telegram API works and how to create your bot.
In order to support the Arducam-Mini-2MP set WITH_ARDUCAM_2640 #define to 1.
Screenshots
Security
The Inventek wifi module creates the ssl connection but does not authenticate the server's certificate ( AT cmd P9=0 ).
For more details http://www.inventeksys.com/IWIN/programming-certificates-tcp-ssltls/
main.cpp
- Committer:
- dvddnr
- Date:
- 2018-01-24
- Revision:
- 1:60fbd0835b9d
- Parent:
- 0:1fc46da4a976
- Child:
- 3:d6d4d692f167
File content as of revision 1:60fbd0835b9d:
#include "mbed.h" #include "stm32l475e_iot01_tsensor.h" #include "stm32l475e_iot01_hsensor.h" #include "stm32l475e_iot01_psensor.h" #include "es_wifi.h" #include "es_wifi_io.h" #include "jsmn.h" #define WIFI_SSID "" #define WIFI_PASSWORD "" #define TELEGRAM_BOT_APIKEY "" #define WIFI_WRITE_TIMEOUT 10000 #define WIFI_READ_TIMEOUT 10000 #define CONNECTION_TRIAL_MAX 10 // serial output Serial pc(SERIAL_TX, SERIAL_RX); // wifi interfaces ES_WIFIObject_t g_es_wifi_ctx; bool wifi_connect(void); bool open_tcpssl(uint8_t socket, uint16_t local_port, uint16_t remote_port, char *domain_name); bool close_tcpssl(uint32_t socket); // http I/O buffer char g_http_io_buffer[ES_WIFI_DATA_SIZE]; // Telegram json I/O buffer #define TBOT_JSON_BUFFER_SIZE 5 * 1024 char g_json_io_buffer[TBOT_JSON_BUFFER_SIZE]; // telegram REST API const char TELEGRAM_GETUPDATES[] = "GET /bot" TELEGRAM_BOT_APIKEY "/getUpdates?offset=%d&timeout=5&limit=1 HTTP/1.1\r\nHost: api.telegram.org\r\nUser-Agent: curl/7.50.1\r\nAccept: */*\r\n\r\n"; const char TELEGRAM_SENDMESSAGE[] = "GET /bot" TELEGRAM_BOT_APIKEY "/sendMessage HTTP/1.1\r\nHost: api.telegram.org\r\nUser-Agent: curl/7.50.1\r\nAccept: */*\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n"; const char TELEGRAM_CUSTOM_KEYBOARD[] = "{\"keyboard\": [[\"Temperature\"],[\"Humidity\"],[\"Pressure\"]],\"one_time_keyboard\": true}"; bool telegram_get_update(int32_t update_id); bool telegram_send_message(); bool telegram_https_get(bool has_json_payload); #define TELEGRAM_BOT_INCOMING_CMD_SIZE 80 char g_incoming_msg[TELEGRAM_BOT_INCOMING_CMD_SIZE]; void telegram_bot(void); // HTTP util bool http_parse_response(char *http_chunk, uint16_t http_chunk_len, bool *status_code_ok, uint16_t *content_len); // JSON parser #define JSON_MAX_TOKENS 128 jsmn_parser g_json_parser; jsmntok_t g_json_tokens[JSON_MAX_TOKENS]; bool jsoneq(const char *json, jsmntok_t *tok, const char *s,jsmntype_t type); int main() { pc.baud(115200); /*Initialize WIFI module */ printf("> Setup wifi connection ..."); if (wifi_connect()) { printf(" OK\r\n"); } else { printf(" KO\r\n"); while (1) ; } printf("> IP Address : %d.%d.%d.%d\r\n", g_es_wifi_ctx.NetSettings.IP_Addr[0], g_es_wifi_ctx.NetSettings.IP_Addr[1], g_es_wifi_ctx.NetSettings.IP_Addr[2], g_es_wifi_ctx.NetSettings.IP_Addr[3]); /* Setup env sensors */ BSP_TSENSOR_Init(); BSP_HSENSOR_Init(); BSP_PSENSOR_Init(); /* start chatbot */ telegram_bot(); } void telegram_bot(void) { int32_t update_id = 0; int32_t chat_id = 0; int json_results; bool well_done = false; while (1) { // Get updates -- API method getUpdates printf("> Get updates\r\n"); g_json_io_buffer[0]=0; if (telegram_get_update(update_id + 1) == false) { printf("> ERROR telegram_get_update\r\n"); continue; } printf("> JSON content: %s\r\n", g_json_io_buffer); // Parsing json response jsmn_init(&g_json_parser); json_results = jsmn_parse(&g_json_parser,g_json_io_buffer,strlen(g_json_io_buffer),g_json_tokens,JSON_MAX_TOKENS); if(json_results < 4) { printf("> ERROR invalid json response\r\n"); continue; } /* check ok */ if( jsoneq(g_json_io_buffer,&g_json_tokens[1],"ok",JSMN_STRING) == false ) continue; if( jsoneq(g_json_io_buffer,&g_json_tokens[2],"true",JSMN_PRIMITIVE) == false ) continue; /* fetch update id */ well_done = false; for(int i=3;i<json_results;i++) { if( jsoneq(g_json_io_buffer,&g_json_tokens[i],"update_id",JSMN_STRING) == true ) { g_json_io_buffer[g_json_tokens[i+1].end]=0; update_id = atoi(g_json_io_buffer+g_json_tokens[i+1].start); well_done = true; } } // update_id not found ? if(well_done == false) continue; /* fetch chat id */ well_done = false; for(int i=3;i<json_results;i++) { if( jsoneq(g_json_io_buffer,&g_json_tokens[i],"id",JSMN_STRING) == true ) { g_json_io_buffer[g_json_tokens[i+1].end]=0; chat_id = atoi(g_json_io_buffer+g_json_tokens[i+1].start); well_done = true; } } // chat_id not found ? if(well_done == false) continue; /*fetch message */ well_done = false; g_incoming_msg[0]=0; for(int i=3;i<json_results;i++) { if( jsoneq(g_json_io_buffer,&g_json_tokens[i],"text",JSMN_STRING) == true ) { int msg_len = g_json_tokens[i+1].end - g_json_tokens[i+1].start; if( msg_len < TELEGRAM_BOT_INCOMING_CMD_SIZE) { memcpy(g_incoming_msg,g_json_io_buffer+g_json_tokens[i+1].start,msg_len); g_incoming_msg[msg_len] = 0; well_done = true; } break; } } printf("> Incoming msg: %s\n\r",g_incoming_msg); // parse incoming message if( strstr(g_incoming_msg,"Temperature") != NULL) { snprintf(g_json_io_buffer,TBOT_JSON_BUFFER_SIZE,"{\"chat_id\":%d,\"text\":\"Temperature %.2f degC\",\"reply_markup\":%s}", chat_id,BSP_TSENSOR_ReadTemp(),TELEGRAM_CUSTOM_KEYBOARD); } else if( strstr(g_incoming_msg,"Humidity") != NULL) { snprintf(g_json_io_buffer,TBOT_JSON_BUFFER_SIZE,"{\"chat_id\":%d,\"text\":\"Humidity %.2f %%\",\"reply_markup\":%s}", chat_id,BSP_HSENSOR_ReadHumidity(),TELEGRAM_CUSTOM_KEYBOARD); } else if( strstr(g_incoming_msg,"Pressure") != NULL) { snprintf(g_json_io_buffer,TBOT_JSON_BUFFER_SIZE,"{\"chat_id\":%d,\"text\":\"Pressure %.2f mBar\",\"reply_markup\":%s}", chat_id,BSP_PSENSOR_ReadPressure(),TELEGRAM_CUSTOM_KEYBOARD); } else { snprintf(g_json_io_buffer,TBOT_JSON_BUFFER_SIZE,"{\"chat_id\":%d,\"text\":\"Available commands\",\"reply_markup\":%s}", chat_id,TELEGRAM_CUSTOM_KEYBOARD); } if( telegram_send_message() == false) { printf("> ERROR telegram_send_message\r\n"); continue; } jsmn_init(&g_json_parser); json_results = jsmn_parse(&g_json_parser,g_json_io_buffer,strlen(g_json_io_buffer),g_json_tokens,JSON_MAX_TOKENS); if(json_results < 4) { printf("> ERROR invalid json response\r\n"); continue; } /* check ok */ if( jsoneq(g_json_io_buffer,&g_json_tokens[1],"ok",JSMN_STRING) == false ) continue; if( jsoneq(g_json_io_buffer,&g_json_tokens[2],"true",JSMN_PRIMITIVE) == false ) continue; } } /***************************************************************************************** * * * telegram rest api * * ******************************************************************************************/ bool telegram_get_update(int32_t update_id) { /* prepare http get header */ snprintf(g_http_io_buffer, ES_WIFI_PAYLOAD_SIZE, TELEGRAM_GETUPDATES, update_id); return telegram_https_get(false); } bool telegram_send_message() { /* prepare http get header */ snprintf(g_http_io_buffer, ES_WIFI_PAYLOAD_SIZE, TELEGRAM_SENDMESSAGE, strlen(g_json_io_buffer)); return telegram_https_get(true); } bool telegram_https_get(bool has_json_payload) { uint16_t io_s, tx_s; ES_WIFI_Status_t io_status; bool http_ok; int32_t http_content_len; char *http_content_pivot; uint16_t content_chunk_size; bool ret_val = false; static uint8_t socket=0; /* open socket */ socket++; if(socket==4) socket=0; io_status = ES_WIFI_STATUS_ERROR; for (int i = 0; i < CONNECTION_TRIAL_MAX; i++) { printf("> Open SSL connection ...\r\n"); if (open_tcpssl(socket, 0, 443, "api.telegram.org")) { printf("> SSL Connection opened successfully.\r\n"); io_status = ES_WIFI_STATUS_OK; break; } } if (io_status != ES_WIFI_STATUS_OK) { printf("> socket open error.\r\n"); return false; } /* send http get header */ tx_s = strlen(g_http_io_buffer); // printf(g_http_io_buffer); io_s = 0; if (ES_WIFI_SendData(&g_es_wifi_ctx, socket, (uint8_t *)g_http_io_buffer, tx_s, &io_s, WIFI_WRITE_TIMEOUT) != ES_WIFI_STATUS_OK) { printf("> ERROR : CANNOT send data\r\n"); goto happy_end; } if (io_s != tx_s) { printf("> ERROR Send %d of %d.\r\n", io_s, tx_s); goto happy_end; } /* send json payload */ if(has_json_payload) { tx_s = strlen(g_json_io_buffer); io_s = 0; if (ES_WIFI_SendData(&g_es_wifi_ctx, socket, (uint8_t *)g_json_io_buffer, tx_s, &io_s, WIFI_WRITE_TIMEOUT) != ES_WIFI_STATUS_OK) { printf("> ERROR : CANNOT send data\r\n"); goto happy_end; } if (io_s != tx_s) { printf("> ERROR Send %d of %d.\r\n", io_s, tx_s); goto happy_end; } } /* fetch response */ io_status = ES_WIFI_ReceiveData(&g_es_wifi_ctx, socket, (uint8_t *)g_http_io_buffer, ES_WIFI_PAYLOAD_SIZE, &io_s, WIFI_READ_TIMEOUT); if (io_status != ES_WIFI_STATUS_OK) { printf("> ERROR : socket receive data\r\n"); goto happy_end; } /* parse http response for the status code and content len */ http_ok = false; http_content_len = 0; g_http_io_buffer[io_s] = 0; if (io_s == 0 || http_parse_response((char *)g_http_io_buffer, io_s, &http_ok, (uint16_t *)&http_content_len) == false) { printf("< Invalid response\r\n"); for (int i = 0; i < io_s; i++) printf("%c", g_http_io_buffer[i]); printf("> Invalid response\r\n"); goto happy_end; } printf("HTTP OK = %d Content len = %d\r\n", http_ok, http_content_len); /* fetch json response */ http_content_pivot = strstr((char *)g_http_io_buffer, "\r\n\r\n"); if (http_content_pivot != NULL) { http_content_pivot += 4; content_chunk_size = strlen(http_content_pivot); if (content_chunk_size < TBOT_JSON_BUFFER_SIZE) strcpy(g_json_io_buffer, http_content_pivot); http_content_len -= content_chunk_size; } else { printf("< Invalid response\r\n"); for (int i = 0; i < io_s; i++) printf("%c", g_http_io_buffer[i]); printf("> Invalid response\r\n"); goto happy_end; } /* continue to fetch json chunck */ while (http_content_len > 0) { if (ES_WIFI_ReceiveData2(&g_es_wifi_ctx, (uint8_t *)g_http_io_buffer, ES_WIFI_PAYLOAD_SIZE, &io_s) != ES_WIFI_STATUS_OK) { printf("> ERROR : socket receive data\r\n"); goto happy_end; } g_http_io_buffer[io_s] = 0; http_content_len -= io_s; if (http_content_len < 0 || io_s == 0) { printf("> ERROR : http content len overflow\r\n"); goto happy_end; } strcat(g_json_io_buffer, g_http_io_buffer); } ret_val = http_ok; happy_end: printf("> Close SSL connection...\r\n"); close_tcpssl(socket); printf("> done.\r\n"); return ret_val; } /***************************************************************************************** * * * JSON parsing * * ******************************************************************************************/ bool jsoneq(const char *json, jsmntok_t *tok, const char *s,jsmntype_t type) { if (tok->type == type && (int) strlen(s) == tok->end - tok->start && strncmp(json + tok->start, s, tok->end - tok->start) == 0) { return true; } return false; } /***************************************************************************************** * * * HTTP parsing * * ******************************************************************************************/ const char HTTP_200OK[] = "200 OK\r\n"; const char HTTP_CL[] = "Content-Length: "; bool http_parse_response(char *http_chunk, uint16_t http_chunk_len, bool *status_code_ok, uint16_t *content_len) { char *line_pivot, *key_pivot; line_pivot = strstr(http_chunk, "\r\n"); if (line_pivot == NULL) return false; key_pivot = strstr(http_chunk, HTTP_200OK); if (key_pivot == NULL) return false; if (key_pivot < line_pivot) *status_code_ok = true; else *status_code_ok = false; while (1) { line_pivot = strstr(line_pivot + 2, "\r\n"); if (line_pivot == NULL) break; key_pivot = strstr(http_chunk, HTTP_CL); if (key_pivot == NULL) continue; if (key_pivot < line_pivot) { key_pivot += strlen(HTTP_CL); *content_len = atoi((char const *)key_pivot); return true; } } return false; } /***************************************************************************************** * * * WIFI * * ******************************************************************************************/ bool wifi_connect(void) { /* HW setup */ if (ES_WIFI_RegisterBusIO(&g_es_wifi_ctx, SPI_WIFI_Init, SPI_WIFI_DeInit, SPI_WIFI_Delay, SPI_WIFI_SendData, SPI_WIFI_ReceiveData) != ES_WIFI_STATUS_OK) return false; if (ES_WIFI_Init(&g_es_wifi_ctx) != ES_WIFI_STATUS_OK) return false; ES_WIFI_ResetToFactoryDefault(&g_es_wifi_ctx); /* JOIN AP */ for (int i = 0; i < CONNECTION_TRIAL_MAX; i++) { if (ES_WIFI_Connect(&g_es_wifi_ctx, WIFI_SSID, WIFI_PASSWORD, ES_WIFI_SEC_WPA_WPA2) == ES_WIFI_STATUS_OK) { if (ES_WIFI_GetNetworkSettings(&g_es_wifi_ctx) == ES_WIFI_STATUS_OK) return true; else return false; } wait_ms(1000); } return false; } bool open_tcpssl(uint8_t socket, uint16_t local_port, uint16_t remote_port, char *domain_name) { ES_WIFI_Conn_t conn; conn.Number = socket; conn.RemotePort = remote_port; conn.LocalPort = local_port; conn.Type = ES_WIFI_TCP_SSL_CONNECTION; strncpy((char *)conn.RemoteIP, domain_name, sizeof(conn.RemoteIP)); return (ES_WIFI_StartClientConnection(&g_es_wifi_ctx, &conn) == ES_WIFI_STATUS_OK) ? true : false; } bool close_tcpssl(uint32_t socket) { ES_WIFI_Conn_t conn; conn.Number = socket; return (ES_WIFI_StopClientConnection(&g_es_wifi_ctx, &conn)== ES_WIFI_STATUS_OK); }