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:

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

/media/uploads/dvddnr/screenshot_20180130-073732.png /media/uploads/dvddnr/screenshot_20180130-073703.png /media/uploads/dvddnr/arducam.jpeg /media/uploads/dvddnr/screenshot_20180216-102601.png

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/

Revision:
1:60fbd0835b9d
Parent:
0:1fc46da4a976
Child:
3:d6d4d692f167
--- a/main.cpp	Tue Jan 23 13:06:11 2018 +0000
+++ b/main.cpp	Wed Jan 24 11:03:38 2018 +0000
@@ -1,66 +1,505 @@
 #include "mbed.h"
 
-// Sensors drivers present in the BSP library
 #include "stm32l475e_iot01_tsensor.h"
 #include "stm32l475e_iot01_hsensor.h"
 #include "stm32l475e_iot01_psensor.h"
-#include "stm32l475e_iot01_magneto.h"
-#include "stm32l475e_iot01_gyro.h"
-#include "stm32l475e_iot01_accelero.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);
 
-DigitalOut led(LED1);
+// 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()
 {
-    float sensor_value = 0;
-    int16_t pDataXYZ[3] = {0};
-    float pGyroDataXYZ[3] = {0};
+
+    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();
+   
 
-    BSP_MAGNETO_Init();
-    BSP_GYRO_Init();
-    BSP_ACCELERO_Init();
+    /* start chatbot */
+    telegram_bot();
+}
 
-    while(1) {
+void telegram_bot(void)
+{
+    int32_t update_id = 0;
+    int32_t chat_id = 0;
+    int json_results;
+    bool well_done = false;
 
-        led = 1;
+
 
-        sensor_value = BSP_TSENSOR_ReadTemp();
-        printf("\nTEMPERATURE = %.2f degC\n", sensor_value);
+    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;
+        }
 
-        sensor_value = BSP_HSENSOR_ReadHumidity();
-        printf("HUMIDITY    = %.2f %%\n", sensor_value);
+        /* 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;
 
-        sensor_value = BSP_PSENSOR_ReadPressure();
-        printf("PRESSURE is = %.2f mBar\n", sensor_value);
+        /* 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;
+            } 
+        }
 
-        led = 0;
+        // update_id not found ?
+        if(well_done == false) continue;
 
-        wait(1);
+        /* 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;
+            } 
+        }
 
-        led = 1;
+        // chat_id not found ?
+        if(well_done == false) continue;
 
-        BSP_MAGNETO_GetXYZ(pDataXYZ);
-        printf("\nMAGNETO_X = %d\n", pDataXYZ[0]);
-        printf("MAGNETO_Y = %d\n", pDataXYZ[1]);
-        printf("MAGNETO_Z = %d\n", pDataXYZ[2]);
+        /*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);
+
 
-        BSP_GYRO_GetXYZ(pGyroDataXYZ);
-        printf("\nGYRO_X = %.2f\n", pGyroDataXYZ[0]);
-        printf("GYRO_Y = %.2f\n", pGyroDataXYZ[1]);
-        printf("GYRO_Z = %.2f\n", pGyroDataXYZ[2]);
+        // 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;
+        }
 
-        BSP_ACCELERO_AccGetXYZ(pDataXYZ);
-        printf("\nACCELERO_X = %d\n", pDataXYZ[0]);
-        printf("ACCELERO_Y = %d\n", pDataXYZ[1]);
-        printf("ACCELERO_Z = %d\n", pDataXYZ[2]);
-
-        led = 0;
-
-        wait(1);
+        /* 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);
+}
+
+