Add remote poll input
This commit is contained in:
parent
d41e22aa96
commit
70b7e09789
|
@ -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 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -274,7 +274,8 @@ CONFIG_ESP_TLS_USING_MBEDTLS=y
|
|||
# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set
|
||||
# CONFIG_ESP_TLS_SERVER is not set
|
||||
# CONFIG_ESP_TLS_PSK_VERIFICATION is not set
|
||||
# CONFIG_ESP_TLS_INSECURE is not set
|
||||
CONFIG_ESP_TLS_INSECURE=y
|
||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||
# end of ESP-TLS
|
||||
|
||||
#
|
||||
|
@ -1328,7 +1329,7 @@ CONFIG_ARTNET_FRAME_TYPE_24BPP=y
|
|||
# end of ArtNet Receiver
|
||||
|
||||
#
|
||||
# Telegram Bot Configuration
|
||||
# Remote Poll Configuration
|
||||
#
|
||||
CONFIG_TG_BOT_POLLING_TIMEOUT=60
|
||||
CONFIG_TG_BOT_MESSAGE_LIMIT=10
|
||||
|
@ -1338,6 +1339,11 @@ CONFIG_TG_BOT_CHARSET_METHOD_ALLOWED_CHARS_STR=y
|
|||
# CONFIG_TG_BOT_CHARSET_METHOD_ALLOWED_CHARS_RANGE is not set
|
||||
# CONFIG_TG_BOT_CHARSET_METHOD_DISALLOWED_CHARS_RANGE is not set
|
||||
CONFIG_TG_BOT_ALLOWED_CHARACTERS_STR=""
|
||||
# end of Remote Poll Configuration
|
||||
|
||||
#
|
||||
# Telegram Bot Configuration
|
||||
#
|
||||
# end of Telegram Bot Configuration
|
||||
|
||||
#
|
||||
|
|
|
@ -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"
|
||||
|
@ -284,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