commit a054b4c5466b03443a4a6af034e55f570aeaf34e Author: Nicolas MASSE Date: Wed Jan 26 16:12:26 2022 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..197ade7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +sdkconfig.old diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5b49f69 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(tic_to_mqtt) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..166cee1 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := tic_to_mqtt + +include $(IDF_PATH)/make/project.mk diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..9d242cc --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" "tic.c" + INCLUDE_DIRS ".") diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild new file mode 100644 index 0000000..6431b97 --- /dev/null +++ b/main/Kconfig.projbuild @@ -0,0 +1,26 @@ +menu "TIC-to-MQTT Configuration" + + config TIC_MODE + int "Mode historique (0) ou standard (1)" + range 0 1 + default 0 + help + Configure la liaison TIC en mode historique ou en mode standard. + + config TIC_UART_RXD + int "Pin utilisée pour recevoir la TIC" + range 0 34 if IDF_TARGET_ESP32 + range 0 46 if IDF_TARGET_ESP32S2 + range 0 19 if IDF_TARGET_ESP32C3 + default 16 + help + GPIO number for UART RX pin. + + config TIC_UART_STACK_SIZE + int "Taille de la pile pour le décodage de la TIC" + range 1024 16384 + default 2048 + help + Defines stack size. Insufficient stack size can cause crash. + +endmenu diff --git a/main/component.mk b/main/component.mk new file mode 100644 index 0000000..44bd2b5 --- /dev/null +++ b/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..bf87ae9 --- /dev/null +++ b/main/main.c @@ -0,0 +1,12 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/uart.h" +#include "esp_log.h" +#include "tic.h" + +void app_main(void) { + tic_uart_init(); +} diff --git a/main/tic.c b/main/tic.c new file mode 100644 index 0000000..7956e46 --- /dev/null +++ b/main/tic.c @@ -0,0 +1,149 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/uart.h" +#include "esp_log.h" +#include "tic.h" + +static const char *TIC_LOGGER = "tic"; +static QueueHandle_t uart_queue; + +uint8_t tic_checksum(uint8_t* buffer, size_t start, size_t end) { + uint16_t checksum = 0; + for (size_t i = start; i <= end; i++) { + checksum += buffer[i]; + } + checksum = (checksum & 0x3F) + 0x20; + return (uint8_t)checksum; +} + +static void tic_uart_events(void *pvParameters) { + uart_event_t event; + size_t buffered_size; + uint8_t* buffer = (uint8_t*) malloc(TIC_READ_BUFFER_SIZE); + uint8_t methods[2] = {0, 0}; + for(;;) { + // Waiting for UART event. + if(xQueueReceive(uart_queue, (void * )&event, (portTickType)portMAX_DELAY)) { + switch(event.type) { + case UART_FIFO_OVF: + case UART_BUFFER_FULL: + case UART_PARITY_ERR: + case UART_FRAME_ERR: + ESP_LOGI(TIC_LOGGER, "error: event type %d. Discarding existing data...", event.type); + uart_flush_input(TIC_UART_NUM); + xQueueReset(uart_queue); + break; + case UART_PATTERN_DET: + bzero(buffer, TIC_READ_BUFFER_SIZE); + uart_get_buffered_data_len(TIC_UART_NUM, &buffered_size); + int pos = uart_pattern_pop_pos(TIC_UART_NUM); + if (pos == -1) { + // There used to be a UART_PATTERN_DET event, but the pattern position queue is full so that it can not + // record the position. We should set a larger queue size. + // As an example, we directly flush the rx buffer here. + uart_flush_input(TIC_UART_NUM); + } else { + uint8_t* scratch = buffer; + uart_read_bytes(TIC_UART_NUM, scratch, pos + 1, 100 / portTICK_PERIOD_MS); + scratch[pos] = '\0'; + ESP_LOGD(TIC_LOGGER, "read data: '%s', separator at %d", scratch, pos); + + // '\n' + tag + sep + value + sep + checksum = at least 6 characters + if (pos < 6) { + ESP_LOGI(TIC_LOGGER, "read string is too short: %d", pos); + break; + } + + // during manual tests, there is no '\n' at the start of the string + // but according to Enedis, there is one in the actual data sent. + if (scratch[0] == '\n') { + scratch++; + pos--; + } + + if (methods[0] >= TIC_CHECKSUM_THRESHOLD || methods[1] >= TIC_CHECKSUM_THRESHOLD) { + if (methods[0] >= TIC_CHECKSUM_THRESHOLD && tic_checksum(scratch, 0, pos - 3) == scratch[pos - 1]) { + ESP_LOGD(TIC_LOGGER, "validated with method1: %s", scratch); + } else if (methods[1] >= TIC_CHECKSUM_THRESHOLD && tic_checksum(scratch, 0, pos - 2) == scratch[pos - 1]) { + ESP_LOGD(TIC_LOGGER, "validated with method2: %s", scratch); + } else { + ESP_LOGI(TIC_LOGGER, "wrong checksum: %s", scratch); + break; + } + } else { + if (tic_checksum(scratch, 0, pos - 3) == scratch[pos - 1]) { + methods[0]++; + ESP_LOGD(TIC_LOGGER, "validated with method 1 while learning: %s", scratch); + } else if (tic_checksum(scratch, 0, pos - 2) == scratch[pos - 1]) { + methods[1]++; + ESP_LOGD(TIC_LOGGER, "validated with method 2 while learning: %s", scratch); + } else { + ESP_LOGI(TIC_LOGGER, "wrong checksum: %s", scratch); + break; + } + } + + // trim the checksum + scratch[pos - 2] = '\0'; + ESP_LOGD(TIC_LOGGER, "validated: '%s'", scratch); + } + break; + //Others + default: + break; + } + } + } + free(buffer); + buffer = NULL; + vTaskDelete(NULL); +} + +void tic_uart_init() { + // Eventually, the log level can be reduced here + //ESP_ERROR_CHECK(esp_log_level_set(TIC_LOGGER, ESP_LOG_INFO)); + +#if CONFIG_TIC_MODE == 0 + uart_config_t uart_config = { + .baud_rate = 1200, + .data_bits = UART_DATA_7_BITS, + .parity = UART_PARITY_EVEN, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_APB, + }; +#endif +#if CONFIG_TIC_MODE == 1 + uart_config_t uart_config = { + .baud_rate = 9600, + // TODO: validate other parameters + .data_bits = UART_DATA_7_BITS, + .parity = UART_PARITY_EVEN, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_APB, + }; +#endif + // Setup the UART + ESP_ERROR_CHECK(uart_driver_install(TIC_UART_NUM, TIC_BUFFER_SIZE, TIC_BUFFER_SIZE, 20, &uart_queue, 0)); + ESP_ERROR_CHECK(uart_param_config(TIC_UART_NUM, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(TIC_UART_NUM, UART_PIN_NO_CHANGE, CONFIG_TIC_UART_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(uart_enable_pattern_det_baud_intr(TIC_UART_NUM, '\r', 1, 9, 0, 0)); + ESP_ERROR_CHECK(uart_pattern_queue_reset(TIC_UART_NUM, 20)); + + // Create a task to handler UART event from ISR + BaseType_t xReturned; + xReturned = xTaskCreate(tic_uart_events, + "tic_uart_events", + CONFIG_TIC_UART_STACK_SIZE, /* Stack size in words, not bytes. */ + NULL, /* Parameter passed into the task. */ + tskIDLE_PRIORITY + 12, + NULL); + if (xReturned != pdPASS) { + ESP_LOGE(TIC_LOGGER, "xTaskCreate('tic_uart_events'): %d", xReturned); + abort(); + } +} \ No newline at end of file diff --git a/main/tic.h b/main/tic.h new file mode 100644 index 0000000..581f7e6 --- /dev/null +++ b/main/tic.h @@ -0,0 +1,11 @@ +#ifndef __TIC_H__ +#define __TIC_H__ + +#define TIC_BUFFER_SIZE (2048) +#define TIC_READ_BUFFER_SIZE (TIC_BUFFER_SIZE / 2) +#define TIC_CHECKSUM_THRESHOLD (10) +#define TIC_UART_NUM UART_NUM_2 + +void tic_uart_init(); + +#endif