Compare commits
4 Commits
5c0c895c16
...
70b7e09789
Author | SHA1 | Date |
---|---|---|
Julian Metzler | 70b7e09789 | |
Julian Metzler | d41e22aa96 | |
Julian Metzler | 818ad88915 | |
Julian Metzler | 72d349401b |
|
@ -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},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
idf_component_register(SRCS sel_k8200_pst.c
|
||||
INCLUDE_DIRS include
|
||||
REQUIRES util)
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
|
@ -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("<Home>");
|
||||
input.append(option);
|
||||
|
||||
for (const [key, value] of Object.entries(map)) {
|
||||
let option = $("<option>");
|
||||
option.attr("value", key);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
idf_component_register(SRCS remote_poll.c
|
||||
INCLUDE_DIRS include
|
||||
REQUIRES esp_http_client json nvs_flash util mbedtls)
|
|
@ -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
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue