diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b49f69..20cd1d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,3 +4,5 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(tic_to_mqtt) + +target_add_binary_data(tic_to_mqtt.elf "main/cacert.pem" TEXT) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 0f5128e..9b048ae 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,2 +1,2 @@ -idf_component_register(SRCS "main.c" "tic.c" "wifi.c" "libteleinfo.cpp" "libteleinfo/src/LibTeleinfo.cpp" +idf_component_register(SRCS "main.c" "tic.c" "wifi.c" "mqtt.c" "libteleinfo.cpp" "libteleinfo/src/LibTeleinfo.cpp" INCLUDE_DIRS ".") diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index cdd74fc..fcf22e5 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -19,7 +19,7 @@ menu "TIC-to-MQTT Configuration" config TIC_UART_STACK_SIZE int "Taille de la pile pour le décodage de la TIC" range 1024 16384 - default 2048 + default 4096 help Defines stack size. Insufficient stack size can cause crash. diff --git a/main/cacert.pem b/main/cacert.pem new file mode 100644 index 0000000..4d6f390 --- /dev/null +++ b/main/cacert.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/main/component.mk b/main/component.mk index 44bd2b5..ef8ee82 100644 --- a/main/component.mk +++ b/main/component.mk @@ -1,3 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # +COMPONENT_EMBED_TXTFILES := cacert.pem diff --git a/main/main.c b/main/main.c index fd098ee..8bc684b 100644 --- a/main/main.c +++ b/main/main.c @@ -7,15 +7,27 @@ #include "esp_log.h" #include "nvs_flash.h" #include "nvs.h" - +#include "esp_tls.h" +#include "esp_crt_bundle.h" #include "tic.h" #include "wifi.h" +#include "mqtt.h" + +// Embedded via component.mk +extern const uint8_t cacert_pem_start[] asm("_binary_cacert_pem_start"); +extern const uint8_t cacert_pem_end[] asm("_binary_cacert_pem_end"); void app_main(void) { // NVS is used to store wifi credentials. So, we need to initialize it first. ESP_ERROR_CHECK(nvs_flash_init()); + // Inject the Let's Encrypt Root CA certificate in the global CA store + ESP_ERROR_CHECK(esp_tls_init_global_ca_store()); + ESP_ERROR_CHECK(esp_tls_set_global_ca_store((const unsigned char *) cacert_pem_start, cacert_pem_end - cacert_pem_start)); + wifi_init_sta(); wifi_wait_for_online(); + mqtt_init(); + mqtt_wait_for_readiness(); tic_uart_init(); } diff --git a/main/mqtt.c b/main/mqtt.c new file mode 100644 index 0000000..1b461bd --- /dev/null +++ b/main/mqtt.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "esp_log.h" +#include "mqtt_client.h" +#include "esp_tls.h" +#include "esp_ota_ops.h" +#include + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_mqtt_event_group; +static esp_mqtt_client_config_t mqtt_cfg; +static esp_mqtt_client_handle_t client; + +/* The event group allows multiple bits for each event, but we only care about one event: + * - we are connected to the MQTT server + */ +#define MQTT_CONNECTED_BIT BIT0 + +static const char *MQTT_LOGGER = "mqtt"; + +void mqtt_publish_data(char* key, char* value) { + char buffer[128]; + snprintf(buffer, sizeof(buffer), "home/power/tic/%s", key); + int qos = 0; + int retain = 0; + if (strcmp(key, "BASE") == 0 || strcmp(key, "HCHP") == 0 || + strcmp(key, "HCHC") == 0 || strcmp(key, "PTEC") == 0) { + qos = 1; + retain = 1; + } + int ret = esp_mqtt_client_publish(client, buffer, value, 0, qos, retain); + if (ret == -1) { + ESP_LOGD(MQTT_LOGGER, "MQTT Message discarded!"); + } +} + +void mqtt_publish_alert(uint8_t value) { + char payload[2] = {'0' + value, 0}; + int ret = esp_mqtt_client_publish(client, "home/power/tic/ADPS", payload, 0, 1, 0); + if (ret == -1) { + ESP_LOGD(MQTT_LOGGER, "MQTT Message discarded!"); + } +} + +esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) { + switch (event->event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(MQTT_LOGGER, "MQTT_EVENT_CONNECTED"); + xEventGroupSetBits(s_mqtt_event_group, MQTT_CONNECTED_BIT); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(MQTT_LOGGER, "MQTT_EVENT_DISCONNECTED"); + xEventGroupClearBits(s_mqtt_event_group, MQTT_CONNECTED_BIT); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(MQTT_LOGGER, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(MQTT_LOGGER, "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { + ESP_LOGI(MQTT_LOGGER, "Last error code reported from esp-tls: 0x%x", event->error_handle->esp_tls_last_esp_err); + ESP_LOGI(MQTT_LOGGER, "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err); + ESP_LOGI(MQTT_LOGGER, "Last captured errno : %d (%s)", event->error_handle->esp_transport_sock_errno, + strerror(event->error_handle->esp_transport_sock_errno)); + } else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) { + ESP_LOGI(MQTT_LOGGER, "Connection refused error: 0x%x", event->error_handle->connect_return_code); + } else { + ESP_LOGW(MQTT_LOGGER, "Unknown error type: 0x%x", event->error_handle->error_type); + } + break; + default: + ESP_LOGD(MQTT_LOGGER, "Other event id:%d", event->event_id); + break; + } + return ESP_OK; +} + +void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { + ESP_LOGD(MQTT_LOGGER, "Event dispatched from event loop base=%s, event_id=%d", base, event_id); + mqtt_event_handler_cb(event_data); +} + +char* get_nvs_string(nvs_handle_t nvs, char* key) { + size_t required_size; + esp_err_t err = nvs_get_str(nvs, key, NULL, &required_size); + if (err != ESP_OK) { + return NULL; + } + char* value = malloc(required_size); + if (!value) { + return NULL; + } + err = nvs_get_str(nvs, key, value, &required_size); + if (err != ESP_OK) { + free(value); + return NULL; + } + return value; +} + +void mqtt_init(void) { + s_mqtt_event_group = xEventGroupCreate(); + + nvs_handle_t nvs; + esp_err_t err = nvs_open("mqtt", NVS_READONLY, &nvs); + if (err != ESP_OK) { + printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + return; + } + + memset(&mqtt_cfg, 0, sizeof(mqtt_cfg)); + mqtt_cfg.uri = get_nvs_string(nvs, "url"); + mqtt_cfg.use_global_ca_store = true; + mqtt_cfg.username = get_nvs_string(nvs, "username"); + mqtt_cfg.password = get_nvs_string(nvs, "password"); + + nvs_close(nvs); + + client = esp_mqtt_client_init(&mqtt_cfg); + esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client); + esp_mqtt_client_start(client); +} + +void mqtt_wait_for_readiness(void) { + /* Waiting until the connection is established (MQTT_CONNECTED_BIT). + * The bits are set by mqtt_event_handler_cb() (see above) + */ + EventBits_t bits = xEventGroupWaitBits(s_mqtt_event_group, + MQTT_CONNECTED_BIT, + pdFALSE, + pdFALSE, + portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & MQTT_CONNECTED_BIT) { + ESP_LOGI(MQTT_LOGGER, "Connected to the MQTT server!"); + } else { + ESP_LOGE(MQTT_LOGGER, "UNEXPECTED EVENT"); + } + + //vEventGroupDelete(s_mqtt_event_group); +} diff --git a/main/mqtt.h b/main/mqtt.h new file mode 100644 index 0000000..4a498af --- /dev/null +++ b/main/mqtt.h @@ -0,0 +1,9 @@ +#ifndef __MQTT_H__ +#define __MQTT_H__ + +void mqtt_init(); +void mqtt_wait_for_readiness(void); +void mqtt_publish_data(char* key, char* value); +void mqtt_publish_alert(uint8_t value); + +#endif diff --git a/main/tic.c b/main/tic.c index f4a22f8..95be29c 100644 --- a/main/tic.c +++ b/main/tic.c @@ -7,23 +7,18 @@ #include "esp_log.h" #include "tic.h" #include "libteleinfo.h" +#include "mqtt.h" static const char *TIC_LOGGER = "tic"; void tic_data_callback(time_t ts, uint8_t flags, char * name, char * value) { - char * prefix = ""; - - if (flags & LIBTELEINFO_FLAGS_ADDED) - prefix = "NEW ->"; - - if (flags & LIBTELEINFO_FLAGS_UPDATED) - prefix = "MAJ ->"; - - ESP_LOGI(TIC_LOGGER, "%s %s=%s", prefix, name, value); + ESP_LOGD(TIC_LOGGER, "%s=%s", name, value); + mqtt_publish_data(name, value); } void tic_adps_callback(uint8_t phase) { - ESP_LOGI(TIC_LOGGER, "ALERTE phase=%d", phase); + ESP_LOGD(TIC_LOGGER, "ALERT phase=%d", phase); + mqtt_publish_alert(phase); } static void tic_uart_read(void *pvParameters) {