Can read and decode TIC data (badly tested)
This commit is contained in:
parent
e9ffb4a032
commit
6c84324fc4
17 changed files with 758 additions and 2 deletions
195
TIC.raw.sample
Normal file
195
TIC.raw.sample
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
SE 000051519
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINS
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051521 Y
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 001 X
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 00250 (
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051521 Y
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 001 X
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 00250 (
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051521 Y
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 001 X
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 00250 (
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051521 Y
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 005 \
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 01250 )
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051521 Y
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 005 \
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 01160 )
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051521 Y
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 004 [
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 00980 2
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051522 Z
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 004 [
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 01020 $
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051522 Z
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 004 [
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 01020 $
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051522 Z
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
||||||
|
IINST 004 [
|
||||||
|
|
||||||
|
IMAX 090 H
|
||||||
|
|
||||||
|
PAPP 01010 #
|
||||||
|
|
||||||
|
HHPHC A ,
|
||||||
|
|
||||||
|
MOTDETAT 000000 B
|
||||||
|
|
||||||
|
ADCO 042375023610 8
|
||||||
|
|
||||||
|
OPTARIF BASE 0
|
||||||
|
|
||||||
|
ISOUSC 30 9
|
||||||
|
|
||||||
|
BASE 000051522 Z
|
||||||
|
|
||||||
|
PTEC TH.. $
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
idf_component_register(SRCS "main.c"
|
idf_component_register(SRCS "tic.c" "uart.c" "main.c"
|
||||||
INCLUDE_DIRS ".")
|
INCLUDE_DIRS ".")
|
||||||
|
|
44
main/Kconfig.projbuild
Normal file
44
main/Kconfig.projbuild
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
menu "Enedis TIC configuration"
|
||||||
|
|
||||||
|
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||||
|
|
||||||
|
config TIC_UART_PORT_NUM
|
||||||
|
int "UART port number"
|
||||||
|
range 0 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
|
||||||
|
default 1 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
|
||||||
|
range 0 1
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
UART communication port number towards Enedis' TIC
|
||||||
|
|
||||||
|
config TIC_UART_BAUD_RATE
|
||||||
|
int "UART communication speed"
|
||||||
|
range 1200 115200
|
||||||
|
default 1200
|
||||||
|
help
|
||||||
|
UART communication speed for TIC. 1200 for historique, 9600 for
|
||||||
|
standard
|
||||||
|
|
||||||
|
config TIC_UART_RXD
|
||||||
|
int "UART RXD pin number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
|
||||||
|
default 5
|
||||||
|
help
|
||||||
|
GPIO number for UART RX pin. See UART documentation for more information
|
||||||
|
about available pin numbers for UART.
|
||||||
|
|
||||||
|
# config TIC_UART_TXD
|
||||||
|
# int "UART TXD pin number"
|
||||||
|
# range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
# default 4
|
||||||
|
# help
|
||||||
|
# GPIO number for UART TX pin. See UART documentation for more information
|
||||||
|
# about available pin numbers for UART.
|
||||||
|
|
||||||
|
config TIC_UART_TASK_STACK_SIZE
|
||||||
|
int "UART task stack size"
|
||||||
|
range 1024 16384
|
||||||
|
default 2048
|
||||||
|
help
|
||||||
|
Defines stack size for UART echo example. Insufficient stack size can cause crash.
|
||||||
|
endmenu
|
10
main/main.c
10
main/main.c
|
@ -1,6 +1,14 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "uart.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
static const char *TAG = "MAIN";
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
|
ESP_LOGI(TAG, "Arriving in main\n");
|
||||||
|
xTaskCreate(uart_listen_task, "uart_listen_task", TIC_TASK_STACK_SIZE, NULL, 10, NULL);
|
||||||
}
|
}
|
||||||
|
|
174
main/tic.c
Normal file
174
main/tic.c
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "tic.h"
|
||||||
|
|
||||||
|
static const char FRAME_START = 0x02;
|
||||||
|
static const char GROUP_START = 0x0a;
|
||||||
|
static const char FIELD_SEP = 0x20;
|
||||||
|
|
||||||
|
DecodeState* create_decode_state(size_t queue_size) {
|
||||||
|
DecodeState* out = malloc(sizeof(DecodeState));
|
||||||
|
out->cur_frame = NULL;
|
||||||
|
out->frame_queue = xQueueCreate(queue_size, sizeof(InfoFrame*));
|
||||||
|
out->checksum = 0;
|
||||||
|
out->fill_step = GFS_NotStarted;
|
||||||
|
out->postpone_buffer[0] = '\0';
|
||||||
|
out->postpone_buffer_size = 0;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_decode_state(DecodeState* st) {
|
||||||
|
vQueueDelete(st->frame_queue);
|
||||||
|
if(st->cur_frame != NULL) {
|
||||||
|
tic_delete_frame(st->cur_frame);
|
||||||
|
free(st->cur_frame);
|
||||||
|
}
|
||||||
|
free(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tic_delete_group(InfoGroup* group) {
|
||||||
|
free(group->time_str);
|
||||||
|
if(group->data_type == GROUP_DATA_STR)
|
||||||
|
free(group->data.str);
|
||||||
|
}
|
||||||
|
void tic_delete_frame(InfoFrame* frame) {
|
||||||
|
for(size_t g_id=0; g_id < frame->group_count; ++g_id)
|
||||||
|
tic_delete_group(frame->groups + g_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* tic_discard_until_frame(char* buffer, size_t buf_size) {
|
||||||
|
size_t pos=0;
|
||||||
|
for(pos=0; pos < buf_size; ++pos) {
|
||||||
|
if(buffer[pos] == FRAME_START) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(pos == buf_size)
|
||||||
|
return NULL;
|
||||||
|
return buffer + pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t BAD_STR_TO_INT = ~0U;
|
||||||
|
uint32_t str_to_int(char* buffer, const char* buffer_end) {
|
||||||
|
uint64_t res = 0;
|
||||||
|
for(char* chr=buffer; chr != buffer_end; ++chr) {
|
||||||
|
if('0' <= *chr && *chr <= '9') {
|
||||||
|
res *= 10;
|
||||||
|
res += (*chr - '0');
|
||||||
|
} else {
|
||||||
|
return BAD_STR_TO_INT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(res > BAD_STR_TO_INT)
|
||||||
|
return BAD_STR_TO_INT;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_data_pointers(char** start, char** end, const char* buffer, char* pos, DecodeState* state) {
|
||||||
|
if(*start == NULL) { // Postponed data
|
||||||
|
*start = state->postpone_buffer;
|
||||||
|
size_t copy_size = pos - buffer;
|
||||||
|
if(copy_size + state->postpone_buffer_size > POSTPONE_BUFFER_MAXSIZE)
|
||||||
|
copy_size = POSTPONE_BUFFER_MAXSIZE - state->postpone_buffer_size;
|
||||||
|
char* postpone_end = state->postpone_buffer
|
||||||
|
+ state->postpone_buffer_size;
|
||||||
|
memcpy(
|
||||||
|
postpone_end,
|
||||||
|
buffer,
|
||||||
|
copy_size);
|
||||||
|
*end = postpone_end + copy_size;
|
||||||
|
} else { // No postponed data
|
||||||
|
*end = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int tic_decode(char* buffer, size_t buf_size, DecodeState* state) {
|
||||||
|
int frames_decoded = 0;
|
||||||
|
char* start = NULL;
|
||||||
|
char* data_end = NULL;
|
||||||
|
|
||||||
|
for(char* pos = buffer; pos != buffer + buf_size; ++pos) {
|
||||||
|
if(state->fill_step == GFS_Checksum) { // End of frame -- verify checksum
|
||||||
|
if(((state->checksum & 0x3F) + 0x20) != *pos) {
|
||||||
|
state->fill_step = GFS_Invalid;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
state->fill_step = GFS_Done;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
state->checksum += *pos; // Update checksum
|
||||||
|
|
||||||
|
if(*pos == FRAME_START) {
|
||||||
|
if(state->cur_frame != NULL) {
|
||||||
|
frames_decoded += 1;
|
||||||
|
if(uxQueueSpacesAvailable(state->frame_queue) == 0) {
|
||||||
|
tic_delete_frame(state->cur_frame);
|
||||||
|
free(state->cur_frame);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
xQueueSend(state->frame_queue, state->cur_frame, 0);
|
||||||
|
}
|
||||||
|
state->cur_frame = malloc(sizeof(InfoFrame));
|
||||||
|
memset(state->cur_frame, 0, sizeof(InfoFrame));
|
||||||
|
state->cur_frame->group_count = 0;
|
||||||
|
}
|
||||||
|
else if(*pos == GROUP_START) {
|
||||||
|
// Reset checksum
|
||||||
|
state->checksum = 0;
|
||||||
|
|
||||||
|
if(state->fill_step == GFS_Done) {
|
||||||
|
state->cur_frame->group_count++;
|
||||||
|
state->fill_step = GFS_NotStarted;
|
||||||
|
} else if(state->fill_step == GFS_Invalid) {
|
||||||
|
memset(state->cur_frame->groups + state->cur_frame->group_count,
|
||||||
|
'0', sizeof(InfoGroup));
|
||||||
|
state->fill_step = GFS_NotStarted;
|
||||||
|
} else if(state->fill_step == GFS_NotStarted) {
|
||||||
|
state->fill_step = GFS_Tag;
|
||||||
|
start = pos + 1;
|
||||||
|
} else {
|
||||||
|
state->fill_step = GFS_Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(*pos == FIELD_SEP) {
|
||||||
|
InfoGroup* cur_group =
|
||||||
|
state->cur_frame->groups + state->cur_frame->group_count;
|
||||||
|
if(state->fill_step == GFS_Tag) {
|
||||||
|
get_data_pointers(&start, &data_end, buffer, pos, state);
|
||||||
|
size_t label_size = data_end - start + 1;
|
||||||
|
if(label_size > INFO_GROUP_LABEL_MAX_SIZE)
|
||||||
|
label_size = INFO_GROUP_LABEL_MAX_SIZE;
|
||||||
|
memcpy(cur_group->label, start, label_size-1);
|
||||||
|
cur_group->label[label_size-1] = '\0';
|
||||||
|
state->fill_step = GFS_Data;
|
||||||
|
start = pos + 1;
|
||||||
|
}
|
||||||
|
else if(state->fill_step == GFS_Data) {
|
||||||
|
get_data_pointers(&start, &data_end, buffer, pos, state);
|
||||||
|
uint32_t int_val = str_to_int(start, data_end);
|
||||||
|
if(int_val == BAD_STR_TO_INT) {
|
||||||
|
size_t len = data_end - start + 1;
|
||||||
|
cur_group->data_type = GROUP_DATA_STR;
|
||||||
|
cur_group->data.str = malloc(sizeof(char) * len);
|
||||||
|
memcpy(cur_group->data.str, start, len-1);
|
||||||
|
cur_group->data.str[len-1] = '\0';
|
||||||
|
} else {
|
||||||
|
cur_group->data_type = GROUP_DATA_NUM;
|
||||||
|
cur_group->data.num = int_val;
|
||||||
|
}
|
||||||
|
state->fill_step = GFS_Checksum;
|
||||||
|
state->checksum -= *pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if((state->fill_step == GFS_Tag || state->fill_step == GFS_Data) && start != NULL) {
|
||||||
|
size_t postpone_size = (buffer + buf_size) - start;
|
||||||
|
if(postpone_size > POSTPONE_BUFFER_MAXSIZE)
|
||||||
|
postpone_size = POSTPONE_BUFFER_MAXSIZE;
|
||||||
|
memcpy(state->postpone_buffer, start, postpone_size);
|
||||||
|
state->postpone_buffer_size = postpone_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return frames_decoded;
|
||||||
|
}
|
75
main/tic.h
Normal file
75
main/tic.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef CPU_TEST
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#else
|
||||||
|
#include "cpu_test_harness.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define INFO_GROUP_LABEL_MAX_SIZE 12lu
|
||||||
|
typedef struct {
|
||||||
|
char label[INFO_GROUP_LABEL_MAX_SIZE];
|
||||||
|
char* time_str;
|
||||||
|
union {
|
||||||
|
uint32_t num;
|
||||||
|
char* str;
|
||||||
|
} data;
|
||||||
|
enum {
|
||||||
|
GROUP_DATA_NUM,
|
||||||
|
GROUP_DATA_STR
|
||||||
|
} data_type;
|
||||||
|
} InfoGroup;
|
||||||
|
|
||||||
|
#define INFO_FRAME_MAX_GROUPS 12lu
|
||||||
|
typedef struct {
|
||||||
|
InfoGroup groups[INFO_FRAME_MAX_GROUPS];
|
||||||
|
uint8_t group_count;
|
||||||
|
} InfoFrame;
|
||||||
|
|
||||||
|
typedef QueueHandle_t InfoFrameQueue;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GFS_NotStarted,
|
||||||
|
GFS_Tag,
|
||||||
|
GFS_Data,
|
||||||
|
GFS_Checksum,
|
||||||
|
GFS_Done,
|
||||||
|
GFS_Invalid
|
||||||
|
} GroupFillStep;
|
||||||
|
|
||||||
|
#define POSTPONE_BUFFER_MAXSIZE 32lu
|
||||||
|
typedef struct {
|
||||||
|
InfoFrameQueue frame_queue;
|
||||||
|
InfoFrame* cur_frame;
|
||||||
|
char checksum;
|
||||||
|
GroupFillStep fill_step;
|
||||||
|
char postpone_buffer[POSTPONE_BUFFER_MAXSIZE];
|
||||||
|
size_t postpone_buffer_size;
|
||||||
|
} DecodeState;
|
||||||
|
|
||||||
|
/// Initialize a decode state
|
||||||
|
DecodeState* create_decode_state(size_t queue_size);
|
||||||
|
/// Frees a DecodeState
|
||||||
|
void free_decode_state(DecodeState*);
|
||||||
|
|
||||||
|
/// Deletes the contents of an InfoGroup
|
||||||
|
void tic_delete_group(InfoGroup* group);
|
||||||
|
/** Deletes the contents of an InfoFrame. The call to free() itself must be
|
||||||
|
* done by the user */
|
||||||
|
void tic_delete_frame(InfoFrame* frame);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discard characters until just before a frame is started, and return a
|
||||||
|
* pointer to the beginning of the next frame (including the start of frame
|
||||||
|
* marker), or NULL if no frame was started.
|
||||||
|
*/
|
||||||
|
char* tic_discard_until_frame(char* buffer, size_t buf_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the buffer, updating the decode state and emitting frames as they are
|
||||||
|
* finished.
|
||||||
|
* Returns how many groups were decoded.
|
||||||
|
*/
|
||||||
|
int tic_decode(char* buffer, size_t buf_size, DecodeState* state);
|
102
main/uart.c
Normal file
102
main/uart.c
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/* UART reception file -- reads data from Linky's TIC */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_sleep.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "tic.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an example which echos any data it receives on configured UART back to the sender,
|
||||||
|
* with hardware flow control turned off. It does not use UART driver event queue.
|
||||||
|
*
|
||||||
|
* - Port: configured UART
|
||||||
|
* - Receive (Rx) buffer: on
|
||||||
|
* - Transmit (Tx) buffer: off
|
||||||
|
* - Flow control: off
|
||||||
|
* - Event queue: off
|
||||||
|
* - Pin assignment: see defines below (See Kconfig)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TIC_RXD (CONFIG_TIC_UART_RXD)
|
||||||
|
#define TIC_RTS (UART_PIN_NO_CHANGE)
|
||||||
|
#define TIC_CTS (UART_PIN_NO_CHANGE)
|
||||||
|
|
||||||
|
#define TIC_UART_PORT_NUM (CONFIG_TIC_UART_PORT_NUM)
|
||||||
|
#define TIC_UART_BAUD_RATE (CONFIG_TIC_UART_BAUD_RATE)
|
||||||
|
static const char *TAG = "TIC UART";
|
||||||
|
|
||||||
|
#define BUF_SIZE (1024)
|
||||||
|
#define TIC_QUEUE_SIZE 64
|
||||||
|
|
||||||
|
static void uart_light_sleep() {
|
||||||
|
// TODO
|
||||||
|
/*
|
||||||
|
ESP_LOGI(TAG, "Entering sleep.\n");
|
||||||
|
vTaskDelay(10/portTICK_PERIOD_MS);
|
||||||
|
ESP_ERROR_CHECK(esp_light_sleep_start());
|
||||||
|
esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
|
||||||
|
ESP_LOGI(TAG, "Woken up: %d\n", wakeup_cause);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_listen_task(void *arg)
|
||||||
|
{
|
||||||
|
/* Configure parameters of an UART driver,
|
||||||
|
* communication pins and install the driver */
|
||||||
|
uart_config_t uart_config = {
|
||||||
|
.baud_rate = TIC_UART_BAUD_RATE,
|
||||||
|
.data_bits = UART_DATA_8_BITS,
|
||||||
|
.parity = UART_PARITY_DISABLE,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||||
|
.source_clk = UART_SCLK_DEFAULT,
|
||||||
|
};
|
||||||
|
int intr_alloc_flags = 0;
|
||||||
|
|
||||||
|
#if CONFIG_UART_ISR_IN_IRAM
|
||||||
|
intr_alloc_flags = ESP_INTR_FLAG_IRAM;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(uart_param_config(TIC_UART_PORT_NUM, &uart_config));
|
||||||
|
ESP_ERROR_CHECK(uart_set_pin(TIC_UART_PORT_NUM, UART_PIN_NO_CHANGE, TIC_RXD, TIC_RTS, TIC_CTS));
|
||||||
|
ESP_ERROR_CHECK(uart_driver_install(TIC_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
|
||||||
|
//ESP_ERROR_CHECK(uart_set_wakeup_threshold(TIC_UART_PORT_NUM, 3));
|
||||||
|
//ESP_ERROR_CHECK(esp_sleep_enable_uart_wakeup(TIC_UART_PORT_NUM));
|
||||||
|
|
||||||
|
// Configure a temporary buffer for the incoming data
|
||||||
|
char *data = (char *) malloc(BUF_SIZE);
|
||||||
|
|
||||||
|
// Setup the TIC decoding
|
||||||
|
DecodeState* tic_state = create_decode_state(TIC_QUEUE_SIZE);
|
||||||
|
int initialized = 0;
|
||||||
|
|
||||||
|
// Discard any data already buffered
|
||||||
|
ESP_ERROR_CHECK(uart_flush(TIC_UART_PORT_NUM));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Start receiving.\n");
|
||||||
|
while (1) {
|
||||||
|
int len = uart_read_bytes(TIC_UART_PORT_NUM, data, (BUF_SIZE - 1), 30 / portTICK_PERIOD_MS);
|
||||||
|
if (len) {
|
||||||
|
if(!initialized) {
|
||||||
|
char* init_begin = tic_discard_until_frame(data, len);
|
||||||
|
if(init_begin != NULL) {
|
||||||
|
size_t discarded = init_begin - data;
|
||||||
|
initialized = 1;
|
||||||
|
tic_decode(init_begin, len - discarded, tic_state);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tic_decode(data, len, tic_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[len] = '\0';
|
||||||
|
ESP_LOGI(TAG, "Recv str: %s", (char *) data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uart_light_sleep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_decode_state(tic_state);
|
||||||
|
}
|
6
main/uart.h
Normal file
6
main/uart.h
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#define TIC_TASK_STACK_SIZE (CONFIG_TIC_UART_TASK_STACK_SIZE)
|
||||||
|
|
||||||
|
void uart_listen_task(void* arg);
|
18
test_cpu/Makefile
Normal file
18
test_cpu/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
ESP_DIR=../main/
|
||||||
|
COMMON_OBJS=cpu_test_harness.o
|
||||||
|
ESP_OBJS=tic.o
|
||||||
|
CFLAGS=-Wextra -O2 -g -I$(ESP_DIR) -I. -DCPU_TEST
|
||||||
|
CC=gcc
|
||||||
|
|
||||||
|
TARGETS=test_tic.bin
|
||||||
|
|
||||||
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
test_%.bin: test_%.o $(COMMON_OBJS) $(ESP_OBJS)
|
||||||
|
$(CC) $(CFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%.o: $(ESP_DIR)/%.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
2
test_cpu/README.md
Normal file
2
test_cpu/README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Harness to test relevant parts of the code on a standard CPU instead of the
|
||||||
|
ESP32.
|
31
test_cpu/cpu_test_harness.c
Normal file
31
test_cpu/cpu_test_harness.c
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "cpu_test_harness.h"
|
||||||
|
#include "tic.h"
|
||||||
|
|
||||||
|
QueueHandle_t xQueueCreate(int a, int b) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vQueueDelete(QueueHandle_t _) {}
|
||||||
|
|
||||||
|
int uxQueueSpacesAvailable(QueueHandle_t _) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xQueueSend(QueueHandle_t _, void* elt, int _b) {
|
||||||
|
InfoFrame* frame = elt;
|
||||||
|
printf(">> SENT: Frame with %u groups.\n", frame->group_count);
|
||||||
|
for(size_t grp_id=0; grp_id < frame->group_count; ++grp_id) {
|
||||||
|
InfoGroup* grp = frame->groups + grp_id;
|
||||||
|
printf("Group %02lu: %s %s ", grp_id, grp->label,
|
||||||
|
(grp->data_type == GROUP_DATA_NUM) ? "num" : "str");
|
||||||
|
if(grp->data_type == GROUP_DATA_NUM)
|
||||||
|
printf("%u\n", grp->data.num);
|
||||||
|
else
|
||||||
|
printf("%s\n", grp->data.str);
|
||||||
|
}
|
||||||
|
|
||||||
|
tic_delete_frame(frame);
|
||||||
|
free(frame);
|
||||||
|
}
|
11
test_cpu/cpu_test_harness.h
Normal file
11
test_cpu/cpu_test_harness.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
#ifndef CPU_TEST
|
||||||
|
#define CPU_TEST
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ========== FreeRtos queues ==========
|
||||||
|
typedef void* QueueHandle_t;
|
||||||
|
QueueHandle_t xQueueCreate(int, int);
|
||||||
|
int uxQueueSpacesAvailable(QueueHandle_t);
|
||||||
|
void xQueueSend(QueueHandle_t, void* elt, int);
|
||||||
|
void vQueueDelete(QueueHandle_t);
|
BIN
test_cpu/cpu_test_harness.o
Normal file
BIN
test_cpu/cpu_test_harness.o
Normal file
Binary file not shown.
BIN
test_cpu/test_tic.bin
Executable file
BIN
test_cpu/test_tic.bin
Executable file
Binary file not shown.
36
test_cpu/test_tic.c
Normal file
36
test_cpu/test_tic.c
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "tic.h"
|
||||||
|
|
||||||
|
#define BUF_SIZE 32
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
DecodeState* state = create_decode_state(42);
|
||||||
|
FILE* tic_handle = fopen("../TIC.raw.sample", "rb");
|
||||||
|
|
||||||
|
//int initialized = 0;
|
||||||
|
char buffer[BUF_SIZE];
|
||||||
|
int buf_size = 0;
|
||||||
|
while(1) {
|
||||||
|
buf_size = fread(buffer, sizeof(char), BUF_SIZE, tic_handle);
|
||||||
|
fprintf(stderr, "Read %d bytes\n", buf_size);
|
||||||
|
if(buf_size == 0)
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
if(!initialized) {
|
||||||
|
char* init_begin = tic_discard_until_frame(buffer, buf_size);
|
||||||
|
if(init_begin != NULL) {
|
||||||
|
size_t discarded = (init_begin - buffer);
|
||||||
|
initialized = 1;
|
||||||
|
tic_decode(init_begin, buf_size - discarded, state);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*/
|
||||||
|
tic_decode(buffer, buf_size, state);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_decode_state(state);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
BIN
test_cpu/tic.o
Normal file
BIN
test_cpu/tic.o
Normal file
Binary file not shown.
54
tester/feed_tic.py
Executable file
54
tester/feed_tic.py
Executable file
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import typing as t
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import serial
|
||||||
|
|
||||||
|
|
||||||
|
def iter_frames(raw_data: bytes) -> t.Iterator[bytes]:
|
||||||
|
FRAME_START = 0x02
|
||||||
|
next_frame = b""
|
||||||
|
# Discard first bytes
|
||||||
|
raw_data = raw_data[raw_data.find(FRAME_START) :]
|
||||||
|
|
||||||
|
for ch in raw_data:
|
||||||
|
# Discard first bytes
|
||||||
|
if ch == FRAME_START and next_frame:
|
||||||
|
yield next_frame
|
||||||
|
next_frame = b""
|
||||||
|
next_frame += bytes([ch])
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("-p", "--port", default="/dev/ttyUSB1")
|
||||||
|
parser.add_argument("-b", "--baud", type=int, default=1200)
|
||||||
|
parser.add_argument(
|
||||||
|
"--frame-gap", type=int, default=1500, help="Time between two frames (in ms)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"recorded_data",
|
||||||
|
help="File containing real recorded data to replay",
|
||||||
|
type=Path,
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
data: bytes = b""
|
||||||
|
with args.recorded_data.open("rb") as h:
|
||||||
|
data = h.read()
|
||||||
|
|
||||||
|
with serial.Serial(port=args.port, baudrate=args.baud) as ser:
|
||||||
|
for frame in iter_frames(data):
|
||||||
|
print(f"Feeding {len(frame)}b frame")
|
||||||
|
start_time = time.monotonic()
|
||||||
|
ser.write(frame)
|
||||||
|
elapsed_time = time.monotonic() - start_time
|
||||||
|
sleep_time = args.frame_gap / 1000 - elapsed_time
|
||||||
|
if sleep_time > 0:
|
||||||
|
time.sleep(sleep_time)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in a new issue