Compare commits

...

4 Commits

Author SHA1 Message Date
Julian Metzler 70b7e09789 Add remote poll input 2024-02-04 19:27:52 +01:00
Julian Metzler d41e22aa96 k8200_pst: Add simple rotation timeout 2024-02-04 18:26:59 +01:00
Julian Metzler 818ad88915 Add KRONE 8200 PST driver 2024-02-04 14:49:01 +01:00
Julian Metzler 72d349401b sel_k9000: Fix Rx IO direction 2024-01-28 17:05:44 +01:00
22 changed files with 2201 additions and 9 deletions

View File

@ -37,6 +37,9 @@ config_entry_t config_entries[] = {
{.key = "wg_endpoint", .dataType = STR, .writeOnly = false},
{.key = "wg_endpnt_port", .dataType = U16, .writeOnly = false},
{.key = "wg_keepalive", .dataType = U16, .writeOnly = false},
{.key = "poll_url", .dataType = STR, .writeOnly = false},
{.key = "poll_token", .dataType = STR, .writeOnly = false},
{.key = "poll_interval", .dataType = U16, .writeOnly = false},
};

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS sel_k8200_pst.c
INCLUDE_DIRS include
REQUIRES util)

View File

@ -0,0 +1,59 @@
menu "KRONE 8200 PST Split-Flap Configuration (Selection Mode)"
config K8200_PST_SEL_TX_IO
int "Tx GPIO"
default 0
help
GPIO pin for data transmitted to split-flap modules
config K8200_PST_SEL_RX_IO
int "Rx GPIO"
default 0
help
GPIO pin for data received from split-flap modules
config K8200_PST_SEL_NMI_IO
int "NMI GPIO"
default 0
help
GPIO pin for interrupt signal to split-flap modules
config K8200_PST_SEL_NMI_ACT_HIGH
bool "NMI active-high"
default false
help
NMI GPIO active-low or active-high
choice K8200_PST_SEL_UART
bool "UART to use"
default K8200_PST_SEL_UART_1
config K8200_PST_SEL_UART_0
bool "UART0"
config K8200_PST_SEL_UART_1
bool "UART1"
config K8200_PST_SEL_UART_2
bool "UART2"
endchoice
config K8200_PST_SEL_RX_BUF_SIZE
int "Rx buffer size in bytes"
default 64
help
UART receive buffer size. Must be greater that UART_FIFO_LEN.
config K8200_PST_SEL_TX_BUF_SIZE
int "Tx buffer size in bytes"
default 256
help
UART transmit buffer size. Set to 0 to disable buffer and block while sending data. Otherwise, must be greater that UART_FIFO_LEN.
config K8200_PST_SEL_ROTATION_TIMEOUT
int "Rotation Timeout (ms)"
default 5000
help
How many milliseconds to wait for rotation to complete before sending NMI signal to stop any rogue units
endmenu

View File

@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#include "nvs.h"
esp_err_t display_init(nvs_handle_t* nvsHandle, uint8_t* display_framebuf_mask, uint16_t* display_num_units);
void display_render_frame(uint8_t* frame, uint8_t* prevFrame, uint16_t frameBufSize, uint8_t* display_framebuf_mask, uint16_t display_num_units);

View File

@ -0,0 +1,128 @@
/*
* Functions for KRONE 8200 PST split-flap displays
*/
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <string.h>
#include "driver/uart.h"
#include "macros.h"
#include "util_generic.h"
#include "util_gpio.h"
#include "util_disp_selection.h"
#include "sel_k8200_pst.h"
#if defined(CONFIG_DISPLAY_DRIVER_SEL_KRONE_8200_PST)
#define LOG_TAG "SEL-K8200-PST"
// TODO: Do something with Rx pin?
esp_err_t display_init(nvs_handle_t* nvsHandle, uint8_t* display_framebuf_mask, uint16_t* display_num_units) {
/*
* Set up all needed peripherals
*/
esp_err_t ret;
ret = display_selection_loadAndParseConfiguration(nvsHandle, display_framebuf_mask, display_num_units, LOG_TAG);
if (ret != ESP_OK) return ret;
uart_config_t uart_config = {
.baud_rate = 2400,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_2,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_REF_TICK,
};
if (CONFIG_K8200_PST_SEL_TX_IO >= 0) gpio_reset_pin(CONFIG_K8200_PST_SEL_TX_IO);
if (CONFIG_K8200_PST_SEL_RX_IO >= 0) gpio_reset_pin(CONFIG_K8200_PST_SEL_RX_IO);
if (CONFIG_K8200_PST_SEL_NMI_IO >= 0) gpio_reset_pin(CONFIG_K8200_PST_SEL_NMI_IO);
if (CONFIG_K8200_PST_SEL_TX_IO >= 0) gpio_set_direction(CONFIG_K8200_PST_SEL_TX_IO, GPIO_MODE_OUTPUT);
if (CONFIG_K8200_PST_SEL_RX_IO >= 0) gpio_set_direction(CONFIG_K8200_PST_SEL_RX_IO, GPIO_MODE_INPUT);
if (CONFIG_K8200_PST_SEL_NMI_IO >= 0) gpio_set_direction(CONFIG_K8200_PST_SEL_NMI_IO, GPIO_MODE_OUTPUT);
ret = uart_driver_install(K8200_PST_SEL_UART, CONFIG_K8200_PST_SEL_RX_BUF_SIZE, CONFIG_K8200_PST_SEL_TX_BUF_SIZE, 0, NULL, 0);
if (ret != ESP_OK) return ret;
ret = uart_param_config(K8200_PST_SEL_UART, &uart_config);
if (ret != ESP_OK) return ret;
ret = uart_set_pin(K8200_PST_SEL_UART, CONFIG_K8200_PST_SEL_TX_IO, CONFIG_K8200_PST_SEL_RX_IO, -1, -1);
if (ret != ESP_OK) return ret;
ret = uart_set_line_inverse(K8200_PST_SEL_UART, UART_SIGNAL_TXD_INV);
if (ret != ESP_OK) return ret;
k8200_pst_set_nmi(0);
vTaskDelay(100 / portTICK_PERIOD_MS);
k8200_pst_set_nmi(1);
vTaskDelay(100 / portTICK_PERIOD_MS);
k8200_pst_set_nmi(0);
vTaskDelay(100 / portTICK_PERIOD_MS);
k8200_pst_reset();
return ESP_OK;
}
void k8200_pst_set_nmi(uint8_t state) {
// state: 1 to assert NMI (stop units), 0 to deassert
ESP_LOGI(LOG_TAG, "NMI=%d", state);
if (CONFIG_K8200_PST_SEL_NMI_IO >= 0) gpio_set_level(CONFIG_K8200_PST_SEL_NMI_IO, CONFIG_K8200_PST_SEL_NMI_ACT_HIGH ? !!state : !state);
}
void k8200_pst_reset(void) {
char command = 0x1A;
uart_write_bytes(K8200_PST_SEL_UART, &command, 1);
}
void k8200_pst_home(void) {
char command = 0x1B;
uart_write_bytes(K8200_PST_SEL_UART, &command, 1);
}
void getCommandBytes_SetCode(uint8_t address, uint8_t code, uint8_t* outBuf) {
outBuf[0] = 0x3A;
outBuf[1] = address;
outBuf[2] = uint8_to_bcd(code);
}
void display_render_frame(uint8_t* frame, uint8_t* prevFrame, uint16_t frameBufSize, uint8_t* display_framebuf_mask, uint16_t display_num_units) {
// Nothing to do if frame hasn't changed
if (prevFrame != NULL && memcmp(frame, prevFrame, frameBufSize) == 0) return;
esp_err_t ret = uart_wait_tx_done(K8200_PST_SEL_UART, 10 / portTICK_PERIOD_MS);
if (ret != ESP_OK) return; // If this is ESP_ERR_TIMEOUT, Tx is still ongoing
size_t bufSize = display_num_units * 3 + 1; // + 1 for CMD_SET_ALL at the end
uint8_t* buf = malloc(bufSize);
uint16_t bufIdx = 0;
for (uint16_t addr = 0; addr < frameBufSize; addr++) {
// Skip addresses that aren't present
if (!GET_MASK(display_framebuf_mask, addr)) continue;
getCommandBytes_SetCode(addr, frame[addr], &buf[bufIdx*3]);
bufIdx++;
}
buf[bufSize-1] = 0x1C; // Start all units
ESP_LOG_BUFFER_HEX(LOG_TAG, buf, bufSize);
for (uint16_t i = 0; i < bufSize; i++) {
uart_write_bytes(K8200_PST_SEL_UART, &buf[i], 1);
ets_delay_us(7500);
}
free(buf);
if (prevFrame != NULL) memcpy(prevFrame, frame, frameBufSize);
// TODO: Implement better monitoring by querying rotation status using framebuffer mask
vTaskDelay(CONFIG_K8200_PST_SEL_ROTATION_TIMEOUT / portTICK_PERIOD_MS);
k8200_pst_set_nmi(1);
vTaskDelay(100 / portTICK_PERIOD_MS);
k8200_pst_set_nmi(0);
}
#endif

View File

@ -0,0 +1,24 @@
#pragma once
#include "esp_system.h"
#include "nvs.h"
#if defined(CONFIG_K8200_PST_SEL_UART_0)
#define K8200_PST_SEL_UART 0
#elif defined(CONFIG_K8200_PST_SEL_UART_1)
#define K8200_PST_SEL_UART 1
#elif defined(CONFIG_K8200_PST_SEL_UART_2)
#define K8200_PST_SEL_UART 2
#endif
#ifndef CONFIG_K8200_PST_SEL_NMI_ACT_HIGH
#define CONFIG_K8200_PST_SEL_NMI_ACT_HIGH 0
#endif
esp_err_t display_init(nvs_handle_t* nvsHandle, uint8_t* display_framebuf_mask, uint16_t* display_num_units);
void k8200_pst_set_nmi(uint8_t state);
void k8200_pst_reset(void);
void k8200_pst_home(void);
void getCommandBytes_SetCode(uint8_t address, uint8_t code, uint8_t* outBuf);
void display_render_frame(uint8_t* frame, uint8_t* prevFrame, uint16_t frameBufSize, uint8_t* display_framebuf_mask, uint16_t display_num_units);

View File

@ -43,7 +43,7 @@ esp_err_t display_init(nvs_handle_t* nvsHandle, uint8_t* display_framebuf_mask,
if (CONFIG_K9000_SEL_TX_IO >= 0) gpio_reset_pin(CONFIG_K9000_SEL_TX_IO);
if (CONFIG_K9000_SEL_RX_IO >= 0) gpio_reset_pin(CONFIG_K9000_SEL_RX_IO);
if (CONFIG_K9000_SEL_TX_IO >= 0) gpio_set_direction(CONFIG_K9000_SEL_TX_IO, GPIO_MODE_OUTPUT);
if (CONFIG_K9000_SEL_RX_IO >= 0) gpio_set_direction(CONFIG_K9000_SEL_RX_IO, GPIO_MODE_OUTPUT);
if (CONFIG_K9000_SEL_RX_IO >= 0) gpio_set_direction(CONFIG_K9000_SEL_RX_IO, GPIO_MODE_INPUT);
ret = uart_driver_install(K9000_SEL_UART, CONFIG_K9000_SEL_RX_BUF_SIZE, CONFIG_K9000_SEL_TX_BUF_SIZE, 0, NULL, 0);
if (ret != ESP_OK) return ret;

View File

@ -309,6 +309,13 @@
input = $("<select>");
input.change(updateDisplay);
let map = config["maps"][unit["map"]];
// Add home position
let option = $("<option>");
option.attr("value", unit["home_pos"]);
option.html("&lt;Home&gt;");
input.append(option);
for (const [key, value] of Object.entries(map)) {
let option = $("<option>");
option.attr("value", key);

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS remote_poll.c
INCLUDE_DIRS include
REQUIRES esp_http_client json nvs_flash util mbedtls)

View File

@ -0,0 +1,70 @@
menu "Remote Poll Configuration"
config TG_BOT_POLLING_TIMEOUT
int "Polling timeout in seconds"
default 60
help
Telegram Bot API polling timeout
config TG_BOT_MESSAGE_LIMIT
int "Max messages to fetch"
default 10
help
Maximum number of messages to fetch at once from the API
config TG_BOT_FORCE_UPPERCASE
bool "Force uppercase characters"
default false
choice TG_BOT_CHARSET_METHOD
bool "Charset Method"
default TG_BOT_CHARSET_METHOD_ALLOWED_CHARS_STR
config TG_BOT_CHARSET_METHOD_ALLOWED_CHARS_STR
bool "Allowed Characters String"
config TG_BOT_CHARSET_METHOD_DISALLOWED_CHARS_STR
bool "Disallowed Characters String"
config TG_BOT_CHARSET_METHOD_ALLOWED_CHARS_RANGE
bool "Allowed Characters Range"
config TG_BOT_CHARSET_METHOD_DISALLOWED_CHARS_RANGE
bool "Disallowed Characters Range"
endchoice
config TG_BOT_ALLOWED_CHARACTERS_STR
depends on TG_BOT_CHARSET_METHOD_ALLOWED_CHARS_STR
string "Allowed Characters String"
default ""
help
All the characters that are allowed for display
config TG_BOT_DISALLOWED_CHARACTERS_STR
depends on TG_BOT_CHARSET_METHOD_DISALLOWED_CHARS_STR
string "Disallowed Characters String"
default ""
help
All the characters that are allowed for display
config TG_BOT_ALLOWED_CHARACTERS_RANGE_MIN
depends on TG_BOT_CHARSET_METHOD_ALLOWED_CHARS_RANGE
int "Allowed Characters Range Min"
default 32
config TG_BOT_ALLOWED_CHARACTERS_RANGE_MAX
depends on TG_BOT_CHARSET_METHOD_ALLOWED_CHARS_RANGE
int "Allowed Characters Range Max"
default 127
config TG_BOT_DISALLOWED_CHARACTERS_RANGE_MIN
depends on TG_BOT_CHARSET_METHOD_DISALLOWED_CHARS_RANGE
int "Disallowed Characters Range Min"
default 32
config TG_BOT_DISALLOWED_CHARACTERS_RANGE_MAX
depends on TG_BOT_CHARSET_METHOD_DISALLOWED_CHARS_RANGE
int "Disallowed Characters Range Max"
default 127
endmenu

View File

@ -0,0 +1,19 @@
#pragma once
#include "esp_http_client.h"
#include "nvs.h"
#include "cJSON.h"
typedef struct {
uint8_t* buffer;
uint16_t duration;
} rp_buffer_list_entry_t;
esp_err_t remote_poll_http_event_handler(esp_http_client_event_t *evt);
void remote_poll_init(nvs_handle_t* nvsHandle, uint8_t* outBuf, size_t bufSize);
void remote_poll_deinit();
void remote_poll_task(void* arg);
void remote_poll_send_request();
esp_err_t remote_poll_process_response(cJSON* json);

View File

@ -0,0 +1,312 @@
#include "esp_log.h"
#include "esp_netif.h"
#include <string.h>
#include "sys/param.h"
#include "esp_http_client.h"
#include "mbedtls/base64.h"
#include "remote_poll.h"
#include "macros.h"
#include "util_buffer.h"
#include "util_generic.h"
#include "util_nvs.h"
#define LOG_TAG "Poll"
static TaskHandle_t rp_task_handle;
nvs_handle_t rp_nvs_handle;
char* pollUrl = NULL;
char* pollToken = NULL;
uint16_t pollInterval = 0;
uint8_t pollUrlInited = 0;
uint8_t pollTokenInited = 0;
uint32_t last_update_id = 0;
char* err_desc = NULL;
// Dynamic array holding the current list of buffers
rp_buffer_list_entry_t* rp_buffers = NULL;
uint8_t rp_num_buffers = 0;
uint8_t rp_cur_buffer = 0;
// Last switch / update times
uint64_t rp_last_switch = 0;
uint64_t rp_last_update = 0;
uint8_t* output_buffer;
size_t output_buffer_size = 0;
extern uint8_t wifi_gotIP;
esp_err_t remote_poll_http_event_handler(esp_http_client_event_t *evt) {
static char *resp_buf;
static int resp_len;
switch(evt->event_id) {
case HTTP_EVENT_ERROR: {
ESP_LOGD(LOG_TAG, "HTTP_EVENT_ERROR");
break;
}
case HTTP_EVENT_ON_CONNECTED: {
ESP_LOGD(LOG_TAG, "HTTP_EVENT_ON_CONNECTED");
break;
}
case HTTP_EVENT_HEADER_SENT: {
ESP_LOGD(LOG_TAG, "HTTP_EVENT_HEADER_SENT");
break;
}
case HTTP_EVENT_ON_HEADER: {
ESP_LOGD(LOG_TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
}
case HTTP_EVENT_ON_DATA: {
ESP_LOGD(LOG_TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
/*
* Check for chunked encoding is added as the URL for chunked encoding used in this example returns binary data.
* However, event handler can also be used in case chunked encoding is used.
*/
if (!esp_http_client_is_chunked_response(evt->client)) {
if (resp_buf == NULL) {
resp_buf = (char *) malloc(esp_http_client_get_content_length(evt->client));
resp_len = 0;
if (resp_buf == NULL) {
ESP_LOGE(LOG_TAG, "Failed to allocate memory for output buffer");
return ESP_FAIL;
}
}
memcpy(resp_buf + resp_len, evt->data, evt->data_len);
resp_len += evt->data_len;
}
break;
}
case HTTP_EVENT_ON_FINISH: {
cJSON* json;
ESP_LOGD(LOG_TAG, "HTTP_EVENT_ON_FINISH");
if (resp_buf != NULL) {
json = cJSON_Parse(resp_buf);
free(resp_buf);
resp_buf = NULL;
} else {
return ESP_FAIL;
}
resp_len = 0;
esp_err_t ret = remote_poll_process_response(json);
if (ret != ESP_OK) {
memset(output_buffer, 0x00, output_buffer_size);
if (err_desc == NULL) {
ESP_LOGE(LOG_TAG, "Error");
// sprintf((char*)output_buffer, "REMOTE POLL API FAIL");
} else {
ESP_LOGE(LOG_TAG, "Error: %s", err_desc);
// strncpy((char*)output_buffer, err_desc, output_buffer_size);
err_desc = NULL;
}
return ret;
}
cJSON_Delete(json);
break;
}
case HTTP_EVENT_DISCONNECTED: {
ESP_LOGI(LOG_TAG, "HTTP_EVENT_DISCONNECTED");
if (resp_buf != NULL) {
free(resp_buf);
resp_buf = NULL;
}
resp_len = 0;
break;
}
}
return ESP_OK;
}
void remote_poll_init(nvs_handle_t* nvsHandle, uint8_t* outBuf, size_t bufSize) {
ESP_LOGI(LOG_TAG, "Initializing remote poll");
rp_nvs_handle = *nvsHandle;
output_buffer = outBuf;
output_buffer_size = bufSize;
remote_poll_deinit();
esp_err_t ret = nvs_get_u16(*nvsHandle, "poll_interval", &pollInterval);
if (ret != ESP_OK) pollInterval = 0;
pollUrl = get_string_from_nvs(nvsHandle, "poll_url");
if (pollUrl != NULL) pollUrlInited = 1;
pollToken = get_string_from_nvs(nvsHandle, "poll_token");
if (pollToken != NULL) pollTokenInited = 1;
if (pollInterval != 0 && pollUrl != NULL && pollToken != NULL) {
if (strlen(pollUrl) != 0 && strlen(pollToken) != 0) {
ESP_LOGI(LOG_TAG, "Starting remote poll task");
xTaskCreatePinnedToCore(remote_poll_task, "remote_poll", 4096, NULL, 5, &rp_task_handle, 0);
}
}
}
void remote_poll_deinit() {
// Free allocated memory
if (pollUrlInited) {
free(pollUrl);
pollUrl = NULL;
pollUrlInited = 0;
}
if (pollTokenInited) {
free(pollToken);
pollToken = NULL;
pollTokenInited = 0;
}
}
void remote_poll_task(void* arg) {
while (1) {
uint64_t now = esp_timer_get_time(); // Microseconds!
// Switch buffer if necessary
if (rp_num_buffers > 0) {
if (rp_cur_buffer >= rp_num_buffers) rp_cur_buffer = 0; // In case rp_num_buffers got smaller
if (rp_last_switch == 0 || now - rp_last_switch >= rp_buffers[rp_cur_buffer].duration * 1000000) {
rp_last_switch = now;
rp_cur_buffer++;
if (rp_cur_buffer >= rp_num_buffers) rp_cur_buffer = 0;
ESP_LOGI(LOG_TAG, "Switching to buffer %d", rp_cur_buffer);
memcpy(output_buffer, rp_buffers[rp_cur_buffer].buffer, output_buffer_size);
}
}
// Update if necessary
if (rp_last_update == 0 || now - rp_last_update >= pollInterval * 1000000) {
rp_last_update = now;
if (wifi_gotIP) remote_poll_send_request();
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void remote_poll_send_request() {
esp_http_client_config_t config = {
.event_handler = remote_poll_http_event_handler,
.disable_auto_redirect = false,
.url = pollUrl
};
cJSON* json;
char* post_data;
esp_http_client_handle_t client = esp_http_client_init(&config);
json = cJSON_CreateObject();
cJSON_AddStringToObject(json, "token", pollToken);
post_data = cJSON_Print(json);
ESP_LOGV(LOG_TAG, "POST Data: %s", post_data);
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_post_field(client, post_data, strlen(post_data));
esp_err_t err = esp_http_client_perform(client);
cJSON_Delete(json);
cJSON_free(post_data);
if (err == ESP_OK) {
ESP_LOGI(LOG_TAG, "HTTP GET Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(LOG_TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
// sprintf((char*)output_buffer, "GET FAILED %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
esp_err_t remote_poll_process_response(cJSON* json) {
/*
Expected JSON schema:
{
"buffers": [
{
"duration": <duration in seconds>,
"buffer": <base64-encoded buffer>
},
{
"duration": <duration in seconds>,
"buffer": <base64-encoded buffer>
}
]
}
On error:
{
"error": "Some error here"
}
*/
ESP_LOGD(LOG_TAG, "Processing response");
if (!cJSON_IsObject(json)) return ESP_FAIL;
cJSON* field_error = cJSON_GetObjectItem(json, "error");
if (field_error != NULL) {
char* error_str = cJSON_GetStringValue(field_error);
ESP_LOGE(LOG_TAG, "Poll API Error: %s", error_str);
return ESP_FAIL;
}
cJSON* buffers_arr = cJSON_GetObjectItem(json, "buffers");
if (!cJSON_IsArray(buffers_arr)) return ESP_FAIL;
uint16_t numBuffers = cJSON_GetArraySize(buffers_arr);
if (numBuffers > 255) {
ESP_LOGE(LOG_TAG, "Got more than 255 buffers, aborting");
return ESP_FAIL;
}
// Free individual buffer arrays and the overall array
for (uint8_t i = 0; i < rp_num_buffers; i++) {
free(rp_buffers[i].buffer);
}
free(rp_buffers);
// Allocate new buffers
ESP_LOGD(LOG_TAG, "Allocating %d buffers", numBuffers);
rp_num_buffers = numBuffers;
rp_buffers = malloc(rp_num_buffers * sizeof(rp_buffer_list_entry_t));
memset(rp_buffers, 0x00, rp_num_buffers * sizeof(rp_buffer_list_entry_t));
for (uint16_t i = 0; i < numBuffers; i++) {
rp_buffers[i].buffer = malloc(output_buffer_size);
}
// Populate new buffers
for (uint8_t i = 0; i < rp_num_buffers; i++) {
size_t b64_len = 0;
cJSON* item = cJSON_GetArrayItem(buffers_arr, i);
cJSON* duration_field = cJSON_GetObjectItem(item, "duration");
rp_buffers[i].duration = cJSON_GetNumberValue(duration_field);
cJSON* buffer_field = cJSON_GetObjectItem(item, "buffer");
char* buffer_str = cJSON_GetStringValue(buffer_field);
size_t buffer_str_len = strlen(buffer_str);
unsigned char* buffer_str_uchar = (unsigned char*)buffer_str;
int result = mbedtls_base64_decode(NULL, 0, &b64_len, buffer_str_uchar, buffer_str_len);
if (result == MBEDTLS_ERR_BASE64_INVALID_CHARACTER) {
// We don't cover MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL here
// because this will always be returned when checking size
return ESP_FAIL;
} else {
b64_len = 0;
result = mbedtls_base64_decode(rp_buffers[i].buffer, output_buffer_size, &b64_len, buffer_str_uchar, buffer_str_len);
if (result != 0) return ESP_FAIL;
}
}
return ESP_OK;
}

View File

@ -126,10 +126,8 @@ void telegram_bot_init(nvs_handle_t* nvsHandle, uint8_t* outBuf, size_t bufSize)
output_buffer = outBuf;
output_buffer_size = bufSize;
if (apiTokenInited) {
// Free allocated memory
free(apiToken);
}
telegram_bot_deinit();
apiToken = get_string_from_nvs(nvsHandle, "tg_bot_token");
if (apiToken != NULL) {
apiTokenInited = 1;
@ -140,9 +138,11 @@ void telegram_bot_init(nvs_handle_t* nvsHandle, uint8_t* outBuf, size_t bufSize)
}
void telegram_bot_deinit() {
// Free allocated memory
if (apiTokenInited) {
// Free allocated memory
free(apiToken);
apiToken = NULL;
apiTokenInited = 0;
}
}

View File

@ -16,4 +16,6 @@ typedef enum {
void buffer_8to1(uint8_t* buf8, uint8_t* buf1, uint16_t width, uint16_t height, buf_merge_t mergeType);
void buffer_utf8_to_iso88591(char* dst, char* src);
void buffer_iso88591_to_utf8(char* dst, char* src);
void buffer_textbuf_to_charbuf(uint8_t* display_text_buffer, uint8_t* display_char_buffer, uint16_t* display_quirk_flags_buffer, uint16_t textBufSize, uint16_t charBufSize);
#if defined(CONFIG_DISPLAY_TYPE_CHARACTER)
void buffer_textbuf_to_charbuf(uint8_t* display_text_buffer, uint8_t* display_char_buffer, uint16_t* display_quirk_flags_buffer, uint16_t textBufSize, uint16_t charBufSize);
#endif

View File

@ -39,6 +39,7 @@ typedef struct {
uint8_t count_set_bits(uint8_t byte);
uint8_t int_num_digits(int64_t n, uint8_t includeNegSign);
uint8_t uint_num_digits(uint64_t n);
uint8_t uint8_to_bcd(uint8_t n);
void str_toUpper(char* str);
void str_filterAllowed(char* out, char* in, char* allowedChars, bool allowLineBreaks);
void str_filterDisallowed(char* out, char* in, char* disallowedChars, bool allowLineBreaks);

View File

@ -60,6 +60,7 @@ void buffer_iso88591_to_utf8(char* dst, char* src) {
}
}
#if defined(CONFIG_DISPLAY_TYPE_CHARACTER)
void buffer_textbuf_to_charbuf(uint8_t* display_text_buffer, uint8_t* display_char_buffer, uint16_t* display_quirk_flags_buffer, uint16_t textBufSize, uint16_t charBufSize) {
/*
Convert a text buffer to a character buffer and a quirk flag buffer.
@ -135,4 +136,5 @@ void buffer_textbuf_to_charbuf(uint8_t* display_text_buffer, uint8_t* display_ch
}
}
}
}
}
#endif

View File

@ -35,6 +35,16 @@ uint8_t uint_num_digits(uint64_t n) {
return result;
}
uint8_t uint8_to_bcd(uint8_t n) {
// Turn a positive integer between in the range [0, 99]
// into its hexadecimal BCD representation.
// e.g. 37 => 0x37
uint8_t tens = n / 10;
if (tens > 9) return 0;
uint8_t ones = n % 10;
return tens * 16 + ones;
}
void str_toUpper(char* str) {
while (*str) {
*str = toupper((unsigned char) *str);

View File

@ -122,6 +122,8 @@ DISPLAY_TEXTBUF_SIZE: Number of characters in the user-facing text buffe
#define DISPLAY_DRIVER "char_16seg_led_ws281x"
#elif defined(CONFIG_DISPLAY_DRIVER_SEL_KRONE_9000)
#define DISPLAY_DRIVER "sel_krone_9000"
#elif defined(CONFIG_DISPLAY_DRIVER_SEL_KRONE_8200_PST)
#define DISPLAY_DRIVER "sel_krone_8200_pst"
#endif
#if defined(CONFIG_DISPLAY_HAS_SHADERS)

View File

@ -38,4 +38,6 @@ build_flags =
[env:16segrgb]
[env:splitflap_k9000]
[env:splitflap_k9000]
[env:splitflap_k8200_pst]

File diff suppressed because it is too large Load Diff

View File

@ -167,6 +167,10 @@ choice DISPLAY_DRIVER
config DISPLAY_DRIVER_SEL_KRONE_9000
depends on DISPLAY_TYPE_SELECTION
bool "KRONE 9000 Split-Flap (using UART peripheral)"
config DISPLAY_DRIVER_SEL_KRONE_8200_PST
depends on DISPLAY_TYPE_SELECTION
bool "KRONE 8200 Split-Flap, PST (using UART peripheral)"
endchoice
config DISPLAY_UNIT_BUF_SIZE

View File

@ -20,6 +20,7 @@
#include "httpd.h"
#include "logging_tcp.h"
#include "macros.h"
#include "remote_poll.h"
#include "telegram_bot.h"
#include "tpm2net.h"
#include "ethernet.h"
@ -54,6 +55,8 @@
#include "driver_display_char_16seg_led_ws281x.h"
#elif defined(CONFIG_DISPLAY_DRIVER_SEL_KRONE_9000)
#include "driver_display_sel_krone_9000.h"
#elif defined(CONFIG_DISPLAY_DRIVER_SEL_KRONE_8200_PST)
#include "driver_display_sel_krone_8200_pst.h"
#endif
#if defined(CONFIG_DISPLAY_TYPE_PIXEL)
@ -282,15 +285,18 @@ void app_main(void) {
tpm2net_init(display_output_buffer, tpm2net_output_buffer, DISPLAY_FRAMEBUF_SIZE, TPM2NET_FRAMEBUF_SIZE);
artnet_init(display_output_buffer, artnet_output_buffer, DISPLAY_FRAMEBUF_SIZE, ARTNET_FRAMEBUF_SIZE);
browser_canvas_init(&server, display_output_buffer, DISPLAY_FRAMEBUF_SIZE);
remote_poll_init(&nvs_handle, display_output_buffer, DISPLAY_FRAMEBUF_SIZE);
#endif
#if defined(CONFIG_DISPLAY_TYPE_CHARACTER)
browser_canvas_init(&server, display_text_buffer, DISPLAY_TEXTBUF_SIZE);
telegram_bot_init(&nvs_handle, display_text_buffer, DISPLAY_TEXTBUF_SIZE);
remote_poll_init(&nvs_handle, display_text_buffer, DISPLAY_TEXTBUF_SIZE);
#endif
#if defined(CONFIG_DISPLAY_TYPE_SELECTION)
browser_canvas_init(&server, display_output_buffer, DISPLAY_FRAMEBUF_SIZE);
remote_poll_init(&nvs_handle, display_output_buffer, DISPLAY_FRAMEBUF_SIZE);
#endif
#if defined(CONFIG_DISPLAY_HAS_BRIGHTNESS_CONTROL)