Merge branch 'work'

new-codepages
Ondřej Hruška 7 years ago
commit 274e3b359b
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 1
      .gitignore
  2. 43
      CMakeLists.txt
  3. 15
      Makefile
  4. 61
      esphttpdconfig.mk
  5. 3
      esphttpdconfig.mk.example
  6. 2
      front-end
  7. 14
      get_version.c
  8. 1
      include/user_config.h
  9. 2
      libesphttpd
  10. 21
      rel-tpl/README.txt
  11. 26
      rel-tpl/flash.sh
  12. 3
      release/.gitignore
  13. 69
      ship.sh
  14. 76
      user/ansi_parser.c
  15. 5
      user/ansi_parser.h
  16. 26
      user/ansi_parser.rl
  17. 35
      user/ansi_parser_callbacks.c
  18. 6
      user/apars_csi.c
  19. 8
      user/apars_dcs.c
  20. 2
      user/apars_dcs.h
  21. 34
      user/apars_pm.c
  22. 10
      user/apars_pm.h
  23. 7
      user/apars_short.c
  24. 4
      user/apars_string.c
  25. 192
      user/apars_utf8.c
  26. 15
      user/api.h
  27. 324
      user/cgi_d2d.c
  28. 25
      user/cgi_d2d.h
  29. 21
      user/cgi_main.c
  30. 130
      user/cgi_sockets.c
  31. 2
      user/cgi_sockets.h
  32. 4
      user/cgi_system.c
  33. 63
      user/cgi_term_cfg.c
  34. 17
      user/cgi_wifi.c
  35. 22
      user/routes.c
  36. 3
      user/routes.h
  37. 662
      user/screen.c
  38. 79
      user/screen.h
  39. 14
      user/serial.c
  40. 85
      user/uart_buffer.c
  41. 6
      user/uart_buffer.h
  42. 9
      user/uart_handler.c
  43. 53
      user/user_main.c
  44. 7
      user/utf8.c
  45. 3
      user/utf8.h
  46. 19
      user/version.h
  47. 16
      user/wifimgr.c
  48. 2
      user/wifimgr.h

1
.gitignore vendored

@ -20,3 +20,4 @@ cmake-build-debug/
.sass-cache
*.map
.gitignore

@ -56,6 +56,8 @@ set(SOURCE_FILES
libesphttpd/util/cgiwebsocket.c
libesphttpd/util/cgiflash.c
libesphttpd/util/captdns.c
libesphttpd/esphttpclient/httpclient.c
libesphttpd/include/httpclient.h
esp_iot_sdk_v1.5.2/include/user_interface.h
esp_iot_sdk_v1.5.2/include/upgrade.h
@ -85,9 +87,9 @@ set(SOURCE_FILES
esp_iot_sdk_v1.5.2/include/json/jsontree.h
esp_iot_sdk_v1.5.2/include/json/jsonparse.h
esp_iot_sdk_v1.5.2/include/json/json.h
include/user_config.h
include/ets_sys_extra.h
include/helpers.h
user/io.c
user/io.h
user/cgi_wifi.c
@ -121,7 +123,6 @@ set(SOURCE_FILES
user/wifimgr.h
user/persist.c
user/persist.h
include/helpers.h
user/syscfg.c
user/syscfg.h
user/ascii.h
@ -139,9 +140,18 @@ set(SOURCE_FILES
user/apars_osc.c
user/apars_osc.h
user/apars_dcs.c
user/apars_dcs.h user/uart_buffer.c user/uart_buffer.h user/jstring.c user/jstring.h user/character_sets.h user/utf8.h user/utf8.c user/cgi_logging.h)
user/apars_dcs.h
user/uart_buffer.c
user/uart_buffer.h
user/jstring.c
user/jstring.h
user/character_sets.h
user/utf8.h
user/utf8.c
user/cgi_logging.h)
include_directories(include)
include_directories(libesphttpd/esphttpclient)
include_directories(user)
include_directories(libesphttpd/include)
include_directories(libesphttpd/espfs)
@ -160,13 +170,34 @@ add_definitions(
-DICACHE_FLASH_ATTR=
-DICACHE_RODATA_ATTR=
-DFLAG_GZIP=2
-DADMIN_PASSWORD="asdf"
-DESP_LANG="en"
-DGIT_HASH_BACKEND="asdf"
-DGIT_HASH_FRONTEND="asdf"
-DGIT_HASH="blabla"
-D__TIMEZONE__="UTC"
-DESPFS_HEATSHRINK
-DDEBUG_ANSI=1
-DDEBUG_ANSI_NOIMPL=1
-DDEBUG_CAPTDNS=1
-DDEBUG_CGI=0
-DDEBUG_ESPFS=1
-DDEBUG_HEAP=1
-DDEBUG_HTTP=1
-DDEBUG_HTTPC=1
-DDEBUG_INPUT=1
-DDEBUG_MALLOC=1
-D__TIMEZONE__="UTC"
-DESPFS_HEATSHRINK)
-DDEBUG_PERSIST=1
-DDEBUG_ROUTER=1
-DDEBUG_UTFCACHE=1
-DDEBUG_WIFI=1
-DDEBUG_WS=1
-DDEBUG_ROUTER=1
)
# all the debug keys should be listed here ^ so clion thinks they are used
# and doesn't mess up the formatting and inspections
add_executable(ESPTerm ${SOURCE_FILES})

@ -62,6 +62,10 @@ LIBS = c gcc hal phy pp net80211 wpa main lwip crypto
#Add in esphttpd lib
LIBS += esphttpd
ifndef ESP_LANG
ESP_LANG = en
endif
# compiler flags using during compilation of source files -ggdb
CFLAGS = -Os -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
-nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \
@ -69,7 +73,7 @@ CFLAGS = -Os -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inli
CFLAGS += -DGIT_HASH_BACKEND='"$(shell git rev-parse --short HEAD)"'
CFLAGS += -DGIT_HASH_FRONTEND='"$(shell cd front-end && git rev-parse --short HEAD)"'
CFLAGS += -D__TIMEZONE__='"$(shell date +%Z)"'
CFLAGS += -D__TIMEZONE__='"$(shell date +%Z)"' -DESP_LANG='"$(ESP_LANG)"'
ifdef GLOBAL_CFLAGS
CFLAGS += $(GLOBAL_CFLAGS)
@ -194,6 +198,10 @@ endef
web:
$(Q) ./build_web.sh
updweb:
$(Q) cd front-end && git pull
$(Q) ./build_web.sh
parser:
$(Q) ./build_parser.sh
@ -206,13 +214,16 @@ espmac:
all: checkdirs
$(Q) make actual_all -j4 -B
release:
$(Q) ./release.sh
actual_all: parser $(TARGET_OUT) $(FW_BASE)
libesphttpd/Makefile:
$(Q) [[ -e "libesphttpd/Makefile" ]] || echo -e "\e[31mlibesphttpd submodule missing.\nIf build fails, run \"git submodule init\" and \"git submodule update\".\e[0m"
libesphttpd: libesphttpd/Makefile
$(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) SERVERNAME_PREFIX="ESPTerm " -j4
$(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) -j4
$(APP_AR): libesphttpd $(OBJ)
$(vecho) "AR $@"

@ -1,61 +0,0 @@
# --------------- esphttpd config options ---------------
# If GZIP_COMPRESSION is set to "yes" then the static css, js, and html files will be compressed with gzip before added to the espfs image
# and will be served with gzip Content-Encoding header.
# This could speed up the downloading of these files, but might break compatibility with older web browsers not supporting gzip encoding
# because Accept-Encoding is simply ignored. Enable this option if you have large static files to serve (for e.g. JQuery, Twitter bootstrap)
# By default only js, css and html files are compressed.
# If you have text based static files with different extensions what you want to serve compressed then you will need to add the extension to the following places:
# - Add the extension to this Makefile at the webpages.espfs target to the find command
# - Add the extension to the gzippedFileTypes array in the user/httpd.c file
#
# Adding JPG or PNG files (and any other compressed formats) is not recommended, because GZIP compression does not works effectively on compressed files.
#Static gzipping is disabled by default.
GZIP_COMPRESSION = yes
# If COMPRESS_W_YUI is set to "yes" then the static css and js files will be compressed with yui-compressor
# This option works only when GZIP_COMPRESSION is set to "yes"
# http://yui.github.io/yuicompressor/
#Disabled by default.
COMPRESS_W_YUI = no
YUI-COMPRESSOR = /usr/bin/yui-compressor
#If USE_HEATSHRINK is set to "yes" then the espfs files will be compressed with Heatshrink and decompressed
#on the fly while reading the file. Because the decompression is done in the esp8266, it does not require
#any support in the browser.
USE_HEATSHRINK = yes
USE_OPENSDK = yes
# this ugly trick is needed to allow a relative path
SDK_BASE=$(dir $(lastword $(MAKEFILE_LIST)))/esp_iot_sdk_v1.5.2/
# combined / separate / ota
OUTPUT_TYPE = combined
# SPI flash size, in K
ESP_SPI_FLASH_SIZE_K = 1024
GLOBAL_CFLAGS = \
-DDEBUG_ROUTER=0 \
-DDEBUG_CAPTDNS=0 \
-DDEBUG_HTTP=0 \
-DDEBUG_ESPFS=0 \
-DDEBUG_PERSIST=1 \
-DDEBUG_UTFCACHE=0 \
-DDEBUG_CGI=0 \
-DDEBUG_WIFI=0 \
-DDEBUG_WS=0 \
-DDEBUG_ANSI=1 \
-DDEBUG_ANSI_NOIMPL=1 \
-DDEBUG_INPUT=0 \
-DDEBUG_HEAP=1 \
-DDEBUG_MALLOC=0 \
-DHTTPD_MAX_BACKLOG_SIZE=8192 \
-DHTTPD_MAX_HEAD_LEN=1024 \
-DHTTPD_MAX_POST_LEN=512 \
-DDEBUG_LOGBUF_SIZE=1024 \
-mforce-l32 \
-DUSE_OPTIMIZE_PRINTF=1

@ -39,9 +39,12 @@ OUTPUT_TYPE = combined
ESP_SPI_FLASH_SIZE_K = 1024
GLOBAL_CFLAGS = \
-DASYNC_LOG=1 \
-DDEBUG_D2D=0 \
-DDEBUG_ROUTER=0 \
-DDEBUG_CAPTDNS=0 \
-DDEBUG_HTTP=0 \
-DDEBUG_HTTPC=0 \
-DDEBUG_ESPFS=0 \
-DDEBUG_PERSIST=1 \
-DDEBUG_UTFCACHE=0 \

@ -1 +1 @@
Subproject commit f5dd70a6f32ac36f0820badc170832f27242a09d
Subproject commit 01148465b71b640892a50ec825205ae581d6dca7

@ -0,0 +1,14 @@
//
// Created by MightyPork on 2017/10/03.
//
// helper for building release packages
//
// Run with `tcc -run`
//
#include "user/version.h"
#include <stdio.h>
void main() {
printf(FW_VERSION);
}

@ -1 +1 @@
Subproject commit 3479ab3efcb4581669370cde6a607f936ff5515a
Subproject commit e4ecf0724e36c828be5222eddce58a6a5cd2386f

@ -0,0 +1,21 @@
This is a release archive of ESPTerm,
the VT100 terminal emulator for ESP8266.
--------------------------------------------
Version: %VERS%
Locale : %LANG%
Built : %DATETIME%
--------------------------------------------
Source repository:
https://github.com/espterm/espterm-firmware
Report any bugs to our bug-tracker at
https://github.com/espterm/espterm-firmware/issues
or send them to out mailing list
espterm-dev@googlegroups.com
On-line demo is available at
https://espterm.github.io/term.html
[EOF]

@ -0,0 +1,26 @@
#!/usr/bin/env bash
# The parameters ESPTOOL, ESPPORT and ESPBAUD can be customized
# - export your preferred values in .bashrc
echo -e "\e[32;1mFlashing ESPTerm %VERS% (%LANG%)\e[0m"
if [ -z ${ESPTOOL} ]; then
ESPTOOL='esptool'
which ${ESPTOOL} &>/dev/null
if [ $? -ne 0 ]; then
ESPTOOL='esptool.py'
which ${ESPTOOL} &>/dev/null
if [ $? -ne 0 ]; then
echo -e '\e[31;1mesptool not found!\e[0m'
exit 1
fi
fi
fi
[ -z ESPPORT ] && ESPPORT=/dev/ttyUSB0
[ -z ESPBAUD ] && ESPBAUD=460800
set -x
${ESPTOOL} --port ${ESPPORT} --baud ${ESPBAUD} \
write_flash 0x00000 '%FILE0%' 0x40000 '%FILE4%'

@ -0,0 +1,3 @@
*
!.gitignore
!flash-tpl.sh

@ -0,0 +1,69 @@
#!/usr/bin/env bash
if [ -z "$1" ]; then
vers=$(tcc -run get_version.c)
else
vers=$1
fi
git pull
echo -n -e "\e[1;36mBuilding packages for version $vers\e[0m"
cd front-end
git pull
cd ..
function buildlang() {
lang=$1
echo -e "\n\e[33;1m------ Building \"${lang}\" package ------\e[0m\n"
make clean
ESP_LANG=${lang} make web
ESP_LANG=${lang} make actual_all -B -j4
cd release
destdir="$vers-$lang"
file0=${vers}-0x00000-${lang}.bin
file4=${vers}-0x40000-${lang}.bin
[ -e ${destdir} ] && rm -r ${destdir}
mkdir ${destdir}
cp ../firmware/0x00000.bin ${destdir}/${file0}
cp ../firmware/0x40000.bin ${destdir}/${file4}
flashsh=${destdir}/flash.sh
cp ../rel-tpl/flash.sh ${flashsh}
sed -i s/%FILE0%/${file0}/ ${flashsh}
sed -i s/%FILE4%/${file4}/ ${flashsh}
sed -i s/%VERS%/${vers}/ ${flashsh}
sed -i s/%LANG%/${lang}/ ${flashsh}
chmod +x ${flashsh}
readmefil=${destdir}/README.txt
cp ../rel-tpl/README.txt ${readmefil}
sed -i s/%VERS%/${vers}/ ${readmefil}
sed -i s/%LANG%/${lang}/ ${readmefil}
dt=$(LC_TIME=en_US.UTF-8 date '+%c')
sed -i "s#%DATETIME%#${dt}#" ${readmefil}
unix2dos ${readmefil}
cd ${destdir}
sha256sum ${file0} ${file4} README.txt flash.sh > checksums.txt
cd ..
targetfile=espterm-${vers}-${lang}.zip
[[ -e ${targetfile}.zip ]] && rm ${targetfile}.zip
pwd
zip -9 ${targetfile} ${destdir}/*
cd ..
}
if [ -z "$ESP_LANG" ]; then
buildlang cs
buildlang en
buildlang de
else
buildlang ${ESP_LANG}
fi

@ -5,10 +5,11 @@
#include "ansi_parser_callbacks.h"
#include "ascii.h"
#include "apars_logging.h"
#include "screen.h"
/* Ragel constants block */
/* #line 12 "user/ansi_parser.c" */
/* #line 13 "user/ansi_parser.c" */
static const char _ansi_actions[] ESP_CONST_DATA = {
0, 1, 0, 1, 1, 1, 2, 1,
3, 1, 4, 1, 5, 1, 6, 1,
@ -32,18 +33,15 @@ static const int ansi_en_charsetcmd_body = 10;
static const int ansi_en_main = 1;
/* #line 11 "user/ansi_parser.rl" */
/* #line 12 "user/ansi_parser.rl" */
// Max nr of CSI parameters
#define CSI_N_MAX 10
#define ANSI_STR_LEN 64
static volatile int cs = -1;
static volatile bool inside_string = false;
// public
volatile u32 ansi_parser_char_cnt = 0;
volatile bool ansi_parser_inhibit = 0;
void ICACHE_FLASH_ATTR
ansi_parser_reset(void) {
@ -108,18 +106,25 @@ ansi_parser(char newchar)
static char string_buffer[ANSI_STR_LEN];
static int str_ni;
if (ansi_parser_inhibit) return;
// This is used to detect timeout delay (time since last rx char)
ansi_parser_char_cnt++;
if (termconf->ascii_debug) {
apars_handle_plainchar(newchar);
return;
}
// Init Ragel on the first run
if (cs == -1) {
/* #line 118 "user/ansi_parser.c" */
/* #line 123 "user/ansi_parser.c" */
{
cs = ansi_start;
}
/* #line 92 "user/ansi_parser.rl" */
/* #line 97 "user/ansi_parser.rl" */
#if DEBUG_ANSI
memset(history, 0, sizeof(history));
@ -133,6 +138,13 @@ ansi_parser(char newchar)
history[HISTORY_LEN-1] = newchar;
#endif
// THose should work always, even inside a string
if (newchar == CAN || newchar == SUB) {
// Cancel the active sequence
cs = ansi_start;
return;
}
// Handle simple characters immediately (bypass parser)
if (newchar < ' ' && !inside_string) {
switch (newchar) {
@ -174,12 +186,6 @@ ansi_parser(char newchar)
apars_handle_enq();
return;
// Cancel the active sequence
case CAN:
case SUB:
cs = ansi_start;
return;
default:
// Discard all other control codes
return;
@ -199,7 +205,7 @@ ansi_parser(char newchar)
// The parser
/* #line 203 "user/ansi_parser.c" */
/* #line 209 "user/ansi_parser.c" */
{
const char *_acts;
unsigned int _nacts;
@ -389,7 +395,7 @@ execFuncs:
while ( _nacts-- > 0 ) {
switch ( *_acts++ ) {
case 0:
/* #line 179 "user/ansi_parser.rl" */
/* #line 185 "user/ansi_parser.rl" */
{
ansi_warn("Parser error.");
apars_show_context();
@ -398,7 +404,7 @@ execFuncs:
}
break;
case 1:
/* #line 188 "user/ansi_parser.rl" */
/* #line 194 "user/ansi_parser.rl" */
{
if ((*p) != 0) {
apars_handle_plainchar((*p));
@ -406,7 +412,7 @@ execFuncs:
}
break;
case 2:
/* #line 196 "user/ansi_parser.rl" */
/* #line 202 "user/ansi_parser.rl" */
{
// Reset the CSI builder
leadchar = NUL;
@ -423,13 +429,13 @@ execFuncs:
}
break;
case 3:
/* #line 211 "user/ansi_parser.rl" */
/* #line 217 "user/ansi_parser.rl" */
{
leadchar = (*p);
}
break;
case 4:
/* #line 215 "user/ansi_parser.rl" */
/* #line 221 "user/ansi_parser.rl" */
{
if (arg_cnt == 0) arg_cnt = 1;
// x10 + digit
@ -439,7 +445,7 @@ execFuncs:
}
break;
case 5:
/* #line 223 "user/ansi_parser.rl" */
/* #line 229 "user/ansi_parser.rl" */
{
if (arg_cnt == 0) arg_cnt = 1; // handle case when first arg is empty
arg_cnt++;
@ -447,20 +453,20 @@ execFuncs:
}
break;
case 6:
/* #line 229 "user/ansi_parser.rl" */
/* #line 235 "user/ansi_parser.rl" */
{
interchar = (*p);
}
break;
case 7:
/* #line 233 "user/ansi_parser.rl" */
/* #line 239 "user/ansi_parser.rl" */
{
apars_handle_csi(leadchar, arg, arg_cnt, interchar, (*p));
{cs = 1;goto _again;}
}
break;
case 8:
/* #line 245 "user/ansi_parser.rl" */
/* #line 251 "user/ansi_parser.rl" */
{
leadchar = (*p);
str_ni = 0;
@ -470,13 +476,13 @@ execFuncs:
}
break;
case 9:
/* #line 253 "user/ansi_parser.rl" */
/* #line 259 "user/ansi_parser.rl" */
{
string_buffer[str_ni++] = (*p);
}
break;
case 10:
/* #line 257 "user/ansi_parser.rl" */
/* #line 263 "user/ansi_parser.rl" */
{
inside_string = false;
string_buffer[str_ni++] = '\0';
@ -485,41 +491,41 @@ execFuncs:
}
break;
case 11:
/* #line 270 "user/ansi_parser.rl" */
/* #line 276 "user/ansi_parser.rl" */
{
apars_handle_hash_cmd((*p));
{cs = 1;goto _again;}
}
break;
case 12:
/* #line 275 "user/ansi_parser.rl" */
/* #line 281 "user/ansi_parser.rl" */
{
apars_handle_short_cmd((*p));
{cs = 1;goto _again;}
}
break;
case 13:
/* #line 280 "user/ansi_parser.rl" */
/* #line 286 "user/ansi_parser.rl" */
{
apars_handle_space_cmd((*p));
{cs = 1;goto _again;}
}
break;
case 14:
/* #line 287 "user/ansi_parser.rl" */
/* #line 293 "user/ansi_parser.rl" */
{
leadchar = (*p);
{cs = 10;goto _again;}
}
break;
case 15:
/* #line 292 "user/ansi_parser.rl" */
/* #line 298 "user/ansi_parser.rl" */
{
apars_handle_chs_designate(leadchar, (*p));
{cs = 1;goto _again;}
}
break;
/* #line 523 "user/ansi_parser.c" */
/* #line 529 "user/ansi_parser.c" */
}
}
goto _again;
@ -537,7 +543,7 @@ _again:
while ( __nacts-- > 0 ) {
switch ( *__acts++ ) {
case 0:
/* #line 179 "user/ansi_parser.rl" */
/* #line 185 "user/ansi_parser.rl" */
{
ansi_warn("Parser error.");
apars_show_context();
@ -547,7 +553,7 @@ _again:
goto _again;}
}
break;
/* #line 551 "user/ansi_parser.c" */
/* #line 557 "user/ansi_parser.c" */
}
}
}
@ -555,6 +561,6 @@ goto _again;}
_out: {}
}
/* #line 315 "user/ansi_parser.rl" */
/* #line 321 "user/ansi_parser.rl" */
}

@ -3,6 +3,11 @@
#include <stdlib.h>
#define CSI_N_MAX 12
#define ANSI_STR_LEN 256
extern volatile bool ansi_parser_inhibit; // discard all characters
void ansi_parser_reset(void);
extern volatile u32 ansi_parser_char_cnt;

@ -3,6 +3,7 @@
#include "ansi_parser_callbacks.h"
#include "ascii.h"
#include "apars_logging.h"
#include "screen.h"
/* Ragel constants block */
%%{
@ -10,15 +11,12 @@
write data;
}%%
// Max nr of CSI parameters
#define CSI_N_MAX 10
#define ANSI_STR_LEN 64
static volatile int cs = -1;
static volatile bool inside_string = false;
// public
volatile u32 ansi_parser_char_cnt = 0;
volatile bool ansi_parser_inhibit = 0;
void ICACHE_FLASH_ATTR
ansi_parser_reset(void) {
@ -83,9 +81,16 @@ ansi_parser(char newchar)
static char string_buffer[ANSI_STR_LEN];
static int str_ni;
if (ansi_parser_inhibit) return;
// This is used to detect timeout delay (time since last rx char)
ansi_parser_char_cnt++;
if (termconf->ascii_debug) {
apars_handle_plainchar(newchar);
return;
}
// Init Ragel on the first run
if (cs == -1) {
%% write init;
@ -102,6 +107,13 @@ ansi_parser(char newchar)
history[HISTORY_LEN-1] = newchar;
#endif
// THose should work always, even inside a string
if (newchar == CAN || newchar == SUB) {
// Cancel the active sequence
cs = ansi_start;
return;
}
// Handle simple characters immediately (bypass parser)
if (newchar < ' ' && !inside_string) {
switch (newchar) {
@ -143,12 +155,6 @@ ansi_parser(char newchar)
apars_handle_enq();
return;
// Cancel the active sequence
case CAN:
case SUB:
cs = ansi_start;
return;
default:
// Discard all other control codes
return;

@ -11,6 +11,14 @@
#include "version.h"
#include "uart_buffer.h"
#include "screen.h"
#include "wifimgr.h"
volatile bool enquiry_suppressed = false;
ETSTimer enqTimer;
void ICACHE_FLASH_ATTR enqTimerCb(void *unused)
{
enquiry_suppressed = false;
}
/**
* Send a response to UART0
@ -19,6 +27,7 @@
void ICACHE_FLASH_ATTR
apars_respond(const char *str)
{
// Using the Tx buffer causes issues with large data (eg. from http requests)
UART_SendAsync(str, -1);
}
@ -37,8 +46,32 @@ apars_handle_bel(void)
void ICACHE_FLASH_ATTR
apars_handle_enq(void)
{
if (enquiry_suppressed) return;
u8 mac[6];
wifi_get_macaddr(SOFTAP_IF, mac);
char buf100[100];
char *buf = buf100;
buf += sprintf(buf, "\x1bX");
buf += sprintf(buf, "ESPTerm "VERSION_STRING" ");
buf += sprintf(buf, "#"GIT_HASH_BACKEND"+"GIT_HASH_FRONTEND" ");
buf += sprintf(buf, "id=%02X%02X%02X ", mac[3], mac[4], mac[5]);
int x = getStaIpAsString(buf);
if (x) buf += x;
else buf--; // remove the trailing space
buf += sprintf(buf, "\x1b\\");
(void)buf;
// version encased in SOS and ST
apars_respond("\x1bXESPTerm " FIRMWARE_VERSION "\x1b\\");
apars_respond(buf100);
// Throttle enquiry - this is a single-character-invoked response,
// so it tends to happen randomly when throwing garbage at the ESP.
// We don't want to fill the output buffer with dozens of enquiry responses
enquiry_suppressed = true;
TIMER_START(&enqTimer, enqTimerCb, 500, 0);
}
void ICACHE_FLASH_ATTR

@ -709,6 +709,8 @@ do_csi_set_private_option(CSI_Data *opts)
mouse_tracking.mode,
mouse_tracking.encoding,
mouse_tracking.focus_tracking);
screen_notifyChange(TOPIC_CHANGE_SCREEN_OPTS);
}
else if (n == 12) {
screen_cursor_blink(yn);
@ -757,11 +759,11 @@ do_csi_set_private_option(CSI_Data *opts)
}
else if (n == 800) { // ESPTerm: Toggle display of buttons
termconf_live.show_buttons = yn;
screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble
screen_notifyChange(TOPIC_CHANGE_SCREEN_OPTS); // this info is included in the screen preamble
}
else if (n == 801) { // ESPTerm: Toggle display of config links
termconf_live.show_config_links = yn;
screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble
screen_notifyChange(TOPIC_CHANGE_SCREEN_OPTS); // this info is included in the screen preamble
}
else {
ansi_noimpl("?OPTION %d", n);

@ -30,7 +30,7 @@
* @param buffer - the DCS body (after DCS and before ST)
*/
void ICACHE_FLASH_ATTR
apars_handle_dcs(const char *buffer)
apars_handle_dcs(char *buffer)
{
char buf[64]; // just about big enough for full-house SGR
size_t len = strlen(buffer);
@ -47,7 +47,9 @@ apars_handle_dcs(const char *buffer)
}
else if (buffer[2] == 'r') {
// DECSTBM - Query scrolling region
sprintf(buf, "\033P1$r%d;%dr\033\\", 1, termconf_live.height); // 1-80 TODO real extent of scrolling region
int v0, v1;
screen_region_get(&v0, &v1);
sprintf(buf, "\033P1$r%d;%dr\033\\", v0+1, v1+1);
apars_respond(buf);
}
else if (buffer[2] == 's') {
@ -64,7 +66,7 @@ apars_handle_dcs(const char *buffer)
}
else if (strneq(buffer+2, " q", 2)) {
// DECSCUSR - Query cursor style
sprintf(buf, "\033P1$r%d q\033\\", 1);
sprintf(buf, "\033P1$r%d q\033\\", termconf_live.cursor_shape);
/*
Ps = 0 -> blinking block.
Ps = 1 -> blinking block (default).

@ -5,6 +5,6 @@
#ifndef ESP_VT100_FIRMWARE_APARS_DCS_H
#define ESP_VT100_FIRMWARE_APARS_DCS_H
void apars_handle_dcs(const char *buffer);
void apars_handle_dcs(char *buffer);
#endif //ESP_VT100_FIRMWARE_APARS_DCS_H

@ -0,0 +1,34 @@
//
// Created by MightyPork on 2017/08/20.
//
// Handle privacy messages
// PM Pt ST
// (PM = ESC ^)
//
// Those are used for device-to-device communication.
// They were not used for anything in the original VT100 and are not
// used by Xterm or any other common emulator, but they should be safely discarded.
//
#include <esp8266.h>
#include <httpclient.h>
#include "apars_pm.h"
#include "version.h"
#include "ansi_parser_callbacks.h"
#include "screen.h"
#include "apars_logging.h"
#include "cgi_d2d.h"
/**
* Helper function to parse incoming DCS (Device Control String)
* @param msg - the DCS body (after DCS and before ST)
*/
void ICACHE_FLASH_ATTR
apars_handle_pm(char *msg)
{
if (d2d_parse_command(msg)) return;
return;
fail:
ansi_warn("D2D message error: %s", msg);
}

@ -0,0 +1,10 @@
//
// Created by MightyPork on 2017/08/20.
//
#ifndef ESP_VT100_FIRMWARE_APARS_PM_H
#define ESP_VT100_FIRMWARE_APARS_PM_H
void apars_handle_pm(char *msg);
#endif //ESP_VT100_FIRMWARE_APARS_PM_H

@ -42,9 +42,11 @@
//
#include <esp8266.h>
#include <httpclient.h>
#include "apars_short.h"
#include "apars_logging.h"
#include "screen.h"
#include "ansi_parser_callbacks.h"
// ----- Character Set ---
@ -103,6 +105,11 @@ void ICACHE_FLASH_ATTR apars_handle_hash_cmd(char c)
screen_fill_with_E();
break;
// development codes - do not use!
case '7':
http_get("http://wtfismyip.com/text", NULL, http_callback_example);
break;
default:
ansi_noimpl("ESC # %c", c);
}

@ -17,6 +17,7 @@
#include "apars_logging.h"
#include "ansi_parser_callbacks.h"
#include "screen.h"
#include "apars_pm.h"
// ----- Generic String cmd - disambiguation -----
@ -24,7 +25,7 @@ void ICACHE_FLASH_ATTR
apars_handle_string_cmd(char leadchar, char *buffer)
{
switch (leadchar) {
case 'k': // ESC k TITLE ST (defined in GNU screen manpage)
case 'k': // ESC k TITLE ST (defined in GNU screen manpage, probably not standard)
screen_set_title(buffer);
break;
@ -37,6 +38,7 @@ apars_handle_string_cmd(char leadchar, char *buffer)
break;
case '^': // PM - Privacy Message
apars_handle_pm(buffer);
break;
case '_': // APC - Application Program Command

@ -8,9 +8,13 @@
#include "apars_utf8.h"
#include "apars_logging.h"
#include "screen.h"
#include "uart_driver.h"
#include "ansi_parser_callbacks.h"
#include "ansi_parser.h"
#include "ascii.h"
static char utf_collect[4];
static int utf_i = 0;
static u8 bytes[4];
static int utf_len = 0;
static int utf_j = 0;
/**
@ -20,11 +24,121 @@ static int utf_j = 0;
void ICACHE_FLASH_ATTR
apars_reset_utf8buffer(void)
{
utf_i = 0;
utf_len = 0;
utf_j = 0;
memset(utf_collect, 0, 4);
memset(bytes, 0, 4);
}
// Code Points First Byte Second Byte Third Byte Fourth Byte
// U+0000 - U+007F 00 - 7F
// U+0080 - U+07FF C2 - DF 80 - BF
// U+0800 - U+0FFF E0 *A0 - BF 80 - BF
// U+1000 - U+CFFF E1 - EC 80 - BF 80 - BF
// U+D000 - U+D7FF ED 80 - *9F 80 - BF
// U+E000 - U+FFFF EE - EF 80 - BF 80 - BF
// U+10000 - U+3FFFF F0 *90 - BF 80 - BF 80 - BF
// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF
// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF
static void ICACHE_FLASH_ATTR screen_print_ascii(const char *str)
{
char gly[2];
gly[1] = 0;
for(int j = 0;str[j]!=0;j++) {
gly[0] = str[j];
screen_putchar(gly);
}
}
static void ICACHE_FLASH_ATTR hdump_spaces_eol(int needed)
{
if (needed == 0) needed = 5;
int x, y;
screen_cursor_get(&y, &x);
if (x > termconf_live.width - needed) {
screen_clear_in_line(CLEAR_FROM_CURSOR);
screen_putchar("\n");
screen_putchar("\r");
}
}
static void ICACHE_FLASH_ATTR hdump_good(const char *ch)
{
char buf[10];
hdump_spaces_eol(6);
screen_set_fg(7);
screen_set_bg(0);
if(ch[0]<32) {
screen_set_fg(7);
screen_set_bg(2);
switch (ch[0]) {
case NUL: screen_print_ascii("NUL"); break;
case SOH: screen_print_ascii("SOH"); break;
case STX: screen_print_ascii("STX"); break;
case ETX: screen_print_ascii("ETX"); break;
case EOT: screen_print_ascii("EOT"); break;
case ENQ: screen_print_ascii("ENQ"); break;
case ACK: screen_print_ascii("ACK"); break;
case BEL: screen_print_ascii("BEL"); break;
case BS: screen_print_ascii("BS"); break;
case TAB: screen_print_ascii("TAB"); break;
case LF: screen_print_ascii("LF"); break;
case VT: screen_print_ascii("VT"); break;
case FF: screen_print_ascii("FF"); break;
case CR: screen_print_ascii("CR"); break;
case SO: screen_print_ascii("SO"); break;
case SI: screen_print_ascii("SI"); break;
case DLE: screen_print_ascii("DLE"); break;
case DC1: screen_print_ascii("DC1"); break;
case DC2: screen_print_ascii("DC2"); break;
case DC3: screen_print_ascii("DC3"); break;
case DC4: screen_print_ascii("DC4"); break;
case NAK: screen_print_ascii("NAK"); break;
case SYN: screen_print_ascii("SYN"); break;
case ETB: screen_print_ascii("ETB"); break;
case CAN: screen_print_ascii("CAN"); break;
case EM: screen_print_ascii("EM"); break;
case SUB: screen_print_ascii("SUB"); break;
case ESC: screen_print_ascii("ESC"); break;
case FS: screen_print_ascii("FS"); break;
case GS: screen_print_ascii("GS"); break;
case RS: screen_print_ascii("RS"); break;
case US: screen_print_ascii("US"); break;
case SP: screen_print_ascii("SP"); break;
case DEL: screen_print_ascii("DEL"); break;
default:
sprintf(buf, "%02Xh", ch[0]);
screen_print_ascii(buf);
}
} else {
screen_putchar(ch);
}
screen_set_default_bg();
screen_set_default_fg();
screen_print_ascii(" ");
}
static void ICACHE_FLASH_ATTR hdump_bad(const char *ch, int len)
{
char buf[10];
hdump_spaces_eol(len*5);
screen_set_fg(7);
screen_set_bg(1);
for (int i=0;i<len;i++) {
sprintf(buf, "%02Xh", ch[i]);
screen_print_ascii(buf);
if(i<len-1) screen_print_ascii(" ");
}
screen_set_default_bg();
screen_set_default_fg();
screen_print_ascii(" ");
}
/**
* Handle a received plain character
* @param c - received character
@ -32,53 +146,79 @@ apars_reset_utf8buffer(void)
void ICACHE_FLASH_ATTR
apars_handle_plainchar(char c)
{
u8 uc = (u8)c;
// collecting unicode glyphs...
if (c & 0x80) {
if (utf_i == 0) {
if (uc & 0x80) {
if (utf_len == 0) {
bytes[0] = uc;
utf_j = 1;
// start
if (c == 192 || c == 193 || c >= 245) {
// forbidden codes (would be an overlong sequence)
if (uc == 0xC0 || uc == 0xC1 || uc > 0xF4) {
// forbidden start codes
goto fail;
}
if ((c & 0xE0) == 0xC0) {
utf_i = 2;
if ((uc & 0xE0) == 0xC0) {
utf_len = 2;
}
else if ((c & 0xF0) == 0xE0) {
utf_i = 3;
else if ((uc & 0xF0) == 0xE0) {
utf_len = 3;
}
else if ((c & 0xF8) == 0xF0) {
utf_i = 4;
else if ((uc & 0xF8) == 0xF0) {
utf_len = 4;
}
else {
// chars over 127 that don't start unicode sequences
goto fail;
}
utf_collect[0] = c;
utf_j = 1;
}
else {
if ((c & 0xC0) != 0x80) {
if ((uc & 0xC0) != 0x80) {
bytes[utf_j++] = uc;
goto fail;
}
else {
utf_collect[utf_j++] = c;
if (utf_j >= utf_i) {
screen_putchar(utf_collect);
bytes[utf_j++] = uc;
if (utf_j >= utf_len) {
// check for bad sequences - overlong or some other problem
if (bytes[0] == 0xF4 && bytes[1] > 0x8F) goto fail;
if (bytes[0] == 0xF0 && bytes[1] < 0x90) goto fail;
if (bytes[0] == 0xED && bytes[1] > 0x9F) goto fail;
if (bytes[0] == 0xE0 && bytes[1] < 0xA0) goto fail;
// trap for surrogates - those break javascript
if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) goto fail;
if (termconf_live.ascii_debug) {
hdump_good((const char *) bytes);
} else {
screen_putchar((const char *) bytes);
}
apars_reset_utf8buffer();
}
}
}
}
else {
utf_collect[0] = c;
utf_collect[1] = 0; // just to make sure it's closed...
screen_putchar(utf_collect);
bytes[0] = uc;
bytes[1] = 0; // just to make sure it's closed...
if (termconf_live.ascii_debug) {
hdump_good((const char *) bytes);
} else {
screen_putchar((const char *) bytes);
}
apars_reset_utf8buffer();
}
return;
fail:
ansi_warn("Bad UTF-8: %0Xh", c);
fail:
if (termconf_live.ascii_debug) {
hdump_bad((const char *) bytes, utf_j);
} else {
screen_putchar("\xEF\xBF\xBD");
}
//ansi_warn("BAD UTF8!");
//apars_show_context();
apars_reset_utf8buffer();
}

@ -0,0 +1,15 @@
//
// Created by MightyPork on 2017/10/01.
//
#ifndef ESPTERM_API_H
#define ESPTERM_API_H
// TODO use X-MACRO for access restrictions etc
#define API_D2D_MSG "/api/v1/msg"
#define API_REBOOT "/api/v1/reboot"
#define API_PING "/api/v1/ping"
#define API_CLEAR "/api/v1/clear"
#endif //ESPTERM_API_H

@ -0,0 +1,324 @@
//
// Created by MightyPork on 2017/10/01.
//
#include <esp8266.h>
#include "cgi_d2d.h"
#include "version.h"
#include "ansi_parser_callbacks.h"
#include "api.h"
#include "uart_driver.h"
#include <httpclient.h>
#include <esp_utils.h>
#define D2D_TIMEOUT_MS 2000
#define D2D_HEADERS \
"User-Agent: ESPTerm "VERSION_STRING" like curl wget HTTPie\r\n" \
"Content-Type: text/plain; charset=utf-8\r\n" \
"Accept-Encoding: identity\r\n" \
"Accept-Charset: utf-8\r\n" \
"Accept: text/*, application/json\r\n" \
"Cache-Control: no-cache,private,max-age=0\r\n"
struct d2d_request_opts {
bool want_body;
bool want_head;
size_t max_result_len;
char *nonce;
};
volatile bool request_pending = false;
// NOTE! We bypass the async buffer here - used for user input and
// responses to queries {apars_respond()}. In rare situations this could
// lead to a race condition and mixing two different messages
static inline void ICACHE_FLASH_ATTR sendResponseToUART(const char *str)
{
UART_WriteString(UART0, str, UART_TIMEOUT_US);
}
static void ICACHE_FLASH_ATTR
requestNoopCb(int http_status,
char *response_headers,
char *response_body,
size_t body_size,
void *userArg)
{
request_pending = false;
if (userArg != NULL) free(userArg);
}
static void ICACHE_FLASH_ATTR
requestCb(int http_status,
char *response_headers,
char *response_body,
size_t body_size,
void *userArg)
{
if (userArg == NULL) {
request_pending = false;
return;
}
struct d2d_request_opts *opts = userArg;
// ensure positive - would be hard to parse
if (http_status < 0) http_status = -http_status;
char buff100[100];
int len = 0;
size_t headers_size = strlen(response_headers);
if (opts->want_head) len += headers_size;
if (opts->want_body) len += body_size + (opts->want_head*2);
if (opts->max_result_len > 0 && len > opts->max_result_len)
len = (int) opts->max_result_len;
d2d_info("Rx HTTP response, code %d, len %d, nonce \"%s\"", http_status, len, opts->nonce?opts->nonce:"");
char *bb = buff100;
bb += sprintf(bb, "\x1b^h;%d;", http_status);
const char *comma = "";
if (opts->want_head) {
bb += sprintf(bb, "%sH", comma);
comma = ",";
}
if (opts->want_body) {
bb += sprintf(bb, "%sB", comma);
comma = ",";
}
if (opts->nonce) {
bb += sprintf(bb, "%sN=%s", comma, opts->nonce);
comma = ",";
}
if (opts->want_head || opts->want_body) {
bb += sprintf(bb, "%sL=%d", comma, len);
//comma = ",";
}
// semicolon only if more data is to be sent
if (opts->want_head || opts->want_body) sprintf(bb, ";");
sendResponseToUART(buff100);
//d2d_dbg("Headers (part) %100s", response_headers);
//d2d_dbg("Body (part) %100s", response_body);
// head and payload separated by \r\n\r\n (one \r\n is at the end of head - maybe)
if (opts->want_head) {
// truncate
if (headers_size > len) {
response_headers[len] = 0;
opts->want_body = false; // soz, it wouldn't fit
}
sendResponseToUART(response_headers);
if(opts->want_body) sendResponseToUART("\r\n");
}
if(opts->want_body) {
// truncate
if (opts->want_head*(headers_size+2)+body_size > len) {
response_body[len - (opts->want_head*(headers_size+2))] = 0;
}
sendResponseToUART(response_body);
}
sendResponseToUART("\a");
free(opts->nonce);
free(opts);
request_pending = false;
}
bool ICACHE_FLASH_ATTR
d2d_parse_command(char *msg)
{
char buff40[40];
char *p;
#define FIND_NEXT(target, delim) do { \
p = strchr(msg, (delim)); \
if (p == NULL) return false; \
*p = '\0'; \
(target) = msg; \
msg = p + 1; \
} while(0) \
if (strstarts(msg, "M;")) {
if (request_pending) return false;
// Send a esp-esp message
msg += 2;
const char *ip;
FIND_NEXT(ip, ';');
const char *payload = msg;
d2d_info("D2D Tx,dest=%s,msg=%s", ip, payload);
sprintf(buff40, "http://%s" API_D2D_MSG, ip);
httpclient_args args;
httpclient_args_init(&args);
args.method = HTTPD_METHOD_POST;
args.body = payload;
args.headers = D2D_HEADERS;
args.timeout = D2D_TIMEOUT_MS;
args.url = buff40; // "escapes scope" warning - can ignore, strdup is used
request_pending = true;
http_request(&args, requestNoopCb);
return true;
}
else if (strstarts(msg, "H;")) {
if (request_pending) return false;
// Send a esp-esp message
msg += 2;
const char *method = NULL;
const char *params = NULL;
const char *nonce = NULL;
const char *url = NULL;
const char *payload = NULL;
httpd_method methodNum;
FIND_NEXT(method, ';');
if (streq(method, "GET")) methodNum = HTTPD_METHOD_GET;
else if (streq(method, "POST")) methodNum = HTTPD_METHOD_POST;
else if (streq(method, "OPTIONS")) methodNum = HTTPD_METHOD_OPTIONS;
else if (streq(method, "PUT")) methodNum = HTTPD_METHOD_PUT;
else if (streq(method, "DELETE")) methodNum = HTTPD_METHOD_DELETE;
else if (streq(method, "PATCH")) methodNum = HTTPD_METHOD_PATCH;
else if (streq(method, "HEAD")) methodNum = HTTPD_METHOD_HEAD;
else {
d2d_warn("BAD METHOD: %s", method);
return false;
}
FIND_NEXT(params, ';');
d2d_info("HTTP request");
d2d_dbg("Method %s", method);
size_t max_buf_len = HTTPCLIENT_DEF_MAX_LEN;
size_t max_result_len = 0; // 0 = no truncate
uint timeout = HTTPCLIENT_DEF_TIMEOUT_MS;
bool want_body = 0;
bool want_head = 0;
bool no_resp = 0;
do {
p = strchr(params, ',');
if (p != NULL) *p = '\0';
const char *param = params;
if (params[0] == 0) break; // no params
if(streq(param, "H")) want_head = 1; // Return head
else if(streq(param, "B")) want_body = 1; // Return body
else if(streq(param, "X")) no_resp = 1; // X - no response, no callback
else if(strstarts(param, "l=")) { // max buffer length
max_buf_len = (size_t) atoi(param + 2);
} else if(strstarts(param, "L=")) { // max length
max_result_len = (size_t) atoi(param + 2);
} else if(strstarts(param, "T=")) { // timeout
timeout = (uint) atoi(param + 2);
} else if(strstarts(param, "N=")) { // Nonce
nonce = param+2;
} else {
d2d_warn("BAD PARAM: %s", param);
return false;
}
d2d_dbg("- param %s", params);
if (p == NULL) break;
params = p + 1;
} while(1);
p = strchr(msg, '\n');
if (p != NULL) *p = '\0';
url = msg;
d2d_dbg("URL: %s", url);
if (p != NULL) {
payload = p + 1;
d2d_dbg("Payload: %s", payload);
} else {
payload = NULL;
}
httpclient_args args;
httpclient_args_init(&args);
args.method = methodNum;
args.body = payload;
args.headers = D2D_HEADERS;
args.timeout = timeout;
args.max_response_len = max_buf_len;
args.url = url;
if (!no_resp) {
struct d2d_request_opts *opts = malloc(sizeof(struct d2d_request_opts));
opts->want_body = want_body;
opts->want_head = want_head;
opts->max_result_len = max_result_len;
opts->nonce = esp_strdup(nonce);
args.userData = opts;
}
request_pending = true;
http_request(&args, no_resp ? requestNoopCb : requestCb);
d2d_dbg("Request sent.");
return true;
}
return false;
}
httpd_cgi_state ICACHE_FLASH_ATTR cgiD2DMessage(HttpdConnData *connData)
{
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
size_t len = 0;
if (connData->post && connData->post->buff)
len = strlen(connData->post->buff);
else if (connData->getArgs)
len = strlen(connData->getArgs);
else
len = 0;
u8 *ip = connData->remote_ip;
char buf[20];
sprintf(buf, "\x1b^m;"IPSTR";L=%d;", ip[0], ip[1], ip[2], ip[3], (int)len);
sendResponseToUART(buf);
if (connData->post && connData->post->buff)
sendResponseToUART(connData->post->buff);
else if (connData->getArgs)
sendResponseToUART(connData->getArgs);
sendResponseToUART("\a");
d2d_info("D2D Rx src="IPSTR",len=%d", ip[0], ip[1], ip[2], ip[3],len);
// Received a msg
httdResponseOptions(connData, 0);
httdSetTransferMode(connData, HTTPD_TRANSFER_CLOSE);
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
httpdSend(connData, "message received\r\n", -1);
return HTTPD_CGI_DONE;
}

@ -0,0 +1,25 @@
//
// Created by MightyPork on 2017/10/01.
//
#ifndef ESPTERM_CGI_D2D_H
#define ESPTERM_CGI_D2D_H
#include <esp8266.h>
#include <httpd.h>
#if DEBUG_D2D
#define d2d_warn warn
#define d2d_dbg dbg
#define d2d_info info
#else
#define d2d_warn(fmt, ...)
#define d2d_dbg(fmt, ...)
#define d2d_info(fmt, ...)
#endif
bool d2d_parse_command(char *msg);
httpd_cgi_state cgiD2DMessage(HttpdConnData *connData);
#endif //ESPTERM_CGI_D2D_H

@ -25,24 +25,12 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token
char buff[150];
if (streq(token, "labels_seq")) {
screenSerializeLabelsToBuffer(buff, 150);
tplSend(connData, buff, -1);
}
else if (streq(token, "theme")) {
sprintf(buff, "%d", termconf->theme);
tplSend(connData, buff, -1);
}
else if (streq(token, "want_all_fn")) {
if (streq(token, "want_all_fn")) {
sprintf(buff, "%d", termconf->want_all_fn);
tplSend(connData, buff, -1);
}
else if (streq(token, "default_fg")) {
sprintf(buff, "%d", termconf->default_fg);
tplSend(connData, buff, -1);
}
else if (streq(token, "default_bg")) {
sprintf(buff, "%d", termconf->default_bg);
else if (streq(token, "debugbar")) {
sprintf(buff, "%d", termconf->debugbar);
tplSend(connData, buff, -1);
}
@ -56,7 +44,7 @@ tplAbout(HttpdConnData *connData, char *token, void **arg)
if (token == NULL) return HTTPD_CGI_DONE;
if (streq(token, "vers_fw")) {
tplSend(connData, FIRMWARE_VERSION, -1);
tplSend(connData, VERSION_STRING, -1);
}
else if (streq(token, "date")) {
tplSend(connData, __DATE__, -1);
@ -68,7 +56,6 @@ tplAbout(HttpdConnData *connData, char *token, void **arg)
tplSend(connData, httpdGetVersion(), -1);
}
else if (streq(token, "vers_sdk")) {
//tplSend(connData, STR(ESP_SDK_VERSION), -1);
tplSend(connData, system_get_sdk_version(), -1);
}
else if (streq(token, "hash_backend")) {

@ -16,20 +16,22 @@
// Must be less than httpd sendbuf
#define SOCK_BUF_LEN 2000
volatile ScreenNotifyTopics pendingBroadcastTopics = 0;
// flags for screen update timeouts
volatile bool notify_available = true;
volatile bool notify_cooldown = false;
volatile bool notify_scheduled = false;
/** True if we sent XOFF to browser to stop uploading,
* and we have to tell it we're ready again */
volatile bool browser_wants_xon = false;
static ETSTimer notifyContentTim;
static ETSTimer notifyLabelsTim;
static ETSTimer updateNotifyTim;
static ETSTimer notifyCooldownTim;
static ETSTimer heartbeatTim;
volatile int active_clients = 0;
volatile int term_active_clients = 0;
// we're trying to do a kind of mutex here, without the actual primitives
// this might glitch, very rarely.
@ -52,26 +54,22 @@ notifyCooldownTimCb(void *arg)
* @param arg
*/
static void ICACHE_FLASH_ATTR
notifyContentTimCb(void *arg)
updateNotify_do(Websock *ws, ScreenNotifyTopics topics)
{
Websock *ws = arg;
void *data = NULL;
int max_bl, total_bl;
char sock_buff[SOCK_BUF_LEN];
cgiWebsockMeasureBacklog(URL_WS_UPDATE, &max_bl, &total_bl);
if (!notify_available || notify_cooldown || (max_bl > 2048)) { // do not send if we have anything significant backlogged
// postpone a little
TIMER_START(&notifyContentTim, notifyContentTimCb, 4, 0);
inp_dbg("postpone notify content");
return;
}
notify_available = false;
for (int i = 0; i < 20; i++) {
httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, &data);
int flg = 0;
if (! ws) {
// broadcast
topics = pendingBroadcastTopics;
pendingBroadcastTopics = 0;
}
httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, topics, &data);
int flg = 0; //WEBSOCK_FLAG_BIN
if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE;
if (i > 0) flg |= WEBSOCK_FLAG_CONT;
if (ws) {
@ -86,56 +84,45 @@ notifyContentTimCb(void *arg)
}
// cleanup
screenSerializeToBuffer(NULL, SOCK_BUF_LEN, &data);
screenSerializeToBuffer(NULL, 0, 0, &data);
notify_cooldown = true;
notify_available = true;
TIMER_START(&notifyCooldownTim, notifyCooldownTimCb, termconf->display_cooldown_ms, 0);
}
/**
* Tell browsers about the new text labels
* Tell browser we have new content
* @param arg
*/
static void ICACHE_FLASH_ATTR
notifyLabelsTimCb(void *arg)
updateNotifyCb(void *arg)
{
Websock *ws = arg;
char sock_buff[SOCK_BUF_LEN];
int max_bl, total_bl;
cgiWebsockMeasureBacklog(URL_WS_UPDATE, &max_bl, &total_bl);
inp_dbg("Notify broadcast +%02Xh?", pendingBroadcastTopics);
if (!notify_available || notify_cooldown) {
if (!notify_available || notify_cooldown || (max_bl > 2048)) { // do not send if we have anything significant backlogged
// postpone a little
TIMER_START(&notifyLabelsTim, notifyLabelsTimCb, 7, 0);
inp_dbg("postpone notify labels");
TIMER_START(&updateNotifyTim, updateNotifyCb, 4, 0);
inp_dbg("postpone notify; avail? %d coold? %d maxbl? %d", notify_available, notify_cooldown, max_bl);
return;
}
notify_available = false;
screenSerializeLabelsToBuffer(sock_buff, SOCK_BUF_LEN);
if (ws) {
cgiWebsocketSend(ws, sock_buff, (int) strlen(sock_buff), 0);
} else {
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), 0);
resetHeartbeatTimer();
}
updateNotify_do(arg, 0);
notify_scheduled = false;
notify_cooldown = true;
notify_available = true;
TIMER_START(&notifyCooldownTim, notifyCooldownTimCb, termconf->display_cooldown_ms, 0);
}
/** Beep */
void ICACHE_FLASH_ATTR
send_beep(void)
{
if (active_clients == 0) return;
// here's some potential for a race error with the other broadcast functions :C
cgiWebsockBroadcast(URL_WS_UPDATE, "B", 1, 0);
resetHeartbeatTimer();
if (term_active_clients == 0) return;
screen_notifyChange(TOPIC_BELL); // XXX has latency, maybe better to send directly
}
@ -143,11 +130,11 @@ send_beep(void)
void ICACHE_FLASH_ATTR
notify_growl(char *msg)
{
if (active_clients == 0) return;
if (term_active_clients == 0) return;
// TODO via timer...
// here's some potential for a race error with the other broadcast functions :C
cgiWebsockBroadcast(URL_WS_UPDATE, msg, (int) strlen(msg), 0);
// here's some potential for a race error with the other broadcast functions
// - we assume app won't send notifications in the middle of updating content
cgiWebsockBroadcast(URL_WS_UPDATE, msg, (int) strlen(msg), WEBSOCK_FLAG_BIN);
resetHeartbeatTimer();
}
@ -157,24 +144,20 @@ notify_growl(char *msg)
* This is a callback for the Screen module,
* called after each visible screen modification.
*/
void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic)
void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyTopics topics)
{
if (active_clients == 0) return;
if (term_active_clients == 0) return;
// this is probably not needed here - ensure timeout is not 0
if (termconf->display_tout_ms == 0)
termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS;
inp_dbg("Notify +%02Xh", topics);
// NOTE: the timers are restarted if already running
pendingBroadcastTopics |= topics;
if (topic == CHANGE_LABELS) {
// separate timer from content change timer, to avoid losing that update
TIMER_START(&notifyLabelsTim, notifyLabelsTimCb, termconf->display_tout_ms+2, 0); // this delay is useful when both are fired at once on screen reset
}
else if (topic == CHANGE_CONTENT) {
// throttle delay
TIMER_START(&notifyContentTim, notifyContentTimCb, termconf->display_tout_ms, 0);
}
int time = termconf->display_tout_ms;
if (time == 0 && notify_scheduled) return; // do not reset the timer if already scheduled
notify_scheduled = true;
// NOTE: the timer is restarted if already running
TIMER_START(&updateNotifyTim, updateNotifyCb, time, 0); // note - this adds latency to beep
}
/**
@ -277,7 +260,7 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int
// TODO base this on the actual buffer empty space, not rx chunk size
if ((UART_AsyncTxGetEmptySpace() < 256) && !browser_wants_xon) {
UART_WriteChar(UART1, '-', 100);
//UART_WriteChar(UART1, '-', 100);
cgiWebsockBroadcast(URL_WS_UPDATE, "-", 1, 0);
browser_wants_xon = true;
@ -296,8 +279,7 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int
case 'i':
// requests initial load
inp_dbg("Client requests initial load");
notifyContentTimCb(ws); // delay??
notifyLabelsTimCb(ws);
updateNotify_do(ws, TOPIC_INITIAL|TOPIC_FLAG_NOCLEAN);
break;
case 'm':
@ -322,13 +304,17 @@ static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int
/** Send a heartbeat msg */
static void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused)
{
if (active_clients > 0) {
static u32 hbcnt=0;
if (term_active_clients > 0) {
if (notify_available) {
inp_dbg(".");
// Heartbeat packet - indicate we're still connected
// JS reloads the page if heartbeat is lost for a couple seconds
cgiWebsockBroadcast(URL_WS_UPDATE, ".", 1, 0);
char buf[10];
sprintf(buf, ".%d", hbcnt++);
cgiWebsockBroadcast(URL_WS_UPDATE, buf, (int) strlen(buf), 0);
// schedule next tick
TIMER_START(&heartbeatTim, heartbeatTimCb, HB_TIME, 0);
@ -348,9 +334,10 @@ static void ICACHE_FLASH_ATTR resetHeartbeatTimer(void)
static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws)
{
active_clients--;
if (active_clients <= 0) {
active_clients = 0;
term_active_clients--;
inp_dbg("Close socket CB, remain %d clients", term_active_clients);
if (term_active_clients <= 0) {
term_active_clients = 0;
if (mouse_tracking.focus_tracking) {
UART_SendAsync("\x1b[O", 3);
@ -358,6 +345,7 @@ static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws)
// stop the timer
os_timer_disarm(&heartbeatTim);
inp_dbg("Stop HB timer");
}
}
@ -368,7 +356,7 @@ void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws)
ws->recvCb = updateSockRx;
ws->closeCb = closeSockCb;
if (active_clients == 0) {
if (term_active_clients == 0) {
if (mouse_tracking.focus_tracking) {
UART_SendAsync("\x1b[I", 3);
}
@ -376,14 +364,14 @@ void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws)
resetHeartbeatTimer();
}
active_clients++;
term_active_clients++;
}
ETSTimer xonTim;
static void ICACHE_FLASH_ATTR notify_empty_txbuf_cb(void *unused)
{
UART_WriteChar(UART1, '+', 100);
//UART_WriteChar(UART1, '+', 100);
cgiWebsockBroadcast(URL_WS_UPDATE, "+", 1, 0);
resetHeartbeatTimer();
browser_wants_xon = false;

@ -15,6 +15,8 @@ void send_beep(void);
/** open pop-up notification */
void notify_growl(char *msg);
extern volatile int term_active_clients;
// defined in the makefile
#if DEBUG_INPUT
#define inp_warn warn

@ -50,7 +50,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiResetDevice(HttpdConnData *connData)
os_timer_setfn(&tmr, tmrCb, NULL);
os_timer_arm(&tmr, 100, false);
httpdSend(connData, "system reset\n", -1);
httpdSend(connData, "system reset\r\n", -1);
return HTTPD_CGI_DONE;
}
@ -66,7 +66,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiPing(HttpdConnData *connData)
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
httpdSend(connData, "pong\n", -1);
httpdSend(connData, "pong\r\n", -1);
return HTTPD_CGI_DONE;
}

@ -40,7 +40,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
char buff[50];
char redir_url_buf[100];
int32 n, w, h;
bool notify_screen_content = 0, notify_screen_labels = 0;
ScreenNotifyTopics topics = 0;
bool shall_clear_screen = false;
bool shall_init_uart = false;
@ -94,7 +94,8 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (termconf->width != w || termconf->height != h) {
termconf->width = w;
termconf->height = h;
shall_clear_screen = true; // this causes a notify
shall_clear_screen = true;
topics |= TOPIC_CHANGE_SCREEN_OPTS | TOPIC_CHANGE_CONTENT_ALL;
}
} while (0);
}
@ -112,6 +113,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (termconf->default_bg != n) {
termconf->default_bg = n; // this is current not sent through socket, no use to notify
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
}
@ -128,6 +130,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (termconf->default_fg != n) {
termconf->default_fg = n; // this is current not sent through socket, no use to notify
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
}
@ -145,7 +148,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (GET_ARG("display_tout_ms")) {
cgi_dbg("Display update idle timeout: %s ms", buff);
n = atoi(buff);
if (n > 0) {
if (n >= 0) {
termconf->display_tout_ms = n;
} else {
cgi_warn("Bad update timeout %s", buff);
@ -168,40 +171,62 @@ cgiTermCfgSetParams(HttpdConnData *connData)
cgi_dbg("FN alt mode: %s", buff);
n = atoi(buff);
termconf->fn_alt_mode = (bool)n;
notify_screen_content = true;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
if (GET_ARG("want_all_fn")) {
cgi_dbg("AllFN mode: %s", buff);
n = atoi(buff);
termconf->want_all_fn = (bool)n;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
if (GET_ARG("crlf_mode")) {
cgi_dbg("CRLF mode: %s", buff);
n = atoi(buff);
termconf->crlf_mode = (bool)n;
notify_screen_content = true;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
if (GET_ARG("show_buttons")) {
cgi_dbg("Show buttons: %s", buff);
n = atoi(buff);
termconf->show_buttons = (bool)n;
notify_screen_content = true;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
if (GET_ARG("show_config_links")) {
cgi_dbg("Show config links: %s", buff);
n = atoi(buff);
termconf->show_config_links = (bool)n;
notify_screen_content = true;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
if (GET_ARG("loopback")) {
cgi_dbg("Loopback: %s", buff);
n = atoi(buff);
termconf->loopback = (bool)n;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
if (GET_ARG("debugbar")) {
cgi_dbg("Debugbar: %s", buff);
n = atoi(buff);
termconf->debugbar = (bool)n;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
if (GET_ARG("allow_decopt_12")) {
cgi_dbg("DECOPT 12: %s", buff);
n = atoi(buff);
termconf->allow_decopt_12 = (bool)n;
}
if (GET_ARG("ascii_debug")) {
cgi_dbg("ascii_debug: %s", buff);
n = atoi(buff);
termconf->ascii_debug = (bool)n;
shall_clear_screen = true;
}
if (GET_ARG("theme")) {
@ -213,6 +238,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
} else {
cgi_warn("Bad theme num: %s", buff);
redir_url += sprintf(redir_url, "theme,");
topics |= TOPIC_CHANGE_SCREEN_OPTS;
}
}
@ -221,7 +247,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
n = atoi(buff);
if (n >= 0 && n <= 6 && n != 1) {
termconf->cursor_shape = (enum CursorShape) n;
notify_screen_content = true;
topics |= TOPIC_CHANGE_SCREEN_OPTS;
} else {
cgi_warn("Bad cursor_shape num: %s", buff);
redir_url += sprintf(redir_url, "cursor_shape,");
@ -231,7 +257,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (GET_ARG("term_title")) {
cgi_dbg("Terminal title default text: \"%s\"", buff);
strncpy_safe(termconf->title, buff, 64); // ATTN those must match the values in
notify_screen_labels = true;
topics |= TOPIC_CHANGE_TITLE;
}
for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) {
@ -239,7 +265,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
if (GET_ARG(buff)) {
cgi_dbg("Button%d default text: \"%s\"", btn_i, buff);
strncpy_safe(termconf->btn[btn_i-1], buff, TERM_BTN_LEN);
notify_screen_labels = true;
topics |= TOPIC_CHANGE_BUTTONS;
}
sprintf(buff, "bm%d", btn_i);
@ -368,13 +394,7 @@ cgiTermCfgSetParams(HttpdConnData *connData)
serialInit();
}
if (notify_screen_content) {
screen_notifyChange(CHANGE_CONTENT);
}
if (notify_screen_labels) {
screen_notifyChange(CHANGE_LABELS);
}
if (topics) screen_notifyChange(topics);
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
} else {
@ -437,9 +457,18 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
else if (streq(token, "show_config_links")) {
sprintf(buff, "%d", (int)termconf->show_config_links);
}
else if (streq(token, "allow_decopt_12")) {
sprintf(buff, "%d", (int)termconf->allow_decopt_12);
}
else if (streq(token, "ascii_debug")) {
sprintf(buff, "%d", (int)termconf->ascii_debug);
}
else if (streq(token, "loopback")) {
sprintf(buff, "%d", (int)termconf->loopback);
}
else if (streq(token, "debugbar")) {
sprintf(buff, "%d", (int)termconf->debugbar);
}
else if (streq(token, "theme")) {
sprintf(buff, "%d", termconf->theme);
}

@ -617,22 +617,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
}
}
else if (streq(token, "sta_active_ip")) {
x = wifi_get_opmode();
connectStatus = wifi_station_get_connect_status();
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) {
strcpy(buff, "");
}
else {
struct ip_info info;
wifi_get_ip_info(STATION_IF, &info);
sprintf(buff, IPSTR, GOOD_IP2STR(info.ip.addr));
// sprintf(buff, "ip: "IPSTR", mask: "IPSTR", gw: "IPSTR,
// GOOD_IP2STR(info.ip.addr),
// GOOD_IP2STR(info.netmask.addr),
// GOOD_IP2STR(info.gw.addr));
}
getStaIpAsString(buff);
}
tplSend(connData, buff, -1);

@ -14,6 +14,8 @@
#include "cgi_persist.h"
#include "syscfg.h"
#include "persist.h"
#include "api.h"
#include "cgi_d2d.h"
/**
* Password for WiFi config
@ -45,7 +47,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData)
break;
case PWLOCK_SETTINGS_NOTERM:
protect = strstarts(connData->url, "/cfg") && !strstarts(connData->url, "/cfg/term");
protect = strstarts(connData->url, "/cfg") &&
!strstarts(connData->url, "/cfg/term");
break;
case PWLOCK_SETTINGS_ALL:
@ -53,7 +56,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData)
break;
case PWLOCK_MENUS:
protect = strstarts(connData->url, "/cfg") || strstarts(connData->url, "/about") || strstarts(connData->url, "/help");
protect = strstarts(connData->url, "/cfg") ||
strstarts(connData->url, "/about") ||
strstarts(connData->url, "/help");
break;
default:
@ -64,11 +69,11 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData)
// pages outside the normal scope
if (sysconf->pwlock > PWLOCK_NONE) {
if (strstarts(connData->url, "/system/reset")) protect = true;
if (strstarts(connData->url, "/api/v1/reboot")) protect = true;
}
if (sysconf->pwlock > PWLOCK_SETTINGS_NOTERM) {
if (strstarts(connData->url, "/system/cls")) protect = true;
if (strstarts(connData->url, "/api/v1/clear")) protect = true;
}
if (sysconf->access_pw[0] == 0) {
@ -103,9 +108,12 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
ROUTE_WS(URL_WS_UPDATE, updateSockConnect),
// --- System control ---
ROUTE_CGI("/system/reset/?", cgiResetDevice),
ROUTE_CGI("/system/ping/?", cgiPing),
ROUTE_CGI("/system/cls/?", cgiResetScreen),
// API endpoints
ROUTE_CGI(API_REBOOT"/?", cgiResetDevice),
ROUTE_CGI(API_PING"/?", cgiPing),
ROUTE_CGI(API_CLEAR"/?", cgiResetScreen),
ROUTE_CGI(API_D2D_MSG"/?", cgiD2DMessage),
ROUTE_REDIRECT("/cfg/?", "/cfg/wifi"),

@ -6,7 +6,4 @@
extern const HttpdBuiltInUrl routes[];
/** Broadcast screen state to sockets */
void screen_notifyChange();
#endif //ROUTES_H

File diff suppressed because it is too large Load Diff

@ -34,11 +34,6 @@
*
*/
// Size designed for the terminal config structure
// Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 300
#define TERMCONF_VERSION 0
#define TERM_BTN_LEN 10
#define TERM_BTN_MSG_LEN 10
#define TERM_TITLE_LEN 64
@ -70,10 +65,18 @@ enum CursorShape {
#define SCR_DEF_CURSOR_SHAPE CURSOR_BLOCK_BL
#define SCR_DEF_CRLF 0 // enter sends CRLF
#define SCR_DEF_ALLFN 0 // capture F5 etc
#define SCR_DEF_DEBUGBAR 0
#define SCR_DEF_DECOPT12 0
#define SCR_DEF_ASCIIDEBUG 0
// --- Persistent Settings ---
#define CURSOR_BLINKS(shape) ((shape)==CURSOR_BLOCK_BL||(shape)==CURSOR_UNDERLINE_BL||(shape)==CURSOR_BAR_BL)
// Size designed for the terminal config structure
// Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 300
#define TERMCONF_VERSION 3
typedef struct {
u32 width;
u32 height;
@ -94,6 +97,9 @@ typedef struct {
enum CursorShape cursor_shape;
bool crlf_mode;
bool want_all_fn;
bool debugbar;
bool allow_decopt_12;
bool ascii_debug;
} TerminalConfigBundle;
// Live config
@ -152,9 +158,29 @@ typedef enum {
CS_1_DOS_437 = '1',
} CHARSET;
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data);
enum ScreenSerializeTopic {
TOPIC_CHANGE_SCREEN_OPTS = (1<<0),
TOPIC_CHANGE_CONTENT_ALL = (1<<1),
TOPIC_CHANGE_CONTENT_PART = (1<<2),
TOPIC_CHANGE_TITLE = (1<<3),
TOPIC_CHANGE_BUTTONS = (1<<4),
TOPIC_CHANGE_CURSOR = (1<<5),
TOPIC_INTERNAL = (1<<6), // debugging internal state
TOPIC_BELL = (1<<7), // beep
TOPIC_FLAG_NOCLEAN = (1<<15), // do not clean dirty extents
// combos
TOPIC_INITIAL =
TOPIC_CHANGE_SCREEN_OPTS |
TOPIC_CHANGE_CONTENT_ALL |
TOPIC_CHANGE_CURSOR |
TOPIC_CHANGE_TITLE |
TOPIC_CHANGE_BUTTONS,
};
typedef u16 ScreenNotifyTopics;
void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len);
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, void **data);
// --- Clearing ---
@ -223,6 +249,8 @@ void screen_wrap_enable(bool enable);
void screen_reverse_wrap_enable(bool enable);
/** Set scrolling region */
void screen_set_scrolling_region(int from, int to);
/* Report scrolling region */
void screen_region_get(int *pv0, int *pv1);
/** Enable or disable origin remap to top left of scrolling region */
void screen_set_origin_mode(bool region_origin);
@ -231,18 +259,19 @@ void screen_set_origin_mode(bool region_origin);
typedef uint8_t Color;
typedef uint16_t CellAttrs;
// TODO sort by the expected frequency of being set - so when we switch to utf-8 encoding for data fields, it uses fewer bytes
#define ATTR_BOLD (1<<0) //!< Bold font
#define ATTR_FAINT (1<<1) //!< Faint foreground color (reduced alpha)
#define ATTR_ITALIC (1<<2) //!< Italic font
#define ATTR_UNDERLINE (1<<3) //!< Underline decoration
#define ATTR_BLINK (1<<4) //!< Blinking
#define ATTR_FRAKTUR (1<<5) //!< Fraktur font (unicode substitution)
#define ATTR_STRIKE (1<<6) //!< Strike-through decoration
#define ATTR_OVERLINE (1<<7) //!< Over-line decoration
#define ATTR_FG (1<<8) //!< 1 if not using default background color (ignore cell bg) - color extension bit
#define ATTR_BG (1<<9) //!< 1 if not using default foreground color (ignore cell fg) - color extension bit
#define ATTR_INVERSE (1<<10) //!< Invert colors - this is useful so we can clear then with SGR manipulation commands
enum SgrAttrBits {
ATTR_FG = (1<<0), //!< 1 if not using default background color (ignore cell bg) - color extension bit
ATTR_BG = (1<<1), //!< 1 if not using default foreground color (ignore cell fg) - color extension bit
ATTR_BOLD = (1<<2), //!< Bold font
ATTR_UNDERLINE = (1<<3), //!< Underline decoration
ATTR_INVERSE = (1<<4), //!< Invert colors - this is useful so we can clear then with SGR manipulation commands
ATTR_BLINK = (1<<5), //!< Blinking
ATTR_ITALIC = (1<<6), //!< Italic font
ATTR_STRIKE = (1<<7), //!< Strike-through decoration
ATTR_OVERLINE = (1<<8), //!< Over-line decoration
ATTR_FAINT = (1<<9), //!< Faint foreground color (reduced alpha)
ATTR_FRAKTUR = (1<<10), //!< Fraktur font (unicode substitution)
};
/** Set cursor foreground color */
void screen_set_fg(Color color);
@ -323,18 +352,14 @@ void screen_repeat_last_character(int count);
/** Report current SGR as num;num;... for DAC query */
void screen_report_sgr(char *buffer);
// --- Notify ---
typedef enum {
CHANGE_CONTENT = 0,
CHANGE_LABELS = 1,
} ScreenNotifyChangeTopic;
/**
* Called when the screen content or settings change
* and the front-end should redraw / update.
* @param topic - what kind of change this is (chooses what message to send)
*/
extern void screen_notifyChange(ScreenNotifyChangeTopic topic);
extern void screen_notifyChange(ScreenNotifyTopics topics);
#define seri_dbg(...)
#define seri_warn(...) warn(__VA_ARGS__)
#endif // SCREEN_H

@ -40,10 +40,10 @@ buf_pop(void *unused)
}
}
//LOCAL void my_putc(char c)
//{
// UART_WriteCharCRLF(UART1, (u8) c, 10);
//}
LOCAL void my_putc(char c)
{
UART_WriteCharCRLF(UART1, (u8) c, 10);
}
/**
* Init the serial ports
@ -56,8 +56,11 @@ void ICACHE_FLASH_ATTR serialInitBase(void)
UART_SetStopBits(UART1, ONE_STOP_BIT);
UART_SetBaudrate(UART1, BIT_RATE_115200);
UART_SetPrintPort(UART1);
#if ASYNC_LOG
os_install_putc1(buf_putc);
//os_install_putc1(my_putc);
#else
os_install_putc1(my_putc);
#endif
UART_SetupAsyncReceiver();
// 1 ms timer
@ -92,4 +95,5 @@ void ICACHE_FLASH_ATTR serialInit(void)
void ICACHE_FLASH_ATTR UART_HandleRxByte(char c)
{
ansi_parser(c);
system_soft_wdt_feed(); // so we survive long torrents
}

@ -8,8 +8,8 @@
#include <esp8266.h>
#include <uart_register.h>
#define UART_TX_BUFFER_SIZE 512 //Ring buffer length of tx buffer
#define UART_RX_BUFFER_SIZE 600 //Ring buffer length of rx buffer
//#define buf_dbg(format, ...) printf(format "\r\n", ##__VA_ARGS__)
#define buf_dbg(format, ...) (void)format
struct UartBuffer {
uint32 UartBuffSize;
@ -22,12 +22,15 @@ struct UartBuffer {
static struct UartBuffer *pTxBuffer = NULL;
static struct UartBuffer *pRxBuffer = NULL;
static struct UartBuffer *UART_AsyncBufferInit(uint32 buf_size);
static u8 rxArray[UART_RX_BUFFER_SIZE];
static u8 txArray[UART_TX_BUFFER_SIZE];
static struct UartBuffer *UART_AsyncBufferInit(uint32 buf_size, u8 *buffer);
void ICACHE_FLASH_ATTR UART_AllocBuffers(void)
{
pTxBuffer = UART_AsyncBufferInit(UART_TX_BUFFER_SIZE);
pRxBuffer = UART_AsyncBufferInit(UART_RX_BUFFER_SIZE);
pTxBuffer = UART_AsyncBufferInit(UART_TX_BUFFER_SIZE, txArray);
pRxBuffer = UART_AsyncBufferInit(UART_RX_BUFFER_SIZE, rxArray);
}
/******************************************************************************
@ -37,7 +40,7 @@ void ICACHE_FLASH_ATTR UART_AllocBuffers(void)
* Returns : NONE
*******************************************************************************/
static struct UartBuffer *ICACHE_FLASH_ATTR
UART_AsyncBufferInit(uint32 buf_size)
UART_AsyncBufferInit(uint32 buf_size, u8 *buffer)
{
uint32 heap_size = system_get_free_heap_size();
if (heap_size <= buf_size) {
@ -47,7 +50,7 @@ UART_AsyncBufferInit(uint32 buf_size)
else {
struct UartBuffer *pBuff = (struct UartBuffer *) malloc(sizeof(struct UartBuffer));
pBuff->UartBuffSize = buf_size;
pBuff->pUartBuff = (uint8 *) malloc(pBuff->UartBuffSize);
pBuff->pUartBuff = buffer != NULL ? buffer : (uint8 *) malloc(pBuff->UartBuffSize);
pBuff->pInPos = pBuff->pUartBuff;
pBuff->pOutPos = pBuff->pUartBuff;
pBuff->Space = (uint16) pBuff->UartBuffSize;
@ -55,6 +58,13 @@ UART_AsyncBufferInit(uint32 buf_size)
}
}
static void ICACHE_FLASH_ATTR
UART_AsyncBufferReset(struct UartBuffer *pBuff)
{
pBuff->pInPos = pBuff->pUartBuff;
pBuff->pOutPos = pBuff->pUartBuff;
pBuff->Space = (uint16) pBuff->UartBuffSize;
}
/**
* Copy data onto Buffer
@ -67,23 +77,30 @@ UART_WriteToAsyncBuffer(struct UartBuffer *pCur, const char *pdata, uint16 data_
{
if (data_len == 0) return;
buf_dbg("WTAB %d, space %d", data_len, pCur->Space);
uint16 tail_len = (uint16) (pCur->pUartBuff + pCur->UartBuffSize - pCur->pInPos);
if (tail_len >= data_len) { //do not need to loop back the queue
buf_dbg("tail %d, no fold", tail_len);
memcpy(pCur->pInPos, pdata, data_len);
pCur->pInPos += (data_len);
pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
pCur->Space -= data_len;
}
else {
buf_dbg("tail only %d, folding", tail_len);
memcpy(pCur->pInPos, pdata, tail_len);
buf_dbg("chunk 1, %d", tail_len);
pCur->pInPos += (tail_len);
pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
pCur->Space -= tail_len;
buf_dbg("chunk 2, %d", data_len - tail_len);
memcpy(pCur->pInPos, pdata + tail_len, data_len - tail_len);
pCur->pInPos += (data_len - tail_len);
pCur->pInPos = (pCur->pUartBuff + (pCur->pInPos - pCur->pUartBuff) % pCur->UartBuffSize);
pCur->Space -= (data_len - tail_len);
}
buf_dbg("new space %d", pCur->Space);
}
/******************************************************************************
@ -158,9 +175,8 @@ void UART_RxFifoCollect(void)
uint8 fifo_data;
fifo_len = (uint8) ((READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT);
if (fifo_len >= pRxBuffer->Space) {
// try to read at least the bit we can
fifo_len = (uint8) (pRxBuffer->Space - 1);
UART_WriteChar(UART1, '%', 1);
UART_WriteChar(UART1, '#', 10);
// discard contents of the FIFO - would loop forever
buf_idx = 0;
while (buf_idx < fifo_len) {
@ -193,35 +209,30 @@ u16 ICACHE_FLASH_ATTR UART_AsyncTxGetEmptySpace(void)
return pTxBuffer->Space;
}
u16 ICACHE_FLASH_ATTR UART_AsyncTxCount(void)
{
return (u16) (pTxBuffer->UartBuffSize - pTxBuffer->Space);
}
/**
* Schedule data to be sent
* @param pdata
* @param data_len - can be -1 for strlen
*/
void ICACHE_FLASH_ATTR
UART_SendAsync(const char *pdata, int16_t data_len)
UART_SendAsync(const char *pdata, int data_len)
{
u16 real_len = (u16) data_len;
if (data_len <= 0) real_len = (u16) strlen(pdata);
size_t real_len = (data_len) <= 0 ? strlen(pdata) : (size_t) data_len;
// if (pTxBuffer == NULL) {
// printf("init tx buf\n\r");
// pTxBuffer = UART_AsyncBufferInit(UART_TX_BUFFER_SIZE);
// if (pTxBuffer != NULL) {
// UART_WriteToAsyncBuffer(pTxBuffer, pdata, real_len);
// }
// else {
// printf("tx alloc fail\r\n");
// }
// }
// else {
if (real_len <= pTxBuffer->Space) {
UART_WriteToAsyncBuffer(pTxBuffer, pdata, real_len);
}
else {
UART_WriteChar(UART1, '^', 1);
}
// }
buf_dbg("Send Async %d", real_len);
if (real_len <= pTxBuffer->Space) {
buf_dbg("accepted, space %d", pTxBuffer->Space);
UART_WriteToAsyncBuffer(pTxBuffer, pdata, (uint16) real_len);
}
else {
buf_dbg("FULL!");
UART_WriteChar(UART1, '=', 10);
}
// Here we enable TX empty interrupt that will take care of sending the content
SET_PERI_REG_MASK(UART_CONF1(UART0), (UART_TX_EMPTY_THRESH_VAL & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S);
@ -257,8 +268,8 @@ void UART_DispatchFromTxBuffer(uint8 uart_no)
uint8 len_tmp;
uint16 data_len;
// if (pTxBuffer) {
data_len = (uint8) (pTxBuffer->UartBuffSize - pTxBuffer->Space);
data_len = (uint16) (pTxBuffer->UartBuffSize - pTxBuffer->Space);
buf_dbg("rem %d",data_len);
if (data_len > fifo_remain) {
len_tmp = fifo_remain;
UART_TxFifoEnq(pTxBuffer, len_tmp, uart_no);
@ -268,8 +279,9 @@ void UART_DispatchFromTxBuffer(uint8 uart_no)
len_tmp = (uint8) data_len;
UART_TxFifoEnq(pTxBuffer, len_tmp, uart_no);
// we get one more IT after fifo ends even if we have 0 more bytes
// for notify
// We get one more IT after fifo ends even if we have 0 more bytes,
// for notify. Otherwise we would say we have space while the FIFO
// was still running
if (next_empty_it_only_for_notify) {
notify_empty_txbuf();
next_empty_it_only_for_notify = 0;
@ -279,9 +291,4 @@ void UART_DispatchFromTxBuffer(uint8 uart_no)
SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
}
}
// }
// else {
// error("pTxBuff null \n\r");
// }
}

@ -7,6 +7,9 @@
#include <esp8266.h>
#define UART_TX_BUFFER_SIZE 1000 //Ring buffer length of tx buffer
#define UART_RX_BUFFER_SIZE 600 //Ring buffer length of rx buffer
// the init func
void UART_AllocBuffers(void);
@ -14,7 +17,7 @@ void UART_AllocBuffers(void);
uint16 UART_ReadAsync(char *pdata, uint16 data_len);
// write to tx buffer
void UART_SendAsync(const char *pdata, int16_t data_len);
void UART_SendAsync(const char *pdata, int data_len);
//move data from uart fifo to rx buffer
void UART_RxFifoCollect(void);
@ -22,6 +25,7 @@ void UART_RxFifoCollect(void);
void UART_DispatchFromTxBuffer(uint8 uart_no);
u16 UART_AsyncRxCount(void);
u16 UART_AsyncTxCount(void);
u16 UART_AsyncTxGetEmptySpace(void);

@ -24,8 +24,8 @@ static void uart_recvTask(os_event_t *events);
static void uart_processTask(os_event_t *events);
// Those heavily affect the byte loss ratio
#define PROCESS_CHUNK_LEN 1
#define FIFO_FULL_THRES 32
#define PROCESS_CHUNK_LEN 10
#define RX_FIFO_FULL_THRES 40
#define uart_recvTaskPrio 1
#define uart_recvTaskQueueLen 25
@ -52,6 +52,7 @@ void ICACHE_FLASH_ATTR UART_Init(void)
// U0RXD
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD);
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0RXD_U);
// U1TXD (GPIO2)
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
@ -78,8 +79,8 @@ void ICACHE_FLASH_ATTR UART_SetupAsyncReceiver(void)
ETS_UART_INTR_ATTACH((void *)uart0_rx_intr_handler, &(UartDev.rcv_buff)); // the buf will be used as an arg
// fifo threshold config (max: UART_RXFIFO_FULL_THRHD = 127)
uint32_t conf = ((FIFO_FULL_THRES & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S);
conf |= ((0x10 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S);
uint32_t conf = ((RX_FIFO_FULL_THRES & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S);
conf |= ((0x05 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S);
// timeout config
conf |= ((0x06 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S); // timeout threshold
conf |= UART_RX_TOUT_EN; // enable timeout

@ -28,6 +28,9 @@
#include "ansi_parser_callbacks.h"
#include "wifimgr.h"
#include "persist.h"
#include "ansi_parser.h"
#include "ascii.h"
#include "uart_buffer.h"
#ifdef ESPFS_POS
CgiUploadFlashDef uploadParams={
@ -49,6 +52,7 @@ CgiUploadFlashDef uploadParams={
#define INCLUDE_FLASH_FNS
#endif
#define HEAP_TIMER_MS 1000
/** Periodically show heap usage */
static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg)
{
@ -58,18 +62,24 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg)
int heap = system_get_free_heap_size();
int diff = (heap-last);
int rxc = UART_AsyncRxCount();
int txc = UART_AsyncTxCount();
int rxp = ((rxc*10000) / UART_RX_BUFFER_SIZE)/100;
int txp = ((txc*10000) / UART_TX_BUFFER_SIZE)/100;
const char *cc = "+";
if (diff<0) cc = "";
if (diff == 0) {
if (cnt == 5) {
// only every 5 secs if no change
dbg("FH: %d", heap);
dbg("Rx/Tx: %d/%d%c, Hp: %d", rxp, txp, '%', heap);
cnt = 0;
}
} else {
// report change
dbg("FH: %d (%s%d)", heap, cc, diff);
dbg("Rx/Tx: %d/%d%c, Hp: %d (%s%d)", rxp, txp, '%', heap, cc, diff);
cnt = 0;
}
@ -86,6 +96,7 @@ static ETSTimer prHeapTimer;
//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done.
void ICACHE_FLASH_ATTR user_init(void)
{
ansi_parser_inhibit = true;
serialInitBase();
// Prevent WiFi starting and connecting by default
@ -93,13 +104,22 @@ void ICACHE_FLASH_ATTR user_init(void)
wifi_station_set_auto_connect(false);
wifi_set_opmode(NULL_MODE); // saves to flash if changed - this might avoid the current spike on startup?
printf("\r\n");
banner("====== ESPTerm ======");
banner_info("Firmware (c) Ondrej Hruska, 2017");
banner_info(TERMINAL_GITHUB_REPO);
banner_info("");
banner_info("Version "FIRMWARE_VERSION", built " __DATE__ " at " __TIME__ " " __TIMEZONE__);
printf("\r\n");
u8 mac[6];
wifi_get_macaddr(SOFTAP_IF, mac);
banner_gap();
banner("================ ESPTerm ================");
banner_info();
banner_info("Project by Ondrej Hruska, 2017");
banner_info();
banner_info(TERMINAL_GITHUB_REPO_NOPROTO);
banner_info();
banner_info("Version "FW_VERSION" ("ESP_LANG"), code name "FW_CODENAME_QUOTED);
banner_info(" back-end #"GIT_HASH_BACKEND" front-end #"GIT_HASH_FRONTEND);
banner_info(" built "__DATE__" at "__TIME__" "__TIMEZONE__);
banner_info();
banner_info("Device ID: %02X%02X%02X", mac[3], mac[4], mac[5]);
banner_gap();
ioInit();
@ -111,11 +131,6 @@ void ICACHE_FLASH_ATTR user_init(void)
espFsInit((void *) (webpages_espfs_start));
#endif
#if DEBUG_HEAP
// Heap use timer & blink
TIMER_START(&prHeapTimer, prHeapTimerCb, 1000, 1);
#endif
// do later (some functions do not work if called from user_init)
TIMER_START(&userStartTimer, user_start, 10, 0);
}
@ -127,10 +142,18 @@ static void ICACHE_FLASH_ATTR user_start(void *unused)
captdnsInit();
httpdInit(routes, 80);
httpdSetName("ESPTerm " VERSION_STRING);
ansi_parser_inhibit = false;
// Print the CANCEL character to indicate the module has restarted
// Critically important for client application if any kind of screen persistence / content re-use is needed
UART_WriteChar(UART0, 24, UART_TIMEOUT_US); // 0x18 - 24 - CAN
UART_WriteChar(UART0, CAN, UART_TIMEOUT_US); // 0x18 - 24 - CAN
#if DEBUG_HEAP
// Heap use timer & blink
TIMER_START(&prHeapTimer, prHeapTimerCb, HEAP_TIMER_MS, 1);
#endif
}
// ---- unused funcs removed from sdk to save space ---

@ -160,8 +160,13 @@ unicode_cache_remove(UnicodeCacheRef ref)
* @return number of bytes on success, 0 on failure (also produces U+FFFD, which uses 3 bytes)
*/
int ICACHE_FLASH_ATTR
utf8_encode(char *out, uint32_t utf)
utf8_encode(char *out, uint32_t utf, bool surrogateFix)
{
// Skip the surrogate block (wtf, unicode???)
if (surrogateFix && utf >= 0xD800) {
utf += 0x800;
}
if (utf <= 0x7F) {
// Plain ASCII
out[0] = (char) utf;

@ -63,9 +63,10 @@ bool unicode_cache_remove(UnicodeCacheRef ref);
*
* @param out - output buffer (min 4 characters), will be 0-terminated if shorten than 4
* @param utf - code point 0-0x10FFFF
* @param surrogateFix - add 0x800 to 0xD800-0xDFFF to avoid invalid code points
* @return number of bytes on success, 0 on failure (also produces U+FFFD, which uses 3 bytes)
*/
int utf8_encode(char *out, uint32_t utf);
int utf8_encode(char *out, uint32_t utf, bool surrogateFix);
#if DEBUG_UTFCACHE
#define utfc_warn warn

@ -5,13 +5,26 @@
#ifndef ESP_VT100_FIRMWARE_VERSION_H
#define ESP_VT100_FIRMWARE_VERSION_H
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#define FW_V_MAJOR 2
#define FW_V_MINOR 0
#define FW_V_MINOR 1
#define FW_V_PATCH 0
#define FW_V_SUFFIX "-beta3"
//#define FW_V_SUFFIX ""
#define FW_CODENAME "Anthill" // 2.1.0
#define FW_CODENAME_QUOTED "\""FW_CODENAME"\""
#define FW_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) FW_V_SUFFIX
#define VERSION_STRING FW_VERSION " " FW_CODENAME_QUOTED
#define FIRMWARE_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH)
#define FIRMWARE_VERSION_NUM (FW_V_MAJOR*1000 + FW_V_MINOR*10 + FW_V_PATCH) // this is used in ID queries
#define TERMINAL_GITHUB_REPO "https://github.com/espterm/espterm-firmware"
#define TERMINAL_GITHUB_REPO_NOPROTO "github.com/espterm/espterm-firmware"
#define TERMINAL_GITHUB_REPO "https://"TERMINAL_GITHUB_REPO_NOPROTO
#define TERMINAL_GITHUB_REPO_FRONT "https://github.com/espterm/espterm-front-end"
#endif //ESP_VT100_FIRMWARE_VERSION_H

@ -8,6 +8,22 @@
WiFiConfigBundle * const wificonf = &persist.current.wificonf;
WiFiConfChangeFlags wifi_change_flags;
int ICACHE_FLASH_ATTR getStaIpAsString(char *buffer)
{
WIFI_MODE x = wifi_get_opmode();
STATION_STATUS connectStatus = wifi_station_get_connect_status();
if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP || wificonf->opmode == SOFTAP_MODE) {
strcpy(buffer, "");
return 0;
}
else {
struct ip_info info;
wifi_get_ip_info(STATION_IF, &info);
return sprintf(buffer, IPSTR, GOOD_IP2STR(info.ip.addr));
}
}
/**
* Restore defaults in the WiFi config block.
* This is to be called if the WiFi config is corrupted on startup,

@ -60,6 +60,8 @@ void wifimgr_restore_defaults(void);
void wifimgr_apply_settings(void);
int getStaIpAsString(char *buffer);
#if DEBUG_WIFI
#define wifi_warn warn
#define wifi_dbg dbg

Loading…
Cancel
Save