initial version made by removing all csp stuff from cspemu
This commit is contained in:
+46
-2
@@ -1,2 +1,46 @@
|
||||
idf_component_register(SRCS "hello_world_main.c"
|
||||
INCLUDE_DIRS "")
|
||||
idf_component_register(SRCS
|
||||
irblaster_main.c
|
||||
settings.c
|
||||
shutdown_handlers.c
|
||||
sntp_cli.c
|
||||
utils.c
|
||||
wifi_conn.c
|
||||
console/console_ioimpl.c
|
||||
console/console_server.c
|
||||
console/register_cmds.c
|
||||
console/telnet_parser.c
|
||||
web/websrv.c
|
||||
console/commands/cmd_dump.c
|
||||
console/commands/cmd_factory_reset.c
|
||||
console/commands/cmd_heap.c
|
||||
console/commands/cmd_ip.c
|
||||
console/commands/cmd_restart.c
|
||||
console/commands/cmd_tasks.c
|
||||
console/commands/cmd_version.c
|
||||
console/commands/cmd_wifi.c
|
||||
console/commands/cmd_pw.c
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
find_package(Git REQUIRED)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE _commit_hash
|
||||
)
|
||||
# TODO what's this?
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE _revision_number
|
||||
)
|
||||
string(REGEX REPLACE "\n" "" _commit_hash "${_commit_hash}")
|
||||
string(REGEX REPLACE "\n" "" _revision_number "${_revision_number}")
|
||||
string(TIMESTAMP _build_time_stamp)
|
||||
|
||||
configure_file(
|
||||
"gitversion.h.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/config/gitversion.h"
|
||||
)
|
||||
|
||||
include_directories("${CMAKE_CURRENT_BINARY_DIR}/config")
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
menu "IRBLASTER Configuration"
|
||||
|
||||
menu "Pin mapping"
|
||||
config PIN_I2C_SDA0
|
||||
int "I2C0 SDA"
|
||||
default 18
|
||||
|
||||
config PIN_I2C_SCL0
|
||||
int "I2C0 SCL"
|
||||
default 19
|
||||
|
||||
config PIN_IRLED
|
||||
int "IR LED"
|
||||
default 17
|
||||
endmenu
|
||||
|
||||
menu "Console"
|
||||
config CONSOLE_TELNET_PORT
|
||||
int "Integrated telnet server listening port"
|
||||
default 23
|
||||
|
||||
config CONSOLE_PW_LEN
|
||||
int "Console max pw len"
|
||||
default 32
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Globals
|
||||
*/
|
||||
|
||||
#ifndef _APPLICATION_H
|
||||
#define _APPLICATION_H
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "settings.h"
|
||||
#include "gitversion.h"
|
||||
|
||||
#define EG_WIFI_CONNECTED_BIT BIT0
|
||||
extern EventGroupHandle_t g_wifi_event_group;
|
||||
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
|
||||
esp_err_t cspemu_add_shutdown_handler(shutdown_handler_t handler);
|
||||
void cspemu_run_shutdown_handlers(void);
|
||||
|
||||
#define APP_NAME "IRBLASTER"
|
||||
#define APP_VERSION "v1"
|
||||
|
||||
#endif //_APPLICATION_H
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/02.
|
||||
//
|
||||
|
||||
#ifndef CSPEMU_CMD_COMMON_H
|
||||
#define CSPEMU_CMD_COMMON_H
|
||||
|
||||
#include "console_server.h"
|
||||
|
||||
//// prototypes (TODO remove..)
|
||||
//void csp_vprintf(const char *format, va_list args);
|
||||
//void csp_printf(const char *format, ...);
|
||||
|
||||
#define EOL "\r\n"
|
||||
|
||||
#define console_printf_e(format, ...) console_printf("\x1b[31m" format "\x1b[m", ##__VA_ARGS__)
|
||||
#define console_printf_w(format, ...) console_printf("\x1b[33m" format "\x1b[m", ##__VA_ARGS__)
|
||||
#define console_fputs(str) console_print(str)
|
||||
#define console_fputsn(str, len) console_write(str, len)
|
||||
#define console_fputc(c) console_write(&c, 1)
|
||||
|
||||
#define MSG_ON "\x1b[32mON\x1b[m"
|
||||
#define MSG_ENABLED "\x1b[32mENABLED\x1b[m"
|
||||
#define MSG_OFF "\x1b[31mOFF\x1b[m"
|
||||
#define MSG_DISABLED "\x1b[31mDISABLED\x1b[m"
|
||||
|
||||
//#define ARG_OPTIONAL_NODE_ID() cmd_args.node->count ? cmd_args.node->ival[0] : csp_get_address()
|
||||
|
||||
#if 0
|
||||
struct cmd_no_args_s {
|
||||
struct arg_end *end;
|
||||
};
|
||||
|
||||
extern struct cmd_no_args_s cmd_no_args;
|
||||
|
||||
#define CMD_CHECK_ARGS(struct_var) do { \
|
||||
int nerrors = arg_parse(argc, argv, (void**) &struct_var); \
|
||||
if (nerrors != 0) { \
|
||||
console_print_errors(struct_var.end, argv[0]); \
|
||||
return 1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/** Report & return error if any args were given to this command */
|
||||
#define CMD_CHECK_NO_ARGS() CMD_CHECK_ARGS(cmd_no_args)
|
||||
#endif
|
||||
|
||||
#endif //CSPEMU_CMD_COMMON_H
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/08.
|
||||
//
|
||||
#include "settings.h"
|
||||
#include "console/cmd_common.h"
|
||||
#include <console/cmddef.h>
|
||||
|
||||
|
||||
static int cmd_dump(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->command = "dump";
|
||||
reg->help = "Dump node info";
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_printf("WiFi enabled: %d\r\n", g_Settings.wifi_enabled);
|
||||
|
||||
// TODO show more settings
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cmd_dump(void)
|
||||
{
|
||||
console_cmd_register(cmd_dump, "dump");
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/08.
|
||||
//
|
||||
|
||||
#include <string.h>
|
||||
#include <linenoise/linenoise.h>
|
||||
#include <settings.h>
|
||||
#include <utils.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#include "console/cmd_common.h"
|
||||
#include <console/cmddef.h>
|
||||
|
||||
|
||||
static int cmd_factory_reset(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_str *magic;
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.magic = arg_str1(NULL, NULL, "<passphrase>", "Passphrase to prevent accidental erase. Must be 'confirm'");
|
||||
cmd_args.end = arg_end(2);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->command = "factory_reset";
|
||||
reg->help = "Wipe non-volatile data memory, restoring everything to defaults.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(cmd_args.magic->sval[0], "confirm")) {
|
||||
nvs_flash_erase();
|
||||
|
||||
console_printf("Non-volatile memory erased.\r\n"
|
||||
"Restarting to apply changes...\r\n\r\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
esp_restart();
|
||||
} else {
|
||||
console_printf("Incorrect passphrase.\r\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cmd_factory_reset(void)
|
||||
{
|
||||
console_cmd_register(cmd_factory_reset, "factory_reset");
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/08.
|
||||
//
|
||||
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_system.h>
|
||||
|
||||
#include "console/cmd_common.h"
|
||||
#include <console/cmddef.h>
|
||||
|
||||
|
||||
/* 'heap' command prints minumum heap size */
|
||||
static int cmd_heap(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->command = "heap";
|
||||
reg->help = "Get emulator heap usage stats (for debug)";
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
|
||||
uint32_t current_free = esp_get_free_heap_size();
|
||||
console_printf("free heap: %u bytes\r\n"
|
||||
" lowest: %u bytes\r\n", current_free, heap_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cmd_heap(void)
|
||||
{
|
||||
console_cmd_register(cmd_heap, "heap");
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <settings.h>
|
||||
#include <lwip/inet.h>
|
||||
|
||||
#include "console/cmd_common.h"
|
||||
#include <console/cmddef.h>
|
||||
#include <application.h>
|
||||
#include <common_utils/utils.h>
|
||||
#include <sntp_cli.h>
|
||||
#include <ping.h>
|
||||
#include <lwip/netdb.h>
|
||||
|
||||
static int cmd_ip_status(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
EMPTY_CMD_SETUP("Show IP status");
|
||||
|
||||
if (!g_Settings.wifi_enabled || !g_State.wifi_inited) {
|
||||
console_color_printf(COLOR_RED, "WiFi interface is disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wifi_config_t config;
|
||||
if (ESP_OK == esp_wifi_get_config(ESP_IF_WIFI_STA, &config)) {
|
||||
if (config.sta.ssid[0]) {
|
||||
EventBits_t bits = xEventGroupGetBits(g_wifi_event_group);
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
console_color_printf(COLOR_GREEN, "Connected to SSID \"%s\"\n", config.sta.ssid);
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
console_color_printf(COLOR_RED, "WiFi connection failed.\n");
|
||||
console_printf("Saved SSID = \"%s\"\n", config.sta.ssid);
|
||||
return 0;
|
||||
} else {
|
||||
console_color_printf(COLOR_RED, "Not connected, retries may be in progress.\n");
|
||||
console_printf("Saved SSID = \"%s\"\n", config.sta.ssid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tcpip_adapter_ip_info_t ipinfo;
|
||||
if (ESP_OK == tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipinfo)) {
|
||||
// ! inet_ntoa uses a global static buffer, cant use multiple in one printf call
|
||||
console_printf("DHCP: %s\n", g_Settings.dhcp_enable ? MSG_ENABLED : MSG_DISABLED " (static IP)");
|
||||
console_printf("IP: %s\n", inet_ntoa(ipinfo.ip.addr));
|
||||
console_printf("Mask: %s\n", inet_ntoa(ipinfo.netmask.addr));
|
||||
console_printf("Gateway: %s\n", inet_ntoa(ipinfo.gw.addr));
|
||||
|
||||
if (!g_Settings.dhcp_enable) {
|
||||
console_printf("DNS: %s\n", inet_ntoa(g_Settings.static_dns));
|
||||
}
|
||||
|
||||
uint8_t mac[6];
|
||||
if (ESP_OK == esp_wifi_get_mac(ESP_IF_WIFI_STA, mac)) {
|
||||
console_printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
} else {
|
||||
console_color_printf(COLOR_RED, "No IP address assigned!\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
console_color_printf(COLOR_RED, "SSID not configured!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_ip_pingwd(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_str *cmd;
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.cmd = arg_str0(NULL, NULL, "<cmd>", "Enable or disable. Omit to check current state");
|
||||
cmd_args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->help = "Enable/disable ping watchdog, or check the current setting. The watchdog periodically pings the gateway and restarts WiFi on failure.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd_args.cmd->count) {
|
||||
int b = parse_boolean_arg(cmd_args.cmd->sval[0]);
|
||||
if (b < 0) return CONSOLE_ERR_INVALID_ARG;
|
||||
g_Settings.dhcp_wd_enable = b;
|
||||
settings_persist(SETTINGS_dhcp_wd_enable);
|
||||
}
|
||||
|
||||
console_printf("Ping WD = %s\n", g_Settings.dhcp_wd_enable? MSG_ENABLED : MSG_DISABLED);
|
||||
|
||||
if (cmd_args.cmd->count) {
|
||||
console_printf("Restart to apply changes\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ping_success_print_cb(int bytes, const char *ip, int seq, int elapsed_ms) {
|
||||
console_printf("Rx %d bytes from %s: icmp_seq=%d time=%d ms\n", bytes, ip, seq, (int) elapsed_ms);
|
||||
}
|
||||
|
||||
static void ping_fail_print_cb(int seq) {
|
||||
console_printf("Request timeout for icmp_seq %d\n", seq);
|
||||
}
|
||||
|
||||
static int cmd_ip_ping(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_str *ip;
|
||||
struct arg_int *num;
|
||||
struct arg_int *timeout;
|
||||
struct arg_int *interval;
|
||||
struct arg_int *size;
|
||||
struct arg_end *end;
|
||||
} args;
|
||||
|
||||
if (reg) {
|
||||
args.ip = arg_str1(NULL, NULL, "<IP>", "Target IP");
|
||||
args.num = arg_int0("n", NULL, "<num>", "Ping count (def 1)");
|
||||
args.timeout = arg_int0("t", NULL, "<ms>", "Timeout (def 3000)");
|
||||
args.interval = arg_int0("i", NULL, "<ms>", "Interval (def 1000)");
|
||||
args.size = arg_int0("s", NULL, "<bytes>", "Payload size (def 32)");
|
||||
args.end = arg_end(5);
|
||||
|
||||
reg->argtable = &args;
|
||||
reg->help = "Ping a host using ICMP";
|
||||
return 0;
|
||||
}
|
||||
|
||||
ping_opts_t opts = PING_CONFIG_DEFAULT();
|
||||
opts.success_cb = ping_success_print_cb;
|
||||
opts.fail_cb = ping_fail_print_cb;
|
||||
opts.count = args.num->count ? args.num->ival[0] : 1;
|
||||
opts.payload_size = args.size->count ? args.size->ival[0] : 32;
|
||||
opts.interval_ms = args.interval->count ? args.interval->ival[0] : 1000;
|
||||
opts.timeout_ms = args.timeout->count ? args.timeout->ival[0] : 3000;
|
||||
|
||||
if (0 == inet_aton(args.ip->sval[0], &opts.ip_addr)) {
|
||||
// we could have received a domain name here
|
||||
struct hostent * ent = gethostbyname(args.ip->sval[0]);
|
||||
if (!ent) {
|
||||
console_println("Could not resolve");
|
||||
return CONSOLE_ERR_IO;
|
||||
}
|
||||
|
||||
memcpy(&opts.ip_addr, ent->h_addr_list[0], sizeof(ip4_addr_t));
|
||||
console_printf("Resolved as %s\n", inet_ntoa(opts.ip_addr));
|
||||
}
|
||||
|
||||
ping_result_t result = {};
|
||||
esp_err_t ret = ping(&opts, &result);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
console_println("Ping error");
|
||||
return ret;
|
||||
} else {
|
||||
console_printf("%d tx, %d rx, %.1f%% loss, latency min %d ms, max %d ms\n",
|
||||
result.sent,
|
||||
result.received,
|
||||
result.loss_pt,
|
||||
result.min_time_ms,
|
||||
result.max_time_ms);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_ip_static_set(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_str *ip;
|
||||
struct arg_str *gw;
|
||||
struct arg_str *mask;
|
||||
struct arg_str *dns;
|
||||
struct arg_str *cmd;
|
||||
struct arg_end *end;
|
||||
} args;
|
||||
|
||||
if (reg) {
|
||||
args.ip = arg_str0("a", NULL, "<IP>", "Set IP address");
|
||||
args.gw = arg_str0("g", NULL, "<GW>", "Set gateway address");
|
||||
args.mask = arg_str0("n", NULL, "<MASK>", "Set netmask");
|
||||
args.dns = arg_str0("d", NULL, "<DNS>", "Set DNS server IP");
|
||||
args.cmd = arg_str0(NULL, NULL, "{enable|disable}", "Enable or disable static IP");
|
||||
args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &args;
|
||||
reg->help = "Configure, enable/disable, or check config of static IP.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool any_change = false;
|
||||
|
||||
if (args.ip->count) {
|
||||
uint32_t a = 0;
|
||||
if (!inet_aton(args.ip->sval[0], &a)) {
|
||||
console_println("Invalid IP");
|
||||
return CONSOLE_ERR_INVALID_ARG;
|
||||
}
|
||||
g_Settings.static_ip = a; // aton output is already in network byte order
|
||||
settings_persist(SETTINGS_static_ip);
|
||||
any_change = true;
|
||||
}
|
||||
|
||||
if (args.gw->count) {
|
||||
uint32_t a = 0;
|
||||
if (!inet_aton(args.gw->sval[0], &a)) {
|
||||
console_println("Invalid GW");
|
||||
return CONSOLE_ERR_INVALID_ARG;
|
||||
}
|
||||
g_Settings.static_ip_gw = a; // aton output is already in network byte order
|
||||
settings_persist(SETTINGS_static_ip_gw);
|
||||
any_change = true;
|
||||
}
|
||||
|
||||
if (args.mask->count) {
|
||||
uint32_t a = 0;
|
||||
if (!inet_aton(args.mask->sval[0], &a)) {
|
||||
console_println("Invalid mask");
|
||||
return CONSOLE_ERR_INVALID_ARG;
|
||||
}
|
||||
g_Settings.static_ip_mask = a; // aton output is already in network byte order
|
||||
settings_persist(SETTINGS_static_ip_mask);
|
||||
any_change = true;
|
||||
}
|
||||
|
||||
if (args.dns->count) {
|
||||
uint32_t a = 0;
|
||||
if (!inet_aton(args.dns->sval[0], &a)) {
|
||||
console_println("Invalid DNS IP");
|
||||
return CONSOLE_ERR_INVALID_ARG;
|
||||
}
|
||||
g_Settings.static_dns = a; // aton output is already in network byte order
|
||||
settings_persist(SETTINGS_static_dns);
|
||||
any_change = true;
|
||||
}
|
||||
|
||||
if (args.cmd->count) {
|
||||
int b = parse_boolean_arg(args.cmd->sval[0]);
|
||||
if (b < 0) return CONSOLE_ERR_INVALID_ARG;
|
||||
g_Settings.dhcp_enable = !b;
|
||||
settings_persist(SETTINGS_dhcp_enable);
|
||||
any_change = true;
|
||||
}
|
||||
|
||||
console_printf("Static IP: %s\n", g_Settings.dhcp_enable ? MSG_DISABLED : MSG_ENABLED);
|
||||
console_printf("- IP: %s\n", inet_ntoa(g_Settings.static_ip));
|
||||
console_printf("- Mask: %s\n", inet_ntoa(g_Settings.static_ip_mask));
|
||||
console_printf("- Gateway: %s\n", inet_ntoa(g_Settings.static_ip_gw));
|
||||
console_printf("- DNS: %s\n", inet_ntoa(g_Settings.static_dns));
|
||||
|
||||
if (any_change) {
|
||||
console_println("Restart to apply changes.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_ip_ntp(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_str *cmd;
|
||||
struct arg_str *addr;
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.cmd = arg_str0(NULL, NULL, "<cmd>", "Enable or disable autostart. Omit to check current state. start = start now");
|
||||
cmd_args.addr = arg_str0("s", NULL, "<server>", "Set NTP server");
|
||||
cmd_args.end = arg_end(2);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->help = "Check or modify NTP client setting, or start it manually.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd_args.addr->count) {
|
||||
strncpy(g_Settings.ntp_srv, cmd_args.addr->sval[0], NTP_SRV_LEN);
|
||||
g_Settings.ntp_srv[NTP_SRV_LEN-1] = 0;
|
||||
settings_persist(SETTINGS_ntp_srv);
|
||||
console_printf("NTP server set to %s\n", g_Settings.ntp_srv);
|
||||
}
|
||||
|
||||
if (cmd_args.cmd->count) {
|
||||
if (streq(cmd_args.cmd->sval[0], "start")) {
|
||||
bool started = sntp_cli_start();
|
||||
if (started) {
|
||||
console_printf("NTP client started manually.\n");
|
||||
} else {
|
||||
console_color_printf(COLOR_RED, "Start failed. Client may be already running.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int b = parse_boolean_arg(cmd_args.cmd->sval[0]);
|
||||
if (b < 0) return CONSOLE_ERR_INVALID_ARG;
|
||||
g_Settings.ntp_enable = b;
|
||||
|
||||
settings_persist(SETTINGS_ntp_enable);
|
||||
}
|
||||
|
||||
console_printf("Client status: %s\n", g_Settings.ntp_enable? MSG_ENABLED : MSG_DISABLED);
|
||||
console_printf("NTP server: %s\n", g_Settings.ntp_srv);
|
||||
|
||||
/* show the current date */
|
||||
time_t now;
|
||||
struct tm timeinfo;
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
|
||||
if(timeinfo.tm_year < (2016 - 1900)) {
|
||||
console_printf("Device time is not valid.\n");
|
||||
} else {
|
||||
char strftime_buf[64];
|
||||
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
|
||||
console_printf("The current UTC date/time is: %s\n", strftime_buf);
|
||||
}
|
||||
|
||||
if (cmd_args.cmd->count) {
|
||||
// if it was "start", we returned early.
|
||||
console_printf("Restart to apply changes\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cmd_ip(void)
|
||||
{
|
||||
console_group_add("ip", "IP status and settings");
|
||||
console_cmd_register(cmd_ip_status, "ip status");
|
||||
console_cmd_register(cmd_ip_pingwd, "ip wd");
|
||||
console_cmd_register(cmd_ip_ntp, "ip ntp");
|
||||
console_cmd_register(cmd_ip_ping, "ip ping");
|
||||
console_cmd_register(cmd_ip_static_set, "ip static");
|
||||
|
||||
// this may be used for shortcuts like "ip in"
|
||||
console_cmd_add_alias_fn(cmd_ip_status, "ip info");
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Set telnet pw
|
||||
//
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <settings.h>
|
||||
|
||||
#include "console/cmd_common.h"
|
||||
#include <console/cmddef.h>
|
||||
#include <application.h>
|
||||
#include <console/prefix_match.h>
|
||||
|
||||
static int cmd_clear(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
EMPTY_CMD_SETUP("Clear access password");
|
||||
console_printf("Access password cleared.\n");
|
||||
g_Settings.console_pw[0] = 0;
|
||||
settings_persist(SETTINGS_console_pw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_set(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_str *pw;
|
||||
struct arg_end *end;
|
||||
} args;
|
||||
|
||||
if (reg) {
|
||||
args.pw = arg_str1(NULL, NULL, "<password>", EXPENDABLE_STRING("New password"));
|
||||
args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &args;
|
||||
reg->help = EXPENDABLE_STRING("Set access password");
|
||||
return CONSOLE_OK;
|
||||
}
|
||||
|
||||
strncpy(g_Settings.console_pw, args.pw->sval[0], CONSOLE_PW_LEN);
|
||||
console_printf("Access pw set to: \"%.*s\"\n", CONSOLE_PW_LEN, args.pw->sval[0]);
|
||||
settings_persist(SETTINGS_console_pw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cmd_pw(void)
|
||||
{
|
||||
console_group_add("pw", "Access password");
|
||||
|
||||
console_cmd_register(cmd_set, "pw set");
|
||||
console_cmd_register(cmd_clear, "pw clear");
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/08.
|
||||
//
|
||||
|
||||
#include "console/cmd_common.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "application.h"
|
||||
#include <console/cmddef.h>
|
||||
|
||||
|
||||
/** 'restart' command restarts the program */
|
||||
static int cmd_restart(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->command = "restart";
|
||||
reg->help = "Restart the emulator";
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_printf("Restarting...\r\n");
|
||||
|
||||
// try to cleanly close all connections
|
||||
telnetsrv_kick_all();
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void register_cmd_restart(void)
|
||||
{
|
||||
console_cmd_register(cmd_restart, "restart");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/08.
|
||||
//
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "console/cmd_common.h"
|
||||
#include <console/cmddef.h>
|
||||
|
||||
|
||||
static const char* TAG = "cmd_tasks";
|
||||
|
||||
static int cmd_tasks_info(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->command = "ps";
|
||||
reg->help = "List running emulator tasks (for debug)";
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t bytes_per_task = 40; /* see vTaskList description */
|
||||
char *task_list_buffer = calloc(uxTaskGetNumberOfTasks() * bytes_per_task, 1);
|
||||
if (task_list_buffer == NULL) {
|
||||
ESP_LOGE(TAG, "failed to allocate buffer for vTaskList output");
|
||||
return 1;
|
||||
}
|
||||
console_fputs("\x1b[1mTask Name\tStatus\tPrio\tHWM\tTask#");
|
||||
#ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
|
||||
console_fputs("\tAffinity");
|
||||
#endif
|
||||
console_fputs("\x1b[m\r\n");
|
||||
vTaskList(task_list_buffer);
|
||||
console_fputs(task_list_buffer);
|
||||
free(task_list_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cmd_tasks()
|
||||
{
|
||||
console_cmd_register(cmd_tasks_info, "ps");
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/08.
|
||||
//
|
||||
|
||||
#include "console/cmd_common.h"
|
||||
|
||||
#include <esp_spi_flash.h>
|
||||
#include <esp_system.h>
|
||||
|
||||
#include "application.h"
|
||||
#include <console/cmddef.h>
|
||||
|
||||
|
||||
/** 'version' command */
|
||||
static int cmd_version(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->command = "version";
|
||||
reg->help = "Get version of the chip, SDK, and firmware.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
esp_chip_info_t info;
|
||||
esp_chip_info(&info);
|
||||
console_printf("IDF Version: %s\n", esp_get_idf_version());
|
||||
console_printf("Firmware: %s\n", APP_VERSION GIT_COUNT);
|
||||
console_printf(" git: %s\n", GIT_HASH);
|
||||
console_printf(" builded: %s\n", BUILD_TIMESTAMP);
|
||||
console_printf("Chip model: %s\n", info.model == CHIP_ESP32 ? "ESP32" : "Unknow");
|
||||
console_printf(" cores: %d\n", info.cores);
|
||||
console_printf(" feature: %s%s%s%s%d%s\n",
|
||||
info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "",
|
||||
info.features & CHIP_FEATURE_BLE ? "/BLE" : "",
|
||||
info.features & CHIP_FEATURE_BT ? "/BT" : "",
|
||||
info.features & CHIP_FEATURE_EMB_FLASH ? "/Embedded-Flash:" : "/External-Flash:",
|
||||
spi_flash_get_chip_size() / (1024 * 1024), "MB");
|
||||
console_printf("rev.number: %d\n", info.revision);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cmd_version(void)
|
||||
{
|
||||
console_cmd_register(cmd_version, "version");
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/08.
|
||||
//
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <settings.h>
|
||||
|
||||
#include "console/cmd_common.h"
|
||||
#include <console/cmddef.h>
|
||||
#include <application.h>
|
||||
#include <console/prefix_match.h>
|
||||
|
||||
|
||||
static int cmd_disable(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
EMPTY_CMD_SETUP("Disable WiFi");
|
||||
console_printf("WiFi "MSG_DISABLED"\nRestart to apply.\n");
|
||||
g_Settings.wifi_enabled = false;
|
||||
settings_persist(SETTINGS_wifi_enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_enable(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
EMPTY_CMD_SETUP("Enable WiFi");
|
||||
console_printf("WiFi "MSG_ENABLED"\nRestart to apply.\n");
|
||||
g_Settings.wifi_enabled = true;
|
||||
settings_persist(SETTINGS_wifi_enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *en_dis_cmds[] = {
|
||||
[0] = "disable",
|
||||
[1] = "enable",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_ap_conf(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_str *cmd;
|
||||
struct arg_str *ssid;
|
||||
struct arg_str *pw;
|
||||
struct arg_str *ip;
|
||||
struct arg_end *end;
|
||||
} args;
|
||||
|
||||
if (reg) {
|
||||
args.cmd = arg_str0(NULL, NULL, "{enable|disable}", EXPENDABLE_STRING("Command"));
|
||||
args.ssid = arg_str0("s", NULL, "<SSID>", EXPENDABLE_STRING("Set AP SSID"));
|
||||
args.pw = arg_str0("p", NULL, "<PWD>", EXPENDABLE_STRING("Set AP WPA2 password. Empty for open."));
|
||||
args.ip = arg_str0("a", NULL, "<IP>", "Set IP address (server + gateway). Always /24");
|
||||
args.end = arg_end(4);
|
||||
|
||||
reg->argtable = &args;
|
||||
reg->help = EXPENDABLE_STRING("Configure WiFi AP mode");
|
||||
return CONSOLE_OK;
|
||||
}
|
||||
|
||||
if (!g_State.wifi_inited) {
|
||||
console_printf("\x1b[31;1mWiFi is disabled!\x1b[22m\nEnable with `wifi enable`, restart to apply.\x1b[m\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (args.cmd->count) {
|
||||
int match = prefix_match(args.cmd->sval[0], en_dis_cmds, 0);
|
||||
|
||||
switch (match) {
|
||||
case 0:
|
||||
console_printf("AP mode "MSG_DISABLED"\nRestart to apply.\n");
|
||||
g_Settings.ap_enabled = false;
|
||||
settings_persist(SETTINGS_ap_enabled);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
console_printf("AP mode "MSG_ENABLED"\nRestart to apply.\n");
|
||||
g_Settings.ap_enabled = true;
|
||||
settings_persist(SETTINGS_ap_enabled);
|
||||
break;
|
||||
|
||||
default:
|
||||
return CONSOLE_ERR_INVALID_ARG;
|
||||
}
|
||||
} else {
|
||||
// No cmd
|
||||
console_printf("AP mode: %s\n", g_Settings.ap_enabled? MSG_ENABLED: MSG_DISABLED);
|
||||
}
|
||||
|
||||
if (args.ip->count) {
|
||||
uint32_t a = 0;
|
||||
if (!inet_aton(args.ip->sval[0], &a)) {
|
||||
console_println("Invalid IP");
|
||||
return CONSOLE_ERR_INVALID_ARG;
|
||||
}
|
||||
g_Settings.ap_ip = a; // aton output is already in network byte order
|
||||
settings_persist(SETTINGS_ap_ip);
|
||||
|
||||
console_println("AP IP changed, restart to apply.\n");
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
wifi_config_t apconf = {};
|
||||
ESP_ERROR_CHECK(esp_wifi_get_config(ESP_IF_WIFI_AP, &apconf));
|
||||
|
||||
if (args.ssid->count) {
|
||||
//apconf.ap.authmode = WIFI_AUTH_OPEN;
|
||||
strcpy((char*)apconf.ap.ssid, args.ssid->sval[0]);
|
||||
apconf.ap.ssid_len = strlen(args.ssid->sval[0]);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (args.pw->count) {
|
||||
size_t len = strlen(args.pw->sval[0]);
|
||||
if (len < 8 && len != 0) {
|
||||
console_println("AP pw must be 8 chars or more!");
|
||||
return CONSOLE_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
strcpy((char*)apconf.ap.password, args.pw->sval[0]);
|
||||
if (len == 0) {
|
||||
// if no pw is set, the AP will be open
|
||||
apconf.ap.authmode = WIFI_AUTH_OPEN;
|
||||
} else {
|
||||
apconf.ap.authmode = WIFI_AUTH_WPA2_PSK;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
esp_err_t rv = esp_wifi_set_config(ESP_IF_WIFI_AP, &apconf);
|
||||
if (rv != ESP_OK) {
|
||||
console_printf("Error set config: %s\n", esp_err_to_name(rv));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
console_printf("AP SSID: \"%s\"\n", apconf.ap.ssid);
|
||||
console_printf("AP PW: \"%s\"\n", apconf.ap.password);
|
||||
console_printf("AP own IP: %s/24\n", inet_ntoa(g_Settings.ap_ip));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_sta_conf(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_str *cmd;
|
||||
struct arg_end *end;
|
||||
} args;
|
||||
|
||||
if (reg) {
|
||||
args.cmd = arg_str0(NULL, NULL, "{enable|disable}", EXPENDABLE_STRING("Command"));
|
||||
args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &args;
|
||||
reg->help = EXPENDABLE_STRING("Configure WiFi STA mode");
|
||||
return CONSOLE_OK;
|
||||
}
|
||||
|
||||
if (!g_State.wifi_inited) {
|
||||
console_printf("\x1b[31;1mWiFi is disabled!\x1b[22m\nEnable with `wifi enable`, restart to apply.\x1b[m\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (args.cmd->count) {
|
||||
int match = prefix_match(args.cmd->sval[0], en_dis_cmds, 0);
|
||||
|
||||
switch (match) {
|
||||
case 0:
|
||||
console_printf("STA mode "MSG_DISABLED"\nRestart to apply.\n");
|
||||
g_Settings.sta_enabled = false;
|
||||
settings_persist(SETTINGS_sta_enabled);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
console_printf("STA mode "MSG_ENABLED"\nRestart to apply.\n");
|
||||
g_Settings.sta_enabled = true;
|
||||
settings_persist(SETTINGS_sta_enabled);
|
||||
break;
|
||||
|
||||
default:
|
||||
return CONSOLE_ERR_INVALID_ARG;
|
||||
}
|
||||
} else {
|
||||
// No cmd
|
||||
console_printf("STA mode: %s\n", g_Settings.sta_enabled? MSG_ENABLED: MSG_DISABLED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Disconnect from WiFi and forget creds */
|
||||
static int cmd_sta_forget(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_end *end;
|
||||
} args;
|
||||
|
||||
if (reg) {
|
||||
args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &args;
|
||||
reg->command = "wifi forget";
|
||||
reg->help = "Disconnect from WiFi AP and erase stored credentials";
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_printf("Removing saved WiFi credentials and disconnecting.\n");
|
||||
|
||||
if (!g_State.wifi_inited) {
|
||||
console_printf("\x1b[31;1mWiFi is disabled!\x1b[22m\nEnable with `wifi enable`, restart to apply.\x1b[m\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wifi_config_t wificonf;
|
||||
esp_wifi_get_config(WIFI_IF_STA, &wificonf);
|
||||
wificonf.sta.ssid[0] = 0;
|
||||
wificonf.sta.password[0] = 0;
|
||||
esp_wifi_set_config(WIFI_IF_STA, &wificonf);
|
||||
esp_wifi_disconnect();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DEF_WIFI_TIMEOUT 10000
|
||||
|
||||
static bool wifi_join(const char* ssid, const char* pass, int timeout_ms)
|
||||
{
|
||||
wifi_config_t wifi_config = {};
|
||||
strncpy((char*) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
|
||||
if (pass) {
|
||||
strncpy((char*) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
|
||||
|
||||
xEventGroupClearBits(g_wifi_event_group, WIFI_CONNECTED_BIT|WIFI_FAIL_BIT);
|
||||
|
||||
ESP_ERROR_CHECK( esp_wifi_disconnect() );
|
||||
ESP_ERROR_CHECK( esp_wifi_connect() );
|
||||
|
||||
int bits = xEventGroupWaitBits(g_wifi_event_group, WIFI_CONNECTED_BIT|WIFI_FAIL_BIT,
|
||||
/* clear */ 0, /* wait for all */0, pdMS_TO_TICKS(timeout_ms));
|
||||
|
||||
return (bits & EG_WIFI_CONNECTED_BIT) != 0;
|
||||
}
|
||||
|
||||
static int cmd_join(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_int *timeout;
|
||||
struct arg_str *ssid;
|
||||
struct arg_str *password;
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.timeout = arg_int0("t", "timeout", "<t>", "Connection timeout, ms");
|
||||
cmd_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
|
||||
cmd_args.password = arg_str0(NULL, NULL, "<pass>", "PSK of AP");
|
||||
cmd_args.end = arg_end(2);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->command = "wifi join";
|
||||
reg->help = "Join WiFi AP as a station";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_State.wifi_inited) {
|
||||
console_printf("\x1b[31;1mWiFi is disabled!\x1b[22m\n"
|
||||
"Enable with `wifi enable`, restart to apply.\x1b[m\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_printf("Connecting to '%s'\n", cmd_args.ssid->sval[0]);
|
||||
|
||||
int tmeo = cmd_args.timeout->count ? cmd_args.timeout->ival[0] : DEF_WIFI_TIMEOUT;
|
||||
|
||||
bool connected = wifi_join(cmd_args.ssid->sval[0],
|
||||
cmd_args.password->sval[0],
|
||||
tmeo);
|
||||
if (!connected) {
|
||||
console_printf("Connection timed out\n");
|
||||
|
||||
// erase config
|
||||
wifi_config_t wifi_config = {};
|
||||
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
|
||||
|
||||
return 1;
|
||||
}
|
||||
console_printf("Connected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_wifi_status(console_ctx_t *ctx, cmd_signature_t *reg)
|
||||
{
|
||||
static struct {
|
||||
struct arg_end *end;
|
||||
} cmd_args;
|
||||
|
||||
if (reg) {
|
||||
cmd_args.end = arg_end(1);
|
||||
|
||||
reg->argtable = &cmd_args;
|
||||
reg->command = "wifi status";
|
||||
reg->help = "Check WiFi / IP status";
|
||||
return 0;
|
||||
}
|
||||
|
||||
console_printf("WiFi support: %s\n", g_Settings.wifi_enabled ? MSG_ENABLED : MSG_DISABLED);
|
||||
console_printf("STA mode: %s\n", g_Settings.sta_enabled? MSG_ENABLED: MSG_DISABLED);
|
||||
console_printf("AP mode: %s\n", g_Settings.ap_enabled? MSG_ENABLED: MSG_DISABLED);
|
||||
|
||||
console_printf("\n");
|
||||
|
||||
if (g_Settings.wifi_enabled) {
|
||||
wifi_config_t config;
|
||||
if (ESP_OK == esp_wifi_get_config(ESP_IF_WIFI_STA, &config)) {
|
||||
if (config.sta.ssid[0]) {
|
||||
console_printf("Configured to connect to SSID \"%s\".\n"
|
||||
"Use `wifi forget` to drop saved credentials.\n\n",
|
||||
config.sta.ssid);
|
||||
|
||||
tcpip_adapter_ip_info_t ipinfo;
|
||||
if (ESP_OK == tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipinfo)) {
|
||||
// ! inet_ntoa uses a global static buffer, cant use multiple in one printf call
|
||||
console_printf("IP: %s, ", inet_ntoa(ipinfo.ip.addr));
|
||||
console_printf("Mask: %s, ", inet_ntoa(ipinfo.netmask.addr));
|
||||
console_printf("Gateway: %s\n", inet_ntoa(ipinfo.gw.addr));
|
||||
} else {
|
||||
console_printf("No IP!\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
console_printf("No network SSID configured.\n"
|
||||
"Use `wifi join` to connect to a WiFi network.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_cmd_wifi(void)
|
||||
{
|
||||
console_group_add("wifi", "WiFi configuration");
|
||||
|
||||
console_cmd_register(cmd_enable, "wifi enable");
|
||||
console_cmd_register(cmd_disable, "wifi disable");
|
||||
|
||||
console_cmd_register(cmd_sta_conf, "wifi sta");
|
||||
console_cmd_register(cmd_sta_forget, "wifi forget");
|
||||
console_cmd_register(cmd_join, "wifi join");
|
||||
|
||||
console_cmd_register(cmd_ap_conf, "wifi ap");
|
||||
|
||||
console_cmd_register(cmd_wifi_status, "wifi status");
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
|
||||
#include <utils.h>
|
||||
#include <stdlib.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/uart.h>
|
||||
#include <errno.h>
|
||||
#include <esp_vfs_dev.h>
|
||||
#include <fcntl.h>
|
||||
#include <settings.h>
|
||||
#include "console_ioimpl.h"
|
||||
#include "tasks.h"
|
||||
#include "telnet_parser.h"
|
||||
#include "cmd_common.h"
|
||||
|
||||
static const char *TAG = "console-io";
|
||||
|
||||
void console_internal_error_print(const char *msg) {
|
||||
ESP_LOGE(TAG, "CONSOLE ERR: %s", msg);
|
||||
}
|
||||
|
||||
void console_setup_uart_stdio(void)
|
||||
{
|
||||
assert(CONFIG_ESP_CONSOLE_UART_NUM == UART_NUM_0);
|
||||
|
||||
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
|
||||
esp_vfs_dev_uart_port_set_rx_line_endings(UART_NUM_0, ESP_LINE_ENDINGS_CR);
|
||||
/* Move the caret to the beginning of the next line on '\n' */
|
||||
esp_vfs_dev_uart_port_set_tx_line_endings(UART_NUM_0, ESP_LINE_ENDINGS_CRLF); // this is the default anyway
|
||||
|
||||
/* Disable buffering on stdin and stdout */
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
// fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // make input non-blocking
|
||||
|
||||
#if 0
|
||||
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
|
||||
* correct while APB frequency is changing in light sleep mode.
|
||||
*/
|
||||
const uart_config_t uart_config = {
|
||||
.baud_rate = CONFIG_CONSOLE_UART_BAUDRATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.use_ref_tick = true
|
||||
};
|
||||
ESP_ERROR_CHECK( uart_param_config(CONFIG_CONSOLE_UART_NUM, &uart_config) );
|
||||
#endif
|
||||
|
||||
/* Install UART driver for interrupt-driven reads and writes */
|
||||
ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM,
|
||||
/* rxbuf */ 256, /* txbuf */ 0, /* que */ 0, /* uart que */ NULL, /* alloc flags */ 0));
|
||||
|
||||
uart_flush(CONFIG_ESP_CONSOLE_UART_NUM);
|
||||
|
||||
/* Tell VFS to use UART driver */
|
||||
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
|
||||
}
|
||||
|
||||
static void my_console_task_freertos(void *param) {
|
||||
console_ctx_t *ctx = param;
|
||||
assert(CONSOLE_CTX_MAGIC == ctx->__internal_magic); // just make sure it's OK
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50)); // ??
|
||||
|
||||
bool logged_in = true;
|
||||
{
|
||||
struct console_ioimpl *io = ctx->ioctx;
|
||||
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
||||
|
||||
if (io->kind == CONSOLE_IO_TELNET) {
|
||||
const size_t pwlen = strnlen(g_Settings.console_pw, CONSOLE_PW_LEN);
|
||||
if (pwlen != 0) {
|
||||
ESP_LOGE(TAG, "Pw=\"%.*s\"", pwlen, g_Settings.console_pw);
|
||||
|
||||
console_print_ctx(&io->ctx, "Password: ");
|
||||
|
||||
// Make the prompt fancy with asterisks and stuff. Backspace is not supported.
|
||||
int pos = 0;
|
||||
char buf[CONSOLE_PW_LEN] = {/*zeros*/};
|
||||
while (1) {
|
||||
char ch = 0;
|
||||
console_read_ctx(ctx, &ch, 1);
|
||||
if (ch == 10 || ch == 13) {
|
||||
console_write_ctx(ctx, "\n", 1);
|
||||
break;
|
||||
}
|
||||
if (ch >= 32 && ch <= 126) {
|
||||
if (pos < CONSOLE_PW_LEN) {
|
||||
buf[pos++] = ch;
|
||||
}
|
||||
console_write_ctx(ctx, "*", 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == strncmp(buf, g_Settings.console_pw, CONSOLE_PW_LEN)) {
|
||||
console_print_ctx(&io->ctx, "Login OK!\n");
|
||||
} else {
|
||||
console_print_ctx(&io->ctx, "Login failed!\n");
|
||||
logged_in = false;
|
||||
if (io->telnet.tcpcli) {
|
||||
tcpd_kick(io->telnet.tcpcli);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logged_in) {
|
||||
console_print_motd(ctx);
|
||||
console_task(param);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Console task ended");
|
||||
|
||||
// This delay should ensure the TCP client is shut down completely
|
||||
// before we proceed to free stuff. The delay is deliberately very generous,
|
||||
// we are in no rush here.
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
|
||||
// Deallocate what console allocated inside ctx, ctx is part of ioimpl so it will NOT be freed
|
||||
ESP_LOGD(TAG, "Clear console context");
|
||||
console_ctx_destroy(ctx);
|
||||
|
||||
ESP_LOGD(TAG, "Clear IO context");
|
||||
struct console_ioimpl *io = ctx->ioctx;
|
||||
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
||||
|
||||
// Tear down IO
|
||||
if (io->kind == CONSOLE_IO_TELNET) {
|
||||
vRingbufferDelete(io->telnet.console_stdin_ringbuf);
|
||||
}
|
||||
|
||||
// Free ioimpl
|
||||
free(io);
|
||||
|
||||
ESP_LOGI(TAG, "Console task shutdown.");
|
||||
// suicide the task
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void telnet_shutdown_handler(console_ctx_t *ctx) {
|
||||
assert(ctx);
|
||||
assert(CONSOLE_CTX_MAGIC == ctx->__internal_magic); // just make sure it's OK
|
||||
struct console_ioimpl *io = ctx->ioctx;
|
||||
assert(io);
|
||||
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
||||
|
||||
tcpd_kick(io->telnet.tcpcli);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start console working with stdin and stdout
|
||||
*/
|
||||
static esp_err_t console_start_io(struct console_ioimpl **pIo, TaskHandle_t * hdl, enum console_iokind kind, TcpdClient_t client) {
|
||||
struct console_ioimpl * io = calloc(1, sizeof(struct console_ioimpl));
|
||||
if (io == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (pIo != NULL) {
|
||||
*pIo = io;
|
||||
}
|
||||
|
||||
io->__magic = CONSOLE_IOIMPL_MAGIC;
|
||||
|
||||
io->kind = kind;
|
||||
|
||||
if (kind == CONSOLE_IO_TELNET) {
|
||||
io->telnet.console_stdin_ringbuf = xRingbufferCreate(CONSOLE_BUFSIZE, RINGBUF_TYPE_BYTEBUF);
|
||||
if (NULL == io->telnet.console_stdin_ringbuf) {
|
||||
ESP_LOGE(TAG, "Failed to create RB!");
|
||||
free(io);
|
||||
if (pIo != NULL) {
|
||||
*pIo = NULL;
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
io->telnet.tcpcli = client;
|
||||
|
||||
// Store the "io" reference as "cctx" in the TCP client, so we know
|
||||
// where to write received data in the callback
|
||||
tcpd_set_client_ctx(client, io);
|
||||
|
||||
} else {
|
||||
io->files.inf = stdin;
|
||||
io->files.outf = stdout;
|
||||
}
|
||||
|
||||
// using "static" allocation - context is part of the io struct
|
||||
// Here "io" is stored as "ioctx" in "ctx" and then passed to the read/write functions
|
||||
if (NULL == console_ctx_init(&io->ctx, io)) {
|
||||
ESP_LOGE(TAG, "Console init failed!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (kind == CONSOLE_IO_FILES) {
|
||||
io->ctx.exit_allowed = false;
|
||||
} else {
|
||||
/* TELNET */
|
||||
io->ctx.shutdown_handler = telnet_shutdown_handler;
|
||||
}
|
||||
|
||||
assert(io->ctx.__internal_magic == CONSOLE_CTX_MAGIC);
|
||||
|
||||
snprintf(io->ctx.prompt, CONSOLE_PROMPT_MAX_LEN, "\x1b[36;1m> \x1b[m");
|
||||
|
||||
if (pdPASS != xTaskCreate(
|
||||
my_console_task_freertos, // func
|
||||
"console", // name
|
||||
CONSOLE_TASK_STACK, // stack
|
||||
&io->ctx, // param
|
||||
CONSOLE_TASK_PRIO, // prio
|
||||
hdl // handle dest
|
||||
)) {
|
||||
ESP_LOGE(TAG, "Err create console task!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
ESP_LOGE(TAG, "console_start_io FAILED, freeing resources!");
|
||||
console_ctx_destroy(&io->ctx);
|
||||
|
||||
if (kind == CONSOLE_IO_TELNET) {
|
||||
vRingbufferDelete(io->telnet.console_stdin_ringbuf);
|
||||
io->telnet.console_stdin_ringbuf = NULL;
|
||||
}
|
||||
|
||||
free(io);
|
||||
if (pIo != NULL) {
|
||||
*pIo = NULL;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t console_start_stdio(struct console_ioimpl **pIo, TaskHandle_t * hdl) {
|
||||
ESP_LOGI(TAG, "Start STDIO console task");
|
||||
|
||||
return console_start_io(pIo, hdl, CONSOLE_IO_FILES, NULL);
|
||||
}
|
||||
|
||||
esp_err_t console_start_tcp(struct console_ioimpl **pIo, TaskHandle_t * hdl, TcpdClient_t client) {
|
||||
ESP_LOGI(TAG, "Start TCP console task");
|
||||
|
||||
return console_start_io(pIo, hdl, CONSOLE_IO_TELNET, client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to console context.
|
||||
*
|
||||
* Return number of characters written, -1 on error.
|
||||
*/
|
||||
int console_write_ctx(console_ctx_t *ctx, const char *text, size_t len) {
|
||||
struct console_ioimpl *io = ctx->ioctx;
|
||||
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
||||
|
||||
if (io->kind == CONSOLE_IO_TELNET) {
|
||||
const char *wp = (const char *)text;
|
||||
const char *sp = (const char *)text;
|
||||
int towrite = 0;
|
||||
|
||||
// hack to allow using bare \n
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
char c = *sp++;
|
||||
if (c == '\n') {
|
||||
// LF: print the chunk before it and a CR.
|
||||
if (towrite > 0) {
|
||||
if (ESP_OK != tcpd_send(io->telnet.tcpcli, (uint8_t *) wp, towrite)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (ESP_OK != tcpd_send(io->telnet.tcpcli, (uint8_t*) "\r", 1)) {
|
||||
return -1;
|
||||
}
|
||||
// The LF gets rolled into the next chunk.
|
||||
wp = sp - 1;
|
||||
towrite = 1;
|
||||
} else {
|
||||
// Non-LF character is printed as is
|
||||
towrite++;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the leftovers (chars from last LF or from the start)
|
||||
if (towrite > 0) {
|
||||
if (ESP_OK != tcpd_send(io->telnet.tcpcli, (uint8_t*) wp, towrite)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
} else {
|
||||
// File IO
|
||||
errno = 0;
|
||||
// the UART driver takes care of encoding \n as \r\n
|
||||
size_t n = fwrite(text, 1, len, io->files.outf);
|
||||
if (n != len) {
|
||||
if (errno || ferror(io->files.outf)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read from console context's input stream.
|
||||
*
|
||||
* Return number of characters read, -1 on error
|
||||
*/
|
||||
int console_read_ctx(console_ctx_t *ctx, char *dest, size_t count) {
|
||||
if (!console_have_stdin_ctx(ctx)) {
|
||||
ESP_LOGW(TAG, "Console stream has no stdin!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct console_ioimpl *io = ctx->ioctx;
|
||||
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
||||
|
||||
if (io->kind == CONSOLE_IO_TELNET) {
|
||||
size_t remain = count;
|
||||
char *wp = dest;
|
||||
do {
|
||||
size_t rcount = 0;
|
||||
uint8_t *chunk = xRingbufferReceiveUpTo(io->telnet.console_stdin_ringbuf, &rcount, portMAX_DELAY, remain);
|
||||
|
||||
// telnet options negotiation
|
||||
rcount = telnet_middleware_read(ctx, chunk, rcount);
|
||||
|
||||
if (rcount > 0) {
|
||||
memcpy(wp, chunk, rcount);
|
||||
wp += rcount;
|
||||
remain -= rcount;
|
||||
}
|
||||
|
||||
vRingbufferReturnItem(io->telnet.console_stdin_ringbuf, chunk);
|
||||
} while (remain > 0);
|
||||
|
||||
return count;
|
||||
} else {
|
||||
// File IO
|
||||
errno = 0;
|
||||
// clearerr(io->files.inf);
|
||||
size_t r = fread(dest, 1, count, io->files.inf);
|
||||
|
||||
if (errno != 0 || feof(io->files.inf) /*|| ferror(io->files.inf)*/) {
|
||||
ESP_LOGW(TAG, "Console stream EOF or error.");
|
||||
perror("Read err");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if console input stream has bytes ready.
|
||||
*
|
||||
* @return number of queued bytes, 0 if none, -1 on error.
|
||||
*/
|
||||
int console_can_read_ctx(console_ctx_t *ctx) {
|
||||
if (!console_have_stdin_ctx(ctx)) {
|
||||
ESP_LOGW(TAG, "Console stream has no stdin!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct console_ioimpl *io = ctx->ioctx;
|
||||
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
||||
|
||||
if (io->kind == CONSOLE_IO_TELNET) {
|
||||
uint32_t nitems = 0;
|
||||
vRingbufferGetInfo(io->telnet.console_stdin_ringbuf, NULL, NULL, NULL, NULL, &nitems);
|
||||
return nitems;
|
||||
} else {
|
||||
// File IO
|
||||
|
||||
// a hack with select - this is used rarely, we can afford the overhead
|
||||
fd_set readfds;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fileno(io->files.inf), &readfds);
|
||||
struct timeval timeout = {0, 0};
|
||||
int sel_rv = select(1, &readfds, NULL, NULL, &timeout);
|
||||
if (sel_rv > 0) {
|
||||
return 1; // at least one
|
||||
} else if (sel_rv == -1) {
|
||||
ESP_LOGW(TAG, "Console stream EOF or error.");
|
||||
return -1; // error
|
||||
} else {
|
||||
return 0; // nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if console context is not NULL and has stdin stream available
|
||||
*
|
||||
* @return have stdin
|
||||
*/
|
||||
bool console_have_stdin_ctx(console_ctx_t *ctx) {
|
||||
if (!ctx->ioctx) return false;
|
||||
struct console_ioimpl *io = ctx->ioctx;
|
||||
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
||||
|
||||
if (io->kind == CONSOLE_IO_TELNET) {
|
||||
return io->telnet.tcpcli != NULL &&
|
||||
io->telnet.console_stdin_ringbuf != NULL;
|
||||
} else {
|
||||
// File IO
|
||||
// ESP_LOGW(TAG, "%p, %d, %d", io->files.inf,
|
||||
// feof(io->files.inf),
|
||||
// ferror(io->files.inf));
|
||||
|
||||
return io->files.inf != NULL &&
|
||||
!feof(io->files.inf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* TODO file description
|
||||
*
|
||||
* Created on 2020/04/09.
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_CONSOLE_IOIMPL_H
|
||||
#define CSPEMU_CONSOLE_IOIMPL_H
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/ringbuf.h>
|
||||
#include <socket_server.h>
|
||||
#include <console/console.h>
|
||||
|
||||
/** Console ring buffer capacity */
|
||||
#define CONSOLE_BUFSIZE 512
|
||||
|
||||
/** Enum for tagging ioimpl kind */
|
||||
enum console_iokind {
|
||||
CONSOLE_IO_FILES,
|
||||
CONSOLE_IO_TELNET,
|
||||
};
|
||||
|
||||
#define CONSOLE_IOIMPL_MAGIC 0x079fbf72
|
||||
|
||||
struct console_ioimpl {
|
||||
/** This is a tag for the following union */
|
||||
enum console_iokind kind;
|
||||
union {
|
||||
/** STDIO variant data */
|
||||
struct {
|
||||
/** STDIN */
|
||||
FILE *inf;
|
||||
/** STDOUT */
|
||||
FILE *outf;
|
||||
} files;
|
||||
|
||||
/** Telnet variant data */
|
||||
struct {
|
||||
/** TCP client handle */
|
||||
TcpdClient_t tcpcli;
|
||||
/** Received data ring buffer */
|
||||
RingbufHandle_t console_stdin_ringbuf;
|
||||
} telnet;
|
||||
};
|
||||
|
||||
/** Console context */
|
||||
console_ctx_t ctx;
|
||||
uint32_t __magic;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup UART IO for console
|
||||
*/
|
||||
void console_setup_uart_stdio(void);
|
||||
|
||||
/**
|
||||
* Start console for stdin/stdout (UART)
|
||||
*
|
||||
* @param pIo - pointer where the allocated struct will be stored
|
||||
* @param hdl - handle to the created task, can be NULL if not used
|
||||
* @return success
|
||||
*/
|
||||
esp_err_t console_start_stdio(struct console_ioimpl **pIo, TaskHandle_t * hdl);
|
||||
|
||||
/**
|
||||
* Start console for a TCP client
|
||||
*
|
||||
* @param pIo - pointer where the allocated struct will be stored
|
||||
* @param hdl - handle to the created task, can be NULL if not used
|
||||
* @param client - TCP server client handle
|
||||
* @return success
|
||||
*/
|
||||
esp_err_t console_start_tcp(struct console_ioimpl **pIo, TaskHandle_t * hdl, TcpdClient_t client);
|
||||
|
||||
|
||||
#endif //CSPEMU_CONSOLE_IOIMPL_H
|
||||
@@ -0,0 +1,155 @@
|
||||
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <esp_log.h>
|
||||
#include <string.h>
|
||||
#include <freertos/ringbuf.h>
|
||||
|
||||
#include "socket_server.h"
|
||||
#include "console_server.h"
|
||||
#include "application.h"
|
||||
#include "tasks.h"
|
||||
#include "telnet_parser.h"
|
||||
#include "console_ioimpl.h"
|
||||
|
||||
static const char *TAG = "console_srv";
|
||||
|
||||
Tcpd_t g_telnet_server = NULL;
|
||||
TcpdClient_t telnetsrv_last_rx_client = NULL;
|
||||
|
||||
/**
|
||||
* Send a textual message to all the connected peers
|
||||
*
|
||||
* @param interface
|
||||
* @param packet
|
||||
* @param timeout
|
||||
* @return
|
||||
*/
|
||||
esp_err_t telnetsrv_send(TcpdClient_t client, const char *message, ssize_t len)
|
||||
{
|
||||
if (!g_telnet_server) {
|
||||
ESP_LOGE(TAG, "server not inited");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (len < 0) len = strlen(message);
|
||||
|
||||
if (client) {
|
||||
tcpd_send(client, (const uint8_t *) message, len);
|
||||
} else {
|
||||
tcpd_broadcast(g_telnet_server, (const uint8_t *) message, len);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle received bytes
|
||||
*
|
||||
* @param client
|
||||
* @param buf
|
||||
* @param nbytes
|
||||
*/
|
||||
static void telnetsrv_handle(TcpdClient_t client, char *buf, int nbytes)
|
||||
{
|
||||
void *ctx = tcpd_get_client_ctx(client);
|
||||
struct console_ioimpl *io = ctx;
|
||||
|
||||
if (!ctx) {
|
||||
ESP_LOGE(TAG, "telnet rx with no ioctx!");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
||||
|
||||
int rv = xRingbufferSend(io->telnet.console_stdin_ringbuf, buf, (size_t) nbytes, pdMS_TO_TICKS(100));
|
||||
if (rv != pdPASS) {
|
||||
ESP_LOGE(TAG, "console ringbuf overflow");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket read handler
|
||||
*/
|
||||
esp_err_t read_fn(Tcpd_t serv, TcpdClient_t client, int sockfd)
|
||||
{
|
||||
char buf[64];
|
||||
int nbytes = read(sockfd, buf, sizeof(buf));
|
||||
if (nbytes <= 0) return ESP_FAIL;
|
||||
|
||||
// ESP_LOGI(TAG, "Rx %d bytes", nbytes);
|
||||
telnetsrv_handle(client, buf, nbytes);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void print_motd(console_ctx_t *ctx)
|
||||
{
|
||||
console_printf_ctx(ctx, COLOR_RESET, "\n"
|
||||
"===================================================\n"
|
||||
" ESP32 node "APP_NAME" "APP_VERSION " #" GIT_HASH "\n"
|
||||
" Built " BUILD_TIMESTAMP "\n"
|
||||
"\n"
|
||||
" Run `ls` for a list of commands.\n"
|
||||
"===================================================\n\n"
|
||||
);
|
||||
// show the initial prompt
|
||||
console_print(ctx->prompt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket open handler - send a MOTD
|
||||
*/
|
||||
static esp_err_t open_fn(Tcpd_t serv, TcpdClient_t client)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
struct console_ioimpl *io = NULL;
|
||||
esp_err_t rv = console_start_tcp(&io, NULL, client);
|
||||
if (rv != ESP_OK) {
|
||||
return rv;
|
||||
}
|
||||
assert(io);
|
||||
|
||||
// set telnet params, unless this is the injected stdin client
|
||||
if (tcpd_get_client_fd(client) != STDIN_FILENO) {
|
||||
telnet_send_will(&io->ctx, OPT_SUPPRESS_GO_AHEAD);
|
||||
telnet_send_will(&io->ctx, OPT_ECHO);
|
||||
telnet_send_dont(&io->ctx, OPT_ECHO);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void console_print_motd(console_ctx_t *ctx)
|
||||
{
|
||||
print_motd(ctx);
|
||||
}
|
||||
|
||||
esp_err_t telnetsrv_start(uint16_t port)
|
||||
{
|
||||
tcpd_config_t server_config = TCPD_INIT_DEFAULT();
|
||||
server_config.max_clients = 3;
|
||||
server_config.task_prio = TELNET_TASK_PRIO;
|
||||
server_config.task_stack = TELNET_TASK_STACK;
|
||||
server_config.task_name = "TelnetSrv";
|
||||
server_config.port = port;
|
||||
server_config.read_fn = read_fn;
|
||||
server_config.open_fn = open_fn;
|
||||
// close fn is not needed, console shuts down if the FD becomes invalid (read fails)
|
||||
|
||||
ESP_ERROR_CHECK(tcpd_init(&server_config, &g_telnet_server));
|
||||
|
||||
// this deadlocks now... XXX
|
||||
// cspemu_add_shutdown_handler(telnetsrv_kick_all);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void telnetsrv_kick_all(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Kick all telnet clients");
|
||||
tcpd_kick_all(g_telnet_server, false); // don't kick injected clients
|
||||
ESP_LOGI(TAG, "Kicking done!");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* TCP debug server.
|
||||
*
|
||||
* Broadcasts TCP debug messages and provides a simple console interface
|
||||
* to modify settings.
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_TCPDBG_SERVER_H
|
||||
#define CSPEMU_TCPDBG_SERVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <console/console.h>
|
||||
#include "esp_err.h"
|
||||
#include "socket_server.h"
|
||||
|
||||
extern Tcpd_t g_telnet_server;
|
||||
|
||||
esp_err_t telnetsrv_start(uint16_t port);
|
||||
|
||||
esp_err_t telnetsrv_send(TcpdClient_t client, const char *message, ssize_t len);
|
||||
|
||||
void telnetsrv_kick_all(void);
|
||||
|
||||
/**
|
||||
* Broadcast the welcome message
|
||||
*/
|
||||
void console_print_motd(console_ctx_t *ctx);
|
||||
|
||||
#endif //CSPEMU_TCPDBG_SERVER_H
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "register_cmds.h"
|
||||
#include <console/console.h>
|
||||
|
||||
static const char *TAG = "reg_cmds";
|
||||
|
||||
extern void register_cmd_wifi();
|
||||
extern void register_cmd_tasks();
|
||||
extern void register_cmd_version();
|
||||
extern void register_cmd_heap();
|
||||
extern void register_cmd_dump();
|
||||
extern void register_cmd_restart();
|
||||
extern void register_cmd_factory_reset();
|
||||
extern void register_cmd_ip();
|
||||
extern void register_cmd_pw(void);
|
||||
|
||||
void register_console_commands()
|
||||
{
|
||||
console_group_add("wifi", "WiFi control");
|
||||
register_cmd_wifi();
|
||||
|
||||
register_cmd_tasks();
|
||||
register_cmd_version();
|
||||
register_cmd_heap();
|
||||
//register_cmd_dump();
|
||||
register_cmd_restart();
|
||||
register_cmd_factory_reset();
|
||||
register_cmd_ip();
|
||||
register_cmd_pw();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef REGISTER_CMDS_H
|
||||
#define REGISTER_CMDS_H
|
||||
|
||||
void register_console_commands(void);
|
||||
|
||||
#endif // REGISTER_CMDS_H
|
||||
@@ -0,0 +1,539 @@
|
||||
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/queue.h>
|
||||
#include <malloc.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_log.h>
|
||||
#include <linenoise/linenoise.h>
|
||||
#include <esp_system.h>
|
||||
#include "telnet_parser.h"
|
||||
#include "console_server.h"
|
||||
|
||||
static const char *TAG = "telnet";
|
||||
|
||||
// The console is all single-threaded, using a circular buffer, so we can get away
|
||||
// with a global state here
|
||||
|
||||
enum telnet_code {
|
||||
CODE_SE = 240,
|
||||
CODE_NOP = 241,
|
||||
CODE_DATA_MARK = 242,
|
||||
CODE_BRK = 243, // break signal - kick all clients (including yourself)
|
||||
CODE_IP = 244, // interrupt process (reboots ESP)
|
||||
CODE_AO = 245, // abort output
|
||||
CODE_AYT = 246, // are you there? (sends an ASCII text reply)
|
||||
CODE_ERASE_CHAR = 247, // these are obsolete
|
||||
CODE_ERASE_LINE = 248,
|
||||
CODE_GO_AHEAD = 249, // not used, always negotiate to SGA
|
||||
CODE_SB = 250,
|
||||
CODE_WILL = 251,
|
||||
CODE_WONT = 252,
|
||||
CODE_DO = 253,
|
||||
CODE_DONT = 254,
|
||||
CODE_IAC = 255
|
||||
};
|
||||
|
||||
enum parser_state {
|
||||
// next character is expected to be text or IAC
|
||||
PS_TEXT = 0,
|
||||
// received IAC, next character should be a verb
|
||||
PS_IAC,
|
||||
PS_WILL,
|
||||
PS_WONT,
|
||||
PS_DO,
|
||||
PS_DONT,
|
||||
PS_SUBNEG,
|
||||
PS_SUBNEG_BODY,
|
||||
PS_SUBNEG_BODY_IAC
|
||||
};
|
||||
|
||||
struct awaiting_reply_item;
|
||||
|
||||
struct awaiting_reply_item {
|
||||
enum telnet_code code;
|
||||
enum telnet_option option;
|
||||
SLIST_ENTRY(awaiting_reply_item) next;
|
||||
};
|
||||
|
||||
SLIST_HEAD(awaiting_reply_list, awaiting_reply_item);
|
||||
|
||||
#define SUBNEG_BUF_LEN 16
|
||||
static struct telnet_state {
|
||||
enum parser_state pstate;
|
||||
enum telnet_option subneg_option;
|
||||
struct awaiting_reply_list awaiting_reply;
|
||||
uint8_t subneg_buf[SUBNEG_BUF_LEN];
|
||||
uint8_t subneg_buf_i;
|
||||
bool mode_rx_binary;
|
||||
bool mode_tx_binary;
|
||||
} s_ts = { /* all zeros */ };
|
||||
|
||||
static void _expect_reply(enum telnet_code code, enum telnet_option option)
|
||||
{
|
||||
ESP_LOGV(TAG, "Awaiting reply to %d:0x%02x", code, option);
|
||||
struct awaiting_reply_item *item = calloc(sizeof(struct awaiting_reply_item), 1);
|
||||
item->code = code;
|
||||
item->option = option;
|
||||
SLIST_INSERT_HEAD(&s_ts.awaiting_reply, item, next);
|
||||
}
|
||||
|
||||
static void send_will(console_ctx_t *cctx, enum telnet_option opt, bool is_response)
|
||||
{
|
||||
ESP_LOGD(TAG, "Send WILL 0x%02x", opt);
|
||||
if (!is_response) _expect_reply(CODE_WILL, opt);
|
||||
const uint8_t buf[3] = {CODE_IAC, CODE_WILL, opt};
|
||||
console_write_ctx(cctx, (const char *) buf, 3);
|
||||
}
|
||||
|
||||
static void send_wont(console_ctx_t *cctx, enum telnet_option opt, bool is_response)
|
||||
{
|
||||
ESP_LOGD(TAG, "Send WON'T 0x%02x", opt);
|
||||
if (!is_response) _expect_reply(CODE_WONT, opt);
|
||||
const uint8_t buf[3] = {CODE_IAC, CODE_WONT, opt};
|
||||
console_write_ctx(cctx, (const char *) buf, 3);
|
||||
}
|
||||
|
||||
static void send_do(console_ctx_t *cctx, enum telnet_option opt, bool is_response)
|
||||
{
|
||||
ESP_LOGD(TAG, "Send DO 0x%02x", opt);
|
||||
if (!is_response) _expect_reply(CODE_DO, opt);
|
||||
const uint8_t buf[3] = {CODE_IAC, CODE_DO, opt};
|
||||
console_write_ctx(cctx, (const char *) buf, 3);
|
||||
}
|
||||
|
||||
static void send_dont(console_ctx_t *cctx, enum telnet_option opt, bool is_response)
|
||||
{
|
||||
ESP_LOGD(TAG, "Send DON'T 0x%02x", opt);
|
||||
if (!is_response) _expect_reply(CODE_DONT, opt);
|
||||
const uint8_t buf[3] = {CODE_IAC, CODE_DONT, opt};
|
||||
console_write_ctx(cctx, (const char *) buf, 3);
|
||||
}
|
||||
|
||||
static void send_subnegotiate(console_ctx_t *cctx, enum telnet_option opt, const uint8_t *data, size_t len, bool is_response)
|
||||
{
|
||||
if (!is_response) _expect_reply(CODE_SB, opt);
|
||||
ESP_LOGD(TAG, "Send SUBNEG 0x%02x", opt);
|
||||
|
||||
const uint8_t hdr[] = {CODE_IAC, CODE_SB, opt};
|
||||
console_write_ctx(cctx, (const char *) hdr, 3);
|
||||
|
||||
// a heroic attempt at doing the double-IAC encoding without malloc
|
||||
const uint8_t *last_chunk = data;
|
||||
const uint8_t a_iac = CODE_IAC;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (data[i] == CODE_IAC) {
|
||||
if (last_chunk != &data[i]) {
|
||||
console_write_ctx(cctx, (const char *) last_chunk, &data[i] - last_chunk);
|
||||
console_write_ctx(cctx, (const char *) &a_iac, 1);
|
||||
last_chunk = &data[i+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (last_chunk != &data[len]) {
|
||||
console_write_ctx(cctx, (const char *) last_chunk, &data[len] - last_chunk);
|
||||
}
|
||||
|
||||
const uint8_t foot[] = {CODE_IAC, CODE_SE};
|
||||
console_write_ctx(cctx, (const char *) foot, 2);
|
||||
}
|
||||
|
||||
void telnet_send_subnegotiate(console_ctx_t *cctx, enum telnet_option opt, const uint8_t *data, size_t len)
|
||||
{
|
||||
send_subnegotiate(cctx, opt, data, len, false);
|
||||
}
|
||||
|
||||
void telnet_send_will(console_ctx_t *cctx, enum telnet_option opt)
|
||||
{
|
||||
send_will(cctx, opt, false);
|
||||
}
|
||||
|
||||
void telnet_send_wont(console_ctx_t *cctx, enum telnet_option opt)
|
||||
{
|
||||
send_wont(cctx, opt, false);
|
||||
}
|
||||
|
||||
void telnet_send_do(console_ctx_t *cctx, enum telnet_option opt)
|
||||
{
|
||||
send_do(cctx, opt, false);
|
||||
}
|
||||
|
||||
void telnet_send_dont(console_ctx_t *cctx, enum telnet_option opt)
|
||||
{
|
||||
send_dont(cctx, opt, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Peer agreed to DO something,
|
||||
* or announces it WILL do something
|
||||
*/
|
||||
static void handle_will(console_ctx_t *cctx, enum telnet_option opt)
|
||||
{
|
||||
struct awaiting_reply_item *it;
|
||||
SLIST_FOREACH(it, &s_ts.awaiting_reply, next) {
|
||||
if (it->option == opt) {
|
||||
if (it->code == CODE_DO) {
|
||||
ESP_LOGD(TAG, "Peer agreed to DO 0x%02x", opt);
|
||||
} else if (it->code == CODE_DONT) {
|
||||
ESP_LOGW(TAG, "Peer refused to NOT DO 0x%02x", opt);
|
||||
// TODO deal with it
|
||||
} else {
|
||||
continue; // leave this entry alone, it's not related
|
||||
}
|
||||
SLIST_REMOVE(&s_ts.awaiting_reply, it, awaiting_reply_item, next);
|
||||
free(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// unexpected WILL - peer offers to enable an option
|
||||
switch (opt) {
|
||||
case OPT_BINARY:
|
||||
send_do(cctx, opt, true);
|
||||
s_ts.mode_rx_binary = true;
|
||||
break;
|
||||
case OPT_ECHO:
|
||||
send_dont(cctx, opt, true);
|
||||
// that's a bad idea, don't do it
|
||||
break;
|
||||
case OPT_SUPPRESS_GO_AHEAD:
|
||||
send_do(cctx, opt, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGD(TAG, "unknown option for WILL: 0x%02x", opt);
|
||||
// please don't, we don't understand what it is
|
||||
send_dont(cctx, opt, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Peer refused to DO something,
|
||||
* or announces it WON'T do something
|
||||
*/
|
||||
static void handle_wont(console_ctx_t *cctx, enum telnet_option opt)
|
||||
{
|
||||
struct awaiting_reply_item *it;
|
||||
SLIST_FOREACH(it, &s_ts.awaiting_reply, next) {
|
||||
if (it->option == opt) {
|
||||
if (it->code == CODE_DO) {
|
||||
ESP_LOGW(TAG, "Peer refused to DO 0x%02x", opt);
|
||||
// TODO deal with it
|
||||
} else if (it->code == CODE_DONT) {
|
||||
ESP_LOGD(TAG, "Peer agreed to NOT DO 0x%02x", opt);
|
||||
} else {
|
||||
continue; // leave this entry alone, it's not related
|
||||
}
|
||||
SLIST_REMOVE(&s_ts.awaiting_reply, it, awaiting_reply_item, next);
|
||||
free(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// unexpected WON'T - peer offers to disable an option
|
||||
switch (opt) {
|
||||
case OPT_BINARY:
|
||||
send_dont(cctx, opt, true);
|
||||
s_ts.mode_rx_binary = false;
|
||||
break;
|
||||
case OPT_ECHO:
|
||||
send_dont(cctx, opt, true);
|
||||
// OK, we don't care
|
||||
break;
|
||||
case OPT_SUPPRESS_GO_AHEAD:
|
||||
// We want SGA, GA is some legacy nonsense nobody uses
|
||||
send_do(cctx, opt, true);
|
||||
break;
|
||||
|
||||
// TODO handle supported options
|
||||
default:
|
||||
ESP_LOGD(TAG, "unknown option for WON'T: 0x%02x", opt);
|
||||
// yeah sure, don't do it, whatever
|
||||
send_dont(cctx, opt, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Peer requests we DO something,
|
||||
* or agrees to something we announced with WILL
|
||||
*/
|
||||
static void handle_do(console_ctx_t *cctx, enum telnet_option opt)
|
||||
{
|
||||
struct awaiting_reply_item *it;
|
||||
SLIST_FOREACH(it, &s_ts.awaiting_reply, next) {
|
||||
if (it->option == opt) {
|
||||
if (it->code == CODE_WONT) {
|
||||
ESP_LOGW(TAG, "Peer rejected that we WON'T 0x%02x", opt);
|
||||
// TODO deal with it
|
||||
} else if (it->code == CODE_WILL) {
|
||||
ESP_LOGD(TAG, "Peer accepted that we WILL 0x%02x", opt);
|
||||
} else {
|
||||
continue; // leave this entry alone, it's not related
|
||||
}
|
||||
SLIST_REMOVE(&s_ts.awaiting_reply, it, awaiting_reply_item, next);
|
||||
free(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// unexpected DO - peer requests we do something
|
||||
switch (opt) {
|
||||
case OPT_BINARY:
|
||||
send_will(cctx, opt, true);
|
||||
s_ts.mode_tx_binary = true;
|
||||
break;
|
||||
case OPT_ECHO:
|
||||
// FIXME
|
||||
send_wont(cctx, opt, true);
|
||||
// send_will(cctx, opt, true);
|
||||
// linenoiseSetEchoMode(1);
|
||||
break;
|
||||
case OPT_SUPPRESS_GO_AHEAD:
|
||||
send_will(cctx, opt, true);
|
||||
break;
|
||||
case OPT_STATUS:
|
||||
send_will(cctx, opt, true);
|
||||
break;
|
||||
case OPT_TIMING_MARK:
|
||||
send_will(cctx, opt, true);
|
||||
break;
|
||||
|
||||
// TODO handle supported options
|
||||
default:
|
||||
ESP_LOGD(TAG, "unknown option for DO: 0x%02x", opt);
|
||||
// we don't know how to do that, so we won't
|
||||
send_wont(cctx, opt, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Peer requests we DON'T do something,
|
||||
* or agrees we shouldn't do something we announced for with WON'T
|
||||
*/
|
||||
static void handle_dont(console_ctx_t *cctx, enum telnet_option opt)
|
||||
{
|
||||
struct awaiting_reply_item *it;
|
||||
SLIST_FOREACH(it, &s_ts.awaiting_reply, next) {
|
||||
if (it->option == opt) {
|
||||
if (it->code == CODE_WILL) {
|
||||
ESP_LOGW(TAG, "Peer rejected that we WILL 0x%02x", opt);
|
||||
// TODO deal with it
|
||||
|
||||
switch (opt) {
|
||||
case OPT_ECHO:
|
||||
// ok, let's not echo then
|
||||
// FIXME
|
||||
// linenoiseSetEchoMode(0);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Rejection unhandled");
|
||||
}
|
||||
} else if (it->code == CODE_WONT) {
|
||||
ESP_LOGD(TAG, "Peer accepted that we WON'T 0x%02x", opt);
|
||||
} else {
|
||||
continue; // leave this entry alone, it's not related
|
||||
}
|
||||
SLIST_REMOVE(&s_ts.awaiting_reply, it, awaiting_reply_item, next);
|
||||
free(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// unexpected DON'T - peer requests we don't do something
|
||||
switch (opt) {
|
||||
case OPT_BINARY:
|
||||
send_wont(cctx, opt, true);
|
||||
s_ts.mode_tx_binary = false;
|
||||
break;
|
||||
case OPT_ECHO:
|
||||
send_wont(cctx, opt, true);
|
||||
// FIXME
|
||||
// linenoiseSetEchoMode(0);
|
||||
break;
|
||||
case OPT_SUPPRESS_GO_AHEAD:
|
||||
// no, we absolutely won't use GA
|
||||
send_will(cctx, opt, true);
|
||||
break;
|
||||
|
||||
// TODO handle supported options
|
||||
default:
|
||||
ESP_LOGD(TAG, "unknown option for DON'T: 0x%02x", opt);
|
||||
// false is probably the default, so we won't do that, ok...
|
||||
send_wont(cctx, opt, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* subnegotiation command (ts.subneg_option) was received and is now stored in
|
||||
* ts.subneg_buf[0 .. ts.subneg_buf_i]. Handle it accordingly
|
||||
*/
|
||||
static void handle_subnegotiate(void)
|
||||
{
|
||||
struct awaiting_reply_item *it;
|
||||
SLIST_FOREACH(it, &s_ts.awaiting_reply, next) {
|
||||
if (it->code == CODE_SB && it->option == s_ts.subneg_option) {
|
||||
// this is an expected reply, delete the await token
|
||||
SLIST_REMOVE(&s_ts.awaiting_reply, it, awaiting_reply_item, next);
|
||||
ESP_LOGD(TAG, "got reply to SB 0x%02x", s_ts.subneg_option);
|
||||
|
||||
// TODO now we would handle the received data
|
||||
free(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// it was not a reply to our request, i.e. the client is asking for something
|
||||
|
||||
ESP_LOGW(TAG, "recv unsupported subneg request 0x%02x", s_ts.subneg_option);
|
||||
|
||||
// TODO handle some requests..
|
||||
}
|
||||
|
||||
size_t telnet_middleware_read(console_ctx_t *cctx, uint8_t *buffer, size_t len)
|
||||
{
|
||||
ESP_LOGV(TAG, "-- Telnet handle %d chars --", len);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, (uint16_t) len, ESP_LOG_VERBOSE);
|
||||
|
||||
uint8_t *wp = buffer;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t c = buffer[i];
|
||||
|
||||
if (c >= 32 && c < 127) {
|
||||
ESP_LOGV(TAG, "char %d (%c)", c, c);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "char %d", c);
|
||||
}
|
||||
|
||||
switch (s_ts.pstate) {
|
||||
case PS_TEXT:
|
||||
if (c == CODE_IAC) {
|
||||
ESP_LOGV(TAG, "TEXT->IAC");
|
||||
s_ts.pstate = PS_IAC;
|
||||
} else {
|
||||
ESP_LOGV(TAG, "TEXT(%c)", c);
|
||||
*wp++ = c;
|
||||
}
|
||||
break;
|
||||
case PS_IAC:
|
||||
switch (c) {
|
||||
case CODE_IAC:
|
||||
// double IAC means literal IAC (but this shouldn't normally happen)
|
||||
*wp++ = c;
|
||||
s_ts.pstate = PS_TEXT;
|
||||
ESP_LOGV(TAG, "Double IAC in TEXT");
|
||||
break;
|
||||
case CODE_WILL:
|
||||
ESP_LOGV(TAG, "IAC->WILL");
|
||||
s_ts.pstate = PS_WILL;
|
||||
break;
|
||||
case CODE_WONT:
|
||||
ESP_LOGV(TAG, "IAC->WONT");
|
||||
s_ts.pstate = PS_WONT;
|
||||
break;
|
||||
case CODE_DO:
|
||||
ESP_LOGV(TAG, "IAC->DO");
|
||||
s_ts.pstate = PS_DO;
|
||||
break;
|
||||
case CODE_DONT:
|
||||
ESP_LOGV(TAG, "IAC->DONT");
|
||||
s_ts.pstate = PS_DONT;
|
||||
break;
|
||||
case CODE_SB:
|
||||
ESP_LOGV(TAG, "IAC->SUBNEG");
|
||||
s_ts.pstate = PS_SUBNEG;
|
||||
break;
|
||||
case CODE_AYT:
|
||||
ESP_LOGV(TAG, "IAC->AYT");
|
||||
s_ts.pstate = PS_TEXT;
|
||||
const char *reply = "\r\nI am here.\r\n";
|
||||
console_write_ctx(cctx, reply, strlen(reply));
|
||||
break;
|
||||
case CODE_IP:
|
||||
ESP_LOGV(TAG, "IAC->IP, reboot");
|
||||
esp_restart();
|
||||
break;
|
||||
case CODE_BRK:
|
||||
ESP_LOGV(TAG, "IAC->BRK, kick clients");
|
||||
telnetsrv_kick_all();
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unexpected code IAC+%d, discard!", c);
|
||||
// Unecpected character, discard and go to base state
|
||||
s_ts.pstate = PS_TEXT;
|
||||
}
|
||||
break;
|
||||
case PS_WILL:
|
||||
handle_will(cctx, c);
|
||||
s_ts.pstate = PS_TEXT;
|
||||
ESP_LOGV(TAG, "WILL->TEXT");
|
||||
break;
|
||||
case PS_WONT:
|
||||
handle_wont(cctx, c);
|
||||
s_ts.pstate = PS_TEXT;
|
||||
ESP_LOGV(TAG, "WONT->TEXT");
|
||||
break;
|
||||
case PS_DO:
|
||||
handle_do(cctx, c);
|
||||
s_ts.pstate = PS_TEXT;
|
||||
ESP_LOGV(TAG, "DO->TEXT");
|
||||
break;
|
||||
case PS_DONT:
|
||||
handle_dont(cctx, c);
|
||||
s_ts.pstate = PS_TEXT;
|
||||
ESP_LOGV(TAG, "DONT->TEXT");
|
||||
break;
|
||||
case PS_SUBNEG:
|
||||
s_ts.subneg_option = c;
|
||||
s_ts.pstate = PS_SUBNEG_BODY;
|
||||
s_ts.subneg_buf_i = 0;
|
||||
ESP_LOGV(TAG, "SUBNEG->SUBNEG_BODY, opt %d", c);
|
||||
break;
|
||||
case PS_SUBNEG_BODY:
|
||||
if (c == CODE_IAC) {
|
||||
ESP_LOGV(TAG, "IAC in subneg body");
|
||||
s_ts.pstate = PS_SUBNEG_BODY_IAC;
|
||||
} else {
|
||||
if (s_ts.subneg_buf_i < SUBNEG_BUF_LEN) {
|
||||
ESP_LOGV(TAG, "subneg += %d", c);
|
||||
s_ts.subneg_buf[s_ts.subneg_buf_i++] = c;
|
||||
} else {
|
||||
ESP_LOGV(TAG, "subneg too long!");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PS_SUBNEG_BODY_IAC:
|
||||
switch (c) {
|
||||
case CODE_IAC:
|
||||
ESP_LOGV(TAG, "Literal IAC in subneg");
|
||||
// double IAC means literal IAC code
|
||||
s_ts.subneg_buf[s_ts.subneg_buf_i++] = CODE_IAC;
|
||||
s_ts.pstate = PS_SUBNEG_BODY;
|
||||
break;
|
||||
case CODE_SE:
|
||||
ESP_LOGV(TAG, "Subneg END");
|
||||
// end of subneg block
|
||||
if (s_ts.subneg_buf_i == SUBNEG_BUF_LEN) {
|
||||
ESP_LOGW(TAG, "Subneg buf OV, discard command SB %d!", s_ts.subneg_option);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Subneg OK! Handling...");
|
||||
handle_subnegotiate();
|
||||
}
|
||||
s_ts.pstate = PS_TEXT;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGV(TAG, "Illegal char in subneg IAC+%d, discard", c);
|
||||
// illegal character, act like the subneg block was closed, but discard the content
|
||||
s_ts.pstate = PS_TEXT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t remains = wp - buffer;
|
||||
|
||||
ESP_LOGV(TAG, "Remain %d cleartext chars", remains);
|
||||
ESP_LOGV(TAG, "> %*.s", remains, buffer);
|
||||
return remains;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Telnet middleware that handles characters read by console and acts on telnet escapes,
|
||||
* removing them from the stream
|
||||
*
|
||||
* Created on 2019/02/17.
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_TELNET_PARSER_H
|
||||
#define CSPEMU_TELNET_PARSER_H
|
||||
|
||||
#include <console/cmddef.h>
|
||||
|
||||
/**
|
||||
* Process a buffer of received characters, handling and removing telnet codes.
|
||||
* The buffer is modified in place and the number of data characters is returned.
|
||||
*
|
||||
* @param[in,out] buffer - buffer to process
|
||||
* @param len - original number of characters in the buffer
|
||||
* @return - number of characters left in the buffer, to be passed to the console handler
|
||||
*/
|
||||
size_t telnet_middleware_read(console_ctx_t *cctx, uint8_t *buffer, size_t len);
|
||||
|
||||
enum telnet_option {
|
||||
OPT_BINARY = 0,
|
||||
OPT_ECHO = 1,
|
||||
OPT_SUPPRESS_GO_AHEAD = 3,
|
||||
OPT_STATUS = 5,
|
||||
OPT_TIMING_MARK = 6,
|
||||
OPT_TERMINAL_TYPE = 24,
|
||||
OPT_END_OF_RECORD = 25,
|
||||
OPT_NEGOTIATE_WINDOW_SIZE = 31,
|
||||
OPT_NEGOTIATE_TERMINAL_SPEED = 32,
|
||||
OPT_REMOTE_FLOW_CONTROL = 33,
|
||||
// OPT_COMPORT_OPTION = 34, - according to RFC, but seems replaced by...
|
||||
OPT_LINEMODE = 34,
|
||||
OPT_X_DISPLAY_LOCATION = 35,
|
||||
OPT_NEW_ENVIRON = 39,
|
||||
OPT_EXOPL = 255,
|
||||
};
|
||||
|
||||
void telnet_send_subnegotiate(console_ctx_t *cctx, enum telnet_option opt, const uint8_t *data, size_t len);
|
||||
void telnet_send_will(console_ctx_t *cctx, enum telnet_option opt);
|
||||
void telnet_send_wont(console_ctx_t *cctx, enum telnet_option opt);
|
||||
void telnet_send_do(console_ctx_t *cctx, enum telnet_option opt);
|
||||
void telnet_send_dont(console_ctx_t *cctx, enum telnet_option opt);
|
||||
|
||||
#endif //CSPEMU_TELNET_PARSER_H
|
||||
@@ -0,0 +1,9 @@
|
||||
/* gitversion.h. Generated by cmake from gitversion.h.in */
|
||||
#ifndef _GITVERSION_H
|
||||
#define _GITVERSION_H
|
||||
|
||||
#define GIT_COUNT "@_revision_number@"
|
||||
#define GIT_HASH "@_commit_hash@"
|
||||
#define BUILD_TIMESTAMP "@_build_time_stamp@"
|
||||
|
||||
#endif //_GITVERSION_H
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Utilities for heap usage debugging
|
||||
*
|
||||
* Created on 2020/10/23.
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_HEAP_DEBUG_H
|
||||
#define CSPEMU_HEAP_DEBUG_H
|
||||
#include <stdint.h>
|
||||
#include <esp_heap_trace.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
extern uint32_t heapdebug_start;
|
||||
extern const char *heapdebug_start_name;
|
||||
|
||||
#define HEAP_MEASURE_START(msg) \
|
||||
heapdebug_start_name = msg; \
|
||||
ESP_LOGD(TAG, "/-- HEAP_BEGIN: %s", msg); \
|
||||
heapdebug_start = esp_get_free_heap_size();
|
||||
|
||||
#define HEAP_TRACE_START() ESP_LOGD(TAG, "--- Start heap tracing!"); \
|
||||
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
|
||||
#define HEAP_MEASURE_END() \
|
||||
ESP_LOGD(TAG, "\\--- HEAP_END: %s, used %d", \
|
||||
heapdebug_start_name, (int32_t)heapdebug_start - (int32_t)esp_get_free_heap_size());
|
||||
|
||||
#define HEAP_TRACE_END() ESP_LOGD(TAG, "--- End heap tracing!"); \
|
||||
ESP_ERROR_CHECK( heap_trace_stop() ); \
|
||||
heap_trace_dump();
|
||||
|
||||
#endif //CSPEMU_HEAP_DEBUG_H
|
||||
@@ -0,0 +1,101 @@
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#include <console/console.h>
|
||||
#include <console/console_ioimpl.h>
|
||||
#include <esp_heap_trace.h>
|
||||
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "driver/adc.h"
|
||||
#include "esp_adc_cal.h"
|
||||
#include "esp_smartconfig.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "application.h"
|
||||
#include "utils.h"
|
||||
#include "dhcp_wd.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "console/console_server.h"
|
||||
|
||||
#include "web/websrv.h"
|
||||
#include "wifi_conn.h"
|
||||
|
||||
#include "console/register_cmds.h"
|
||||
|
||||
#include "tasks.h"
|
||||
#include "heap_debug.h"
|
||||
|
||||
static const char *TAG = "main";
|
||||
|
||||
//#define NUM_RECORDS 200
|
||||
//static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
|
||||
|
||||
uint32_t heapdebug_start;
|
||||
const char *heapdebug_start_name;
|
||||
|
||||
void app_main(void) {
|
||||
// ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) );
|
||||
|
||||
HEAP_MEASURE_START("NVS,settings,isr,i2c");
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_register_shutdown_handler(cspemu_run_shutdown_handlers));
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
settings_init();
|
||||
settings_load();
|
||||
|
||||
// Start IDF service for pin change interrupts
|
||||
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
||||
HEAP_MEASURE_END();
|
||||
|
||||
HEAP_MEASURE_START("Netif");
|
||||
ESP_LOGD(TAG, "initing netif");
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
HEAP_MEASURE_END();
|
||||
|
||||
if (g_Settings.wifi_enabled && (g_Settings.sta_enabled || g_Settings.ap_enabled)) {
|
||||
HEAP_MEASURE_START("WIFI SETUP");
|
||||
initialise_wifi();
|
||||
HEAP_MEASURE_END();
|
||||
|
||||
HEAP_MEASURE_START("WEBSRV SETUP");
|
||||
websrv_init();
|
||||
HEAP_MEASURE_END();
|
||||
|
||||
g_State.wifi_inited = true;
|
||||
} else {
|
||||
// initialise the bare minimum so wifi config can be changed
|
||||
|
||||
ESP_LOGD(TAG, "initing wifi");
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_LOGD(TAG, "set storage, set sta");
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
|
||||
g_State.wifi_inited = true;
|
||||
}
|
||||
|
||||
HEAP_MEASURE_START("CONSOLE INIT");
|
||||
console_init(NULL);
|
||||
register_console_commands();
|
||||
HEAP_MEASURE_END();
|
||||
|
||||
HEAP_MEASURE_START("CONSOLE start");
|
||||
console_setup_uart_stdio();
|
||||
ESP_ERROR_CHECK(console_start_stdio(NULL, NULL));
|
||||
HEAP_MEASURE_END();
|
||||
|
||||
HEAP_MEASURE_START("Telnet start");
|
||||
telnetsrv_start(CONSOLE_TELNET_PORT);
|
||||
HEAP_MEASURE_END();
|
||||
|
||||
ESP_LOGI(TAG, "Startup finished, free heap = %u, cmds %"PRIu32, esp_get_free_heap_size(), console_count_commands());
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/11/18.
|
||||
//
|
||||
|
||||
#include <nvs.h>
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const char * TAG = "settings.c";
|
||||
|
||||
static uint16_t app_boot_count = 0;
|
||||
|
||||
#undef X
|
||||
#define X(pre,name,post,def,gen_nvs,nvs_format,save_prefix) .name = def,
|
||||
const struct cspemu_settings defaults = {
|
||||
SETTINGS_XTABLE()
|
||||
};
|
||||
|
||||
|
||||
struct cspemu_settings g_Settings;
|
||||
struct cspemu_globals g_State = {
|
||||
.wifi_inited = false,
|
||||
};
|
||||
|
||||
static nvs_handle storage = 0;
|
||||
extern nvs_handle g_nvs_storage __attribute__((alias("storage")));
|
||||
|
||||
void settings_init(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_open("cspemu", NVS_READWRITE, &storage));
|
||||
}
|
||||
|
||||
union fu {
|
||||
float f;
|
||||
uint32_t u;
|
||||
};
|
||||
|
||||
/** Read float from NVS. See `nvs_get_u32` for more info. */
|
||||
static esp_err_t nvs_get_f32 (nvs_handle_t handle, const char* key, float* out_value) {
|
||||
uint32_t u = 0;
|
||||
esp_err_t rv = nvs_get_u32(handle, key, &u);
|
||||
if (rv != ESP_OK) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
union fu reinterpret = {
|
||||
.u = u,
|
||||
};
|
||||
|
||||
*out_value = reinterpret.f;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/** Write float to NVS. See `nvs_set_u32` for more info. */
|
||||
static esp_err_t nvs_set_f32 (nvs_handle_t handle, const char* key, float value) {
|
||||
union fu reinterpret = {
|
||||
.f = value,
|
||||
};
|
||||
return nvs_set_u32(handle, key, reinterpret.u);
|
||||
}
|
||||
|
||||
/** Dummy read from NVS; placeholder for XTABLE entries with manually implemented getters */
|
||||
static esp_err_t nvs_get_none(nvs_handle handle, const char* key, void* out_value) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/** Dummy write to NVS; placeholder for XTABLE entries with manually implemented setters */
|
||||
static esp_err_t nvs_set_none(nvs_handle handle, const char* key, void* value) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void settings_load(void)
|
||||
{
|
||||
esp_err_t rv;
|
||||
memcpy(&g_Settings, &defaults, sizeof(struct cspemu_settings));
|
||||
|
||||
// abort on failure other than "not found"
|
||||
#define NVSCHECK(callback) \
|
||||
do { \
|
||||
rv = callback; \
|
||||
if (rv != ESP_OK && rv != ESP_ERR_NVS_NOT_FOUND) { \
|
||||
ESP_LOGE(TAG, "NVS ERR %d", rv); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
NVSCHECK(nvs_get_u16(storage, "bootcount", &app_boot_count));
|
||||
ESP_LOGI(TAG, "NVS restored bootcount %d", app_boot_count);
|
||||
app_boot_count++;
|
||||
NVSCHECK(nvs_set_u16(storage, "bootcount", app_boot_count));
|
||||
|
||||
|
||||
// version will be used for settings format upgrades
|
||||
uint8_t version = 0;
|
||||
NVSCHECK(nvs_get_u8(storage, "version", &version));
|
||||
|
||||
#undef X
|
||||
#define X(pre, name, post, def, gen_nvs, nvs_format, save_prefix) \
|
||||
if (gen_nvs) { \
|
||||
NVSCHECK(nvs_get_##nvs_format(storage, STR(name), save_prefix g_Settings. name)); \
|
||||
}
|
||||
|
||||
SETTINGS_XTABLE()
|
||||
|
||||
// custom values
|
||||
|
||||
#define ensure_str_terminated(str, len) if (strnlen((str), (len)) >= (len)) { (str)[(len)-1] = 0; }
|
||||
|
||||
{
|
||||
size_t capacity = CONSOLE_PW_LEN;
|
||||
NVSCHECK(nvs_get_str(storage, "tcpp_pw", g_Settings.console_pw, &capacity));
|
||||
ensure_str_terminated(g_Settings.console_pw, CONSOLE_PW_LEN);
|
||||
}
|
||||
{
|
||||
size_t capacity = NTP_SRV_LEN;
|
||||
NVSCHECK(nvs_get_str(storage, "ntp_srv", g_Settings.ntp_srv, &capacity));
|
||||
ensure_str_terminated(g_Settings.ntp_srv, NTP_SRV_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
void settings_persist(enum settings_key_enum what)
|
||||
{
|
||||
esp_err_t rv;
|
||||
char name[24];
|
||||
|
||||
#undef NVSCHECK
|
||||
#define NVSCHECK(callback) \
|
||||
do { \
|
||||
rv = (callback); \
|
||||
if (rv != ESP_OK) { \
|
||||
ESP_LOGE(TAG, "NVS ERR %d", rv); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
#undef X
|
||||
#define X(pre,name,post,def,gen_nvs,nvs_format,save_prefix) \
|
||||
if ((gen_nvs) && ((what)==SETTINGS_ALL || (what)==SETTINGS_## name)) { \
|
||||
NVSCHECK(nvs_set_##nvs_format(storage, STR(name), g_Settings. name)); \
|
||||
}
|
||||
|
||||
SETTINGS_XTABLE()
|
||||
|
||||
// custom values
|
||||
if (what==SETTINGS_ALL || what==SETTINGS_console_pw) {
|
||||
NVSCHECK(nvs_set_str(storage, "tcpp_pw", g_Settings.console_pw));
|
||||
}
|
||||
|
||||
if (what==SETTINGS_ALL || what==SETTINGS_ntp_srv) {
|
||||
NVSCHECK(nvs_set_str(storage, "ntp_srv", g_Settings.ntp_srv));
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t app_get_bootcount()
|
||||
{
|
||||
return app_boot_count;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/11/18.
|
||||
//
|
||||
|
||||
#ifndef CSPEMU_SETTINGS_H
|
||||
#define CSPEMU_SETTINGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <nvs.h>
|
||||
#include <driver/uart.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#define CONSOLE_TELNET_PORT CONFIG_CONSOLE_TELNET_PORT
|
||||
#define CONSOLE_PW_LEN CONFIG_CONSOLE_PW_LEN
|
||||
#define USE_CAPTIVE_PORTAL 0
|
||||
|
||||
extern nvs_handle g_nvs_storage;
|
||||
|
||||
#define NTP_SRV_LEN 32
|
||||
#define DEF_NTP_SRV "pool.ntp.org"
|
||||
|
||||
/* type , name , suffix , defval , generate load/save funcs
|
||||
* , , save fn - use 'none' if unused to avoid build errors
|
||||
* , , , save fn var prefix */
|
||||
#define SETTINGS_XTABLE() \
|
||||
X(bool , wifi_enabled , , 1 , true , bool , &) /* ssid and pass are stored in flash by SDK funcs */ \
|
||||
X(bool , sta_enabled , , 1 , true , bool , &) \
|
||||
X(bool , ap_enabled , , 0 , true , bool , &) \
|
||||
X(bool , console_echo , , 1 , true , bool , &) /* Console modes */ \
|
||||
X(bool , console_ansi , , 1 , true , bool , &) \
|
||||
X(char , console_pw, [CONSOLE_PW_LEN], "" , false , none , ) \
|
||||
X(bool , ntp_enable , , 1 , true , bool , &) \
|
||||
X(char , ntp_srv ,[NTP_SRV_LEN], DEF_NTP_SRV , false, none , ) \
|
||||
X(bool , dhcp_wd_enable , , 1 , true , bool , &) \
|
||||
X(bool , dhcp_enable , , 1 , true , bool , &) \
|
||||
X(uint32_t , static_ip , , 0 , true , u32 , &) \
|
||||
X(uint32_t , static_ip_mask , , 0x00ffffff , true , u32 , &) /* 255.255.255.0 */ \
|
||||
X(uint32_t , static_ip_gw , , 0x0100a8c0 , true , u32 , &) /* 192.168.0.1 */ \
|
||||
X(uint32_t , ap_ip , , 0x0100a8c0 , true , u32 , &) /* 192.168.0.1 */ \
|
||||
X(uint32_t , static_dns , , 0x08080808 , true , u32 , &) /* 8.8.8.8 */
|
||||
|
||||
enum settings_key_enum {
|
||||
#undef X
|
||||
#define X(pre,name,post,def,gen_nvs,nvs_format,save_prefix) \
|
||||
SETTINGS_##name,
|
||||
|
||||
SETTINGS_ALL,
|
||||
SETTINGS_XTABLE() // unfortunately the last bit is lowercase
|
||||
};
|
||||
|
||||
/**
|
||||
* Init the NVS namespace
|
||||
*/
|
||||
void settings_init(void);
|
||||
|
||||
/**
|
||||
* Load settings from the NVS
|
||||
*/
|
||||
void settings_load(void);
|
||||
|
||||
/**
|
||||
* Save the settings object to NVS
|
||||
*/
|
||||
void settings_persist(enum settings_key_enum what);
|
||||
|
||||
/**
|
||||
* Load default rtable into the settings struct
|
||||
* (called when the saved config is rejected by CSP)
|
||||
*/
|
||||
void settings_load_default_rtable(void);
|
||||
|
||||
/**
|
||||
* Save the current CSP rtable into the settings object (can then be persisted)
|
||||
*/
|
||||
void cspemu_settings_store_rtable(void);
|
||||
|
||||
extern struct cspemu_settings g_Settings;
|
||||
extern struct cspemu_globals g_State;
|
||||
|
||||
|
||||
struct cspemu_globals {
|
||||
bool wifi_inited;
|
||||
};
|
||||
|
||||
struct cspemu_settings {
|
||||
#undef X
|
||||
|
||||
#define X(pre,name,post,def,gen_nvs,nvs_format,save_prefix) \
|
||||
pre name post;
|
||||
|
||||
SETTINGS_XTABLE()
|
||||
};
|
||||
|
||||
uint16_t app_get_bootcount();
|
||||
|
||||
#endif //CSPEMU_SETTINGS_H
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "esp_log.h"
|
||||
#include "shutdown_handlers.h"
|
||||
|
||||
static const char *TAG = "shutdown_hdl";
|
||||
|
||||
#define MAX_SHUTDOWN_HANDLERS 10
|
||||
static int shutdown_handlers_next = 0;
|
||||
static shutdown_handler_t shutdown_handlers[MAX_SHUTDOWN_HANDLERS];
|
||||
|
||||
// ---------------------------
|
||||
|
||||
esp_err_t cspemu_add_shutdown_handler(shutdown_handler_t handler)
|
||||
{
|
||||
ESP_LOGI(TAG, "+ new shutdown handler %p", handler);
|
||||
if (shutdown_handlers_next < MAX_SHUTDOWN_HANDLERS) {
|
||||
shutdown_handlers[shutdown_handlers_next++] = handler;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
void cspemu_run_shutdown_handlers(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Running shutdown handlers");
|
||||
for (int i = 0; i < shutdown_handlers_next; i++) {
|
||||
shutdown_handlers[i]();
|
||||
}
|
||||
// prevent them being run multiple times
|
||||
shutdown_handlers_next = 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* TODO file description
|
||||
*
|
||||
* Created on 2020/04/02.
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_SHUTDOWN_HANDLERS_H
|
||||
#define CSPEMU_SHUTDOWN_HANDLERS_H
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
esp_err_t cspemu_add_shutdown_handler(shutdown_handler_t handler);
|
||||
|
||||
void cspemu_run_shutdown_handlers(void);
|
||||
|
||||
#endif //CSPEMU_SHUTDOWN_HANDLERS_H
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "sntp_cli.h"
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "esp_log.h"
|
||||
#include "tasks.h"
|
||||
#include "utils.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/apps/sntp.h"
|
||||
#include "settings.h"
|
||||
|
||||
static const char * TAG = "sntp_cli";
|
||||
|
||||
static volatile bool sntp_running = false;
|
||||
|
||||
static void vTaskSNTPclient(void * pvParameters)
|
||||
{
|
||||
(void)pvParameters; //UN-USED
|
||||
ESP_LOGI(TAG, "Starting SNTP client, server %s", g_Settings.ntp_srv);
|
||||
vTaskDelay(configTICK_RATE_HZ);
|
||||
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, g_Settings.ntp_srv);
|
||||
sntp_init();
|
||||
vTaskDelay(2*configTICK_RATE_HZ);
|
||||
|
||||
time_t now;
|
||||
struct tm timeinfo;
|
||||
int retry = 0;
|
||||
const int retry_count = 10;
|
||||
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
|
||||
while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) {
|
||||
ESP_LOGD(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
|
||||
vTaskDelay(3*configTICK_RATE_HZ);
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
}
|
||||
|
||||
if(timeinfo.tm_year < (2016 - 1900)) {
|
||||
ESP_LOGW(TAG, "SNTP client failed to get time!");
|
||||
} else {
|
||||
char strftime_buf[64];
|
||||
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
|
||||
ESP_LOGI(TAG, "The current UTC date/time is: %s, SNTP client done", strftime_buf);
|
||||
}
|
||||
|
||||
sntp_stop();
|
||||
sntp_running = false;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
bool sntp_cli_start()
|
||||
{
|
||||
if (sntp_running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sntp_running = true;
|
||||
RTOS_ERROR_CHECK(xTaskCreate(vTaskSNTPclient, "SNTPc", 4096, NULL, PRIO_LOW, NULL));
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef SNTP_CLI_H
|
||||
#define SNTP_CLI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/** Start the SNTP client. Returns true if started, false if already running. */
|
||||
bool sntp_cli_start();
|
||||
|
||||
#endif // SNTP_CLI_H
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/12/16.
|
||||
//
|
||||
|
||||
#ifndef CSPEMU_PRIORITIES_H
|
||||
#define CSPEMU_PRIORITIES_H
|
||||
|
||||
#define PRIO_NORMAL 4
|
||||
#define PRIO_LOW 2
|
||||
#define PRIO_HIGH 6
|
||||
|
||||
#define CONSOLE_TASK_STACK 4096
|
||||
#define CONSOLE_TASK_PRIO PRIO_NORMAL
|
||||
|
||||
#define TELNET_TASK_STACK 4096
|
||||
#define TELNET_TASK_PRIO PRIO_LOW
|
||||
|
||||
#endif //CSPEMU_PRIORITIES_H
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "utils.h"
|
||||
#include "application.h"
|
||||
|
||||
static const char * TAG = "utils.c";
|
||||
|
||||
static void recon_internal(void);
|
||||
|
||||
/**
|
||||
* Attempt to reconnect to AP if we have SSID stored
|
||||
*/
|
||||
void try_reconn_if_have_wifi_creds(void)
|
||||
{
|
||||
recon_internal();
|
||||
}
|
||||
|
||||
static volatile bool recurse = false;
|
||||
|
||||
static void recon_internal(void)
|
||||
{
|
||||
if (recurse) {
|
||||
ESP_LOGE(TAG, "Stopping recursion!");
|
||||
}
|
||||
recurse = true;
|
||||
|
||||
wifi_config_t wificonf;
|
||||
|
||||
// try to connect if we have a saved config
|
||||
ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_STA, &wificonf));
|
||||
|
||||
if (wificonf.sta.ssid[0]) {
|
||||
ESP_LOGI(TAG, "(Re)connecting using saved STA creds");
|
||||
ESP_ERROR_CHECK(esp_wifi_connect());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "No WiFi creds, no (re)conn");
|
||||
}
|
||||
|
||||
recurse = false;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t nvs_get_bool(nvs_handle handle, const char* key, bool* out_value)
|
||||
{
|
||||
uint8_t x = (uint8_t) *out_value;
|
||||
esp_err_t rv = nvs_get_u8(handle, key, &x);
|
||||
if (rv == ESP_OK) {
|
||||
*out_value = (bool)x;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
esp_err_t nvs_set_bool(nvs_handle handle, const char* key, bool value)
|
||||
{
|
||||
return nvs_set_u8(handle, key, (uint8_t) value);
|
||||
}
|
||||
|
||||
void feed_all_dogs(void)
|
||||
{
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
|
||||
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Utilities and helpers
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_UTILS_H
|
||||
#define CSPEMU_UTILS_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <string.h>
|
||||
#include <nvs.h>
|
||||
#include <common_utils/utils.h>
|
||||
|
||||
/** Error check macro for FreeRTOS status codes */
|
||||
#define RTOS_ERROR_CHECK(code) if (pdPASS != (code)) ESP_ERROR_CHECK(ESP_FAIL);
|
||||
|
||||
/** Error check macro for NULL return value */
|
||||
#define NULL_CHECK(code) if (NULL == (code)) ESP_ERROR_CHECK(ESP_FAIL);
|
||||
|
||||
/** Error check macro for CSP status codes */
|
||||
#define CSP_ERROR_CHECK(code) if (CSP_ERR_NONE != (code)) ESP_ERROR_CHECK(ESP_FAIL);
|
||||
|
||||
/**
|
||||
* Reconnect to WiFi if there are saved credentials.
|
||||
* Called from the main event group handler.
|
||||
*/
|
||||
void try_reconn_if_have_wifi_creds(void);
|
||||
|
||||
/**
|
||||
* Helper to retrieve a bool value from the NVS. Internally uses 'u8'
|
||||
*
|
||||
* @param handle - NVS storage
|
||||
* @param key
|
||||
* @param out_value
|
||||
* @return success
|
||||
*/
|
||||
esp_err_t nvs_get_bool(nvs_handle handle, const char* key, bool* out_value);
|
||||
|
||||
/**
|
||||
* Helper to store a bool into the NVS. Internally uses 'u8'
|
||||
*
|
||||
* @param handle - NVS storage
|
||||
* @param key
|
||||
* @param value
|
||||
* @return success
|
||||
*/
|
||||
esp_err_t nvs_set_bool(nvs_handle handle, const char* key, bool value);
|
||||
|
||||
/**
|
||||
* Feed watchdogs (inside a busy wait loop)
|
||||
*/
|
||||
void feed_all_dogs(void);
|
||||
|
||||
/**
|
||||
* @brief malloc() and snprintf() combined
|
||||
* @attention DO NOT use in if/for/do etc without braces.
|
||||
*
|
||||
* The caller is responsible for disposing of the allocated string afterwards.
|
||||
*/
|
||||
#define malloc_sprintf(var, n, format, ...) \
|
||||
char *var = calloc(n, 1); \
|
||||
if (!var) assert(0); \
|
||||
snprintf(var, n, format, ##__VA_ARGS__);
|
||||
|
||||
/**
|
||||
* Generate externs for an embedded file.
|
||||
* Variables {varname} and {varname}_end will be produced.
|
||||
*/
|
||||
#define efile(varname, filename) \
|
||||
extern const char varname[] asm("_binary_"filename"_start"); \
|
||||
extern const char varname##_end[] asm("_binary_"filename"_end");
|
||||
|
||||
/** Get embedded file size (must be declared with efile() first) */
|
||||
#define efsize(varname) ((varname##_end) - (varname))
|
||||
|
||||
#endif //CSPEMU_UTILS_H
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
#include <fileserver/token_subs.h>
|
||||
#include <application.h>
|
||||
#include <httpd_utils/captive.h>
|
||||
|
||||
#include "websrv.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "www_files_enum.h"
|
||||
|
||||
static const char *TAG="websrv";
|
||||
|
||||
static httpd_handle_t s_hServer = NULL;
|
||||
|
||||
// Embedded files (must also be listed in CMakeLists.txt as COMPONENT_EMBED_TXTFILES)
|
||||
efile(index_file, "index_html");
|
||||
|
||||
static struct tpl_kv_list build_index_replacements_kv(void)
|
||||
{
|
||||
//char name[TPL_KV_KEY_LEN];
|
||||
struct tpl_kv_list kv = tpl_kv_init();
|
||||
tpl_kv_add(&kv, "version", APP_VERSION);
|
||||
return kv;
|
||||
}
|
||||
|
||||
/* Main page */
|
||||
static esp_err_t handler_index(httpd_req_t *req)
|
||||
{
|
||||
struct tpl_kv_list kv = build_index_replacements_kv();
|
||||
|
||||
esp_err_t suc = httpd_send_template_file(req, FILE_INDEX_HTML, tpl_kv_replacer, &kv, 0);
|
||||
tpl_kv_free(&kv);
|
||||
return suc;
|
||||
}
|
||||
|
||||
/* Update XHR for new index page data */
|
||||
static esp_err_t handler_update(httpd_req_t *req)
|
||||
{
|
||||
struct tpl_kv_list kv = build_index_replacements_kv();
|
||||
|
||||
esp_err_t suc = tpl_kv_send_as_ascii_map(req, &kv);
|
||||
tpl_kv_free(&kv);
|
||||
return suc;
|
||||
}
|
||||
|
||||
/* Set a param */
|
||||
static esp_err_t handler_set(httpd_req_t *req)
|
||||
{
|
||||
char buf[64];
|
||||
int n = httpd_req_recv(req, buf, 63);
|
||||
if (n < 0) {
|
||||
ESP_LOGW(TAG, "rx er");
|
||||
goto err;
|
||||
}
|
||||
buf[n]=0;
|
||||
|
||||
char keybuf[20];
|
||||
char valbuf[20];
|
||||
if (ESP_OK != httpd_query_key_value(buf, "key", keybuf, 20)) goto err;
|
||||
if (ESP_OK != httpd_query_key_value(buf, "value", valbuf, 20)) goto err;
|
||||
|
||||
// TODO
|
||||
ESP_LOGW(TAG, "bad key %s", keybuf);
|
||||
goto err;
|
||||
|
||||
return httpd_resp_send(req, NULL, 0);
|
||||
|
||||
err:
|
||||
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, NULL);
|
||||
}
|
||||
|
||||
/* Request emulator reboot */
|
||||
static esp_err_t handler_reboot(httpd_req_t *req)
|
||||
{
|
||||
httpd_resp_send(req, "<!DOCTYPE html><html><head>"
|
||||
"<meta http-equiv=\"refresh\" content=\"10; url=/\">"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"Reboot requested. Reloading in 10 seconds.<br>"
|
||||
"<a href=\"/\">Try now.</a>", -1);
|
||||
|
||||
ESP_LOGI(TAG, "Restarting ESP...");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
/* An HTTP GET handler */
|
||||
static esp_err_t handler_staticfiles(httpd_req_t *r)
|
||||
{
|
||||
const struct embedded_file_info *file;
|
||||
const char *fname = r->user_ctx;
|
||||
enum file_access_level access = FILE_ACCESS_PROTECTED;
|
||||
|
||||
// wildcard files must be public
|
||||
if (fname == NULL) {
|
||||
fname = r->uri + 1; // URI always starts with slash, but we dont want a slash in the file name
|
||||
access = FILE_ACCESS_PUBLIC;
|
||||
}
|
||||
|
||||
#if USE_CAPTIVE_PORTAL
|
||||
// First check if this is a phone taken here by the captive portal
|
||||
esp_err_t rv = httpd_captive_redirect(r);
|
||||
if (rv != ESP_ERR_NOT_FOUND) return rv;
|
||||
#endif
|
||||
|
||||
if (ESP_OK != www_get_static_file(fname, access, &file)) {
|
||||
ESP_LOGW(TAG, "File not found: %s", fname);
|
||||
return httpd_resp_send_404(r);
|
||||
}
|
||||
else {
|
||||
if (streq(file->mime, "text/html")) {
|
||||
// using the template func to allow includes
|
||||
return httpd_send_template_file_struct(r, file, /*replacer*/NULL, /*ctx*/NULL, /*opts*/0);
|
||||
} else {
|
||||
return httpd_send_static_file_struct(r, file, TPL_ESCAPE_NONE, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const httpd_uri_t routes[] = {
|
||||
{
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = handler_index,
|
||||
},
|
||||
{
|
||||
.uri = "/data",
|
||||
.method = HTTP_GET,
|
||||
.handler = handler_update,
|
||||
},
|
||||
{
|
||||
.uri = "/set",
|
||||
.method = HTTP_POST,
|
||||
.handler = handler_set,
|
||||
},
|
||||
{
|
||||
.uri = "/reboot",
|
||||
.method = HTTP_GET,
|
||||
.handler = handler_reboot,
|
||||
},
|
||||
{
|
||||
.uri = "*", // any file except protected (e.g. not HTML, PEM etc)
|
||||
.method = HTTP_GET,
|
||||
.handler = handler_staticfiles,
|
||||
},
|
||||
};
|
||||
|
||||
esp_err_t websrv_init(void)
|
||||
{
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.max_open_sockets = 3;
|
||||
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
config.lru_purge_enable = true;
|
||||
|
||||
ESP_LOGI(TAG, "Starting HTTP server on port: '%d'", config.server_port);
|
||||
|
||||
esp_err_t suc = httpd_start(&s_hServer, &config);
|
||||
if (suc == ESP_OK) {
|
||||
ESP_LOGI(TAG, "HTTP server started");
|
||||
|
||||
for (int i = 0; i < sizeof(routes) / sizeof(httpd_uri_t); i++) {
|
||||
httpd_register_uri_handler(s_hServer, &routes[i]);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Error starting server!");
|
||||
return suc;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Integrated webserver
|
||||
*
|
||||
* Created on 2019/07/13.
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_WEBSRV_H
|
||||
#define CSPEMU_WEBSRV_H
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
esp_err_t websrv_init(void);
|
||||
|
||||
#endif //CSPEMU_WEBSRV_H
|
||||
@@ -0,0 +1,190 @@
|
||||
#include <nvs_flash.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "wifi_conn.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_types.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "sntp_cli.h"
|
||||
|
||||
#include "application.h"
|
||||
#include "dhcp_wd.h"
|
||||
|
||||
#define WIFI_MAX_RETRY 5
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected*/
|
||||
EventGroupHandle_t g_wifi_event_group;
|
||||
|
||||
static const char *TAG = "wifi station";
|
||||
|
||||
static int s_retry_num = 0;
|
||||
static dhcp_wd_handle_t dhcp_wd = NULL;
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
ESP_LOGI(TAG, "STA disconnected");
|
||||
xEventGroupClearBits(g_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
|
||||
if (dhcp_wd) {
|
||||
dhcp_watchdog_notify(dhcp_wd, DHCP_WD_NOTIFY_DISCONNECTED);
|
||||
}
|
||||
|
||||
if (s_retry_num < WIFI_MAX_RETRY) {
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_LOGI(TAG, "Retry to connect (try %d)", s_retry_num+1);
|
||||
} else {
|
||||
xEventGroupSetBits(g_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
} else if (event_base == IP_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
|
||||
if (dhcp_wd) {
|
||||
dhcp_watchdog_notify(dhcp_wd, DHCP_WD_NOTIFY_CONNECTED);
|
||||
}
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
s_retry_num = 0;
|
||||
xEventGroupSetBits(g_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
|
||||
if (g_Settings.ntp_enable) {
|
||||
sntp_cli_start();
|
||||
}
|
||||
|
||||
if (dhcp_wd) {
|
||||
dhcp_watchdog_notify(dhcp_wd, DHCP_WD_NOTIFY_GOT_IP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool wifi_wait_connect()
|
||||
{
|
||||
s_retry_num = 0;
|
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
|
||||
EventBits_t bits = xEventGroupWaitBits(g_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
|
||||
* happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "Connected to AP!");
|
||||
return true;
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
ESP_LOGI(TAG, "Failed to connect.");
|
||||
return false;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UNEXPECTED EVENT");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void initialise_wifi(void)
|
||||
{
|
||||
esp_err_t rv = ESP_OK;
|
||||
|
||||
#define ERROR_CHECK(x) do { if (ESP_OK != (rv = (x))) goto fail; } while (0)
|
||||
|
||||
assert(g_Settings.sta_enabled || g_Settings.ap_enabled);
|
||||
|
||||
g_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_LOGD(TAG, "create wifi netif");
|
||||
esp_netif_t *netif_sta = NULL;
|
||||
if (g_Settings.sta_enabled) {
|
||||
netif_sta = esp_netif_create_default_wifi_sta();
|
||||
}
|
||||
if (g_Settings.ap_enabled) {
|
||||
esp_netif_create_default_wifi_ap();
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "initing wifi");
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_LOGD(TAG, "set storage, set sta");
|
||||
ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
|
||||
|
||||
wifi_mode_t mode;
|
||||
if (g_Settings.sta_enabled && g_Settings.ap_enabled) {
|
||||
mode = WIFI_MODE_APSTA;
|
||||
} else if (g_Settings.sta_enabled) {
|
||||
mode = WIFI_MODE_STA;
|
||||
} else {
|
||||
// This function is never called if both STA and AP are disabled!
|
||||
mode = WIFI_MODE_AP;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "set wifi mode");
|
||||
ERROR_CHECK(esp_wifi_set_mode(mode));
|
||||
|
||||
if (g_Settings.ap_enabled) {
|
||||
ESP_LOGD(TAG, "set AP IP config");
|
||||
|
||||
tcpip_adapter_ip_info_t ip_info;
|
||||
ERROR_CHECK(tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP));
|
||||
|
||||
memset(&ip_info, 0, sizeof(ip_info));
|
||||
ip_info.ip.addr = g_Settings.ap_ip;
|
||||
ip_info.gw.addr = g_Settings.ap_ip;
|
||||
ip_info.netmask.addr = 0x00ffffff;
|
||||
|
||||
ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_AP, &ip_info));
|
||||
ERROR_CHECK(tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP));
|
||||
}
|
||||
|
||||
const bool use_dhcp = (g_Settings.dhcp_enable || g_Settings.static_ip == 0)
|
||||
&& g_Settings.sta_enabled;
|
||||
|
||||
if (g_Settings.sta_enabled) {
|
||||
if (!use_dhcp) {
|
||||
ESP_LOGD(TAG, "set STA static IP");
|
||||
|
||||
tcpip_adapter_ip_info_t ip_info;
|
||||
ERROR_CHECK(tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA));
|
||||
|
||||
memset(&ip_info, 0, sizeof(ip_info));
|
||||
ip_info.ip.addr = g_Settings.static_ip;
|
||||
ip_info.gw.addr = g_Settings.static_ip_gw;
|
||||
ip_info.netmask.addr = g_Settings.static_ip_mask;
|
||||
ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));
|
||||
|
||||
tcpip_adapter_dns_info_t dnsinfo = {
|
||||
// special esp version of ip address because why not I guess.
|
||||
// ipv4 has union tag 0, so it needn't be set
|
||||
.ip.u_addr.ip4.addr = g_Settings.static_dns,
|
||||
};
|
||||
|
||||
ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, ESP_NETIF_DNS_MAIN, &dnsinfo));
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "register WiFi events");
|
||||
ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "wifi start");
|
||||
ERROR_CHECK(esp_wifi_start() );
|
||||
|
||||
ESP_LOGI(TAG, "WiFi config done");
|
||||
|
||||
if (g_Settings.dhcp_wd_enable && use_dhcp) {
|
||||
ESP_LOGD(TAG, "Start gateway ping watchdog");
|
||||
ERROR_CHECK(dhcp_watchdog_start(netif_sta, true, &dhcp_wd));
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
ESP_LOGE(TAG, "Error setting up WiFi: %d - %s", rv, esp_err_to_name(rv));
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Handle WiFi connection
|
||||
*
|
||||
* Created on 2020/04/02.
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_WIFI_CONN_H
|
||||
#define CSPEMU_WIFI_CONN_H
|
||||
|
||||
void initialise_wifi(void);
|
||||
bool wifi_wait_connect();
|
||||
|
||||
#endif //CSPEMU_WIFI_CONN_H
|
||||
Reference in New Issue
Block a user