Compare commits

..
123 Commits
Author SHA1 Message Date
MightyPork 76fbf5d65f Merge branch 'work' 2017-11-19 23:07:24 +01:00
MightyPork 6b4013f779 v bump 2017-11-19 22:55:54 +01:00
MightyPork 0438f88c83 update frontend ref 2017-11-12 12:57:31 +01:00
MightyPork d0d7200e57 Merge branch 'double-lines' into work 2017-11-12 12:57:04 +01:00
MightyPork 78aaa949a3 support double lines 2017-11-12 11:40:36 +01:00
MightyPork 5dbb592946 added OSC for color buttons 2017-10-30 21:25:56 +01:00
MightyPork d9aa0418fa updated protocol, added button themeing and font conf 2017-10-30 20:47:41 +01:00
MightyPork 9008f2e1af ref upd 2017-10-26 00:57:40 +02:00
MightyPork bc240e676b added hu to build script 2017-10-26 00:51:51 +02:00
MightyPork 3af928b39d version bump for pre-release build 2017-10-26 00:45:43 +02:00
MightyPork ead9120ae8 ref update 2017-10-26 00:41:24 +02:00
MightyPork dca44963e8 add new OSC and configurable button count 2017-10-26 00:31:10 +02:00
MightyPork 3c12efe28f updated submodule ref 2017-10-25 23:24:07 +02:00
MightyPork e98588b317 Fix broken validation for bm* 2017-10-23 23:49:19 +02:00
MightyPork 0e6c297fe4 be smarter about detecting changes in loaded ini 2017-10-23 23:24:20 +02:00
MightyPork 3e4181f18e Merge branch 'xmacro-config' into work 2017-10-23 22:43:34 +02:00
MightyPork 2f4cd08c60 import now fully working 2017-10-23 22:43:04 +02:00
MightyPork ba2b73a287 ini upload and parse (no loading to conf yet) 2017-10-23 01:40:56 +02:00
MightyPork 4e285694d1 ini export! 2017-10-22 23:41:07 +02:00
MightyPork 9700465927 fixed many bugs 2017-10-22 22:23:09 +02:00
MightyPork 1ab4101ac0 use xmacros for terminal config 2017-10-22 20:30:24 +02:00
MightyPork f0bc70553a use xmacros for system 2017-10-22 17:55:52 +02:00
MightyPork 3eac0ee794 fmt again 2017-10-22 16:50:45 +02:00
MightyPork 2c80b59d71 formatting 2017-10-22 16:46:28 +02:00
MightyPork 9146c9880a slightly cleaner XTABLE_WIFI 2017-10-22 16:45:45 +02:00
MightyPork cabd42f2b4 base of xmarco-based config, used for wifi and network 2017-10-22 16:34:00 +02:00
MightyPork fbd4693035 Merge branch 'master' into work 2017-10-08 13:26:40 +02:00
MightyPork c5670c09bb update frontend ref 2017-10-08 12:57:36 +02:00
MightyPork 4e74d1cd79 updated front-end ref 2017-10-08 12:54:26 +02:00
MightyPork 4c8360dbb5 Merge remote-tracking branch 'origin/work' 2017-10-08 12:35:21 +02:00
MightyPork c0adc253b4 add cgi for setting background image 2017-10-08 12:34:50 +02:00
MightyPork be3e06aac1 version bump 2017-10-08 11:21:11 +02:00
MightyPork 992c927490 Merge remote-tracking branch 'origin/work' 2017-10-08 11:16:44 +02:00
MightyPork ef3b02e68c added new chars to cp 1 2017-10-07 00:46:19 +02:00
MightyPork 1c556457c7 Merge branch 'new-codepages' into work 2017-10-07 00:15:34 +02:00
MightyPork e49a8b0adf added codepages 2 and 3 2017-10-06 23:25:01 +02:00
MightyPork 81c3e426e7 added quarter blocks to codepage 1 2017-10-06 22:28:36 +02:00
MightyPorkandGitHub c90de79a34 Update README.md 2017-10-06 00:12:28 +02:00
MightyPork df404f5f63 2.1.1 - patched cslang 2017-10-04 21:47:32 +02:00
MightyPork 00153656f1 flip front-end back to "work" 2017-10-04 00:22:08 +02:00
MightyPork aa920aee27 Merge branch 'master' into work 2017-10-04 00:21:45 +02:00
MightyPork 7b8c936559 Version bump 2017-10-04 00:13:14 +02:00
MightyPork 17c18ed1c9 update front-end to latest master 2017-10-04 00:12:47 +02:00
MightyPork 274e3b359b Merge branch 'work' 2017-10-04 00:11:26 +02:00
MightyPork 73c98eb2d1 some logging adjustments 2017-10-04 00:10:54 +02:00
MightyPork 022e882001 added IP to answerback message 2017-10-03 22:16:07 +02:00
MightyPork dc08512397 one timezone is probably enough in the release readme 2017-10-03 20:58:00 +02:00
MightyPork 6c1ab28e75 improved build script, automatic version fetch from source 2017-10-03 20:30:53 +02:00
MightyPork 2796709624 version bump to beta2 2017-10-03 01:55:46 +02:00
MightyPork d2edcd7fd5 added new release system 2017-10-03 00:59:21 +02:00
MightyPork 76272530ec improved ENQ response, now includes hashes and ID. Added ID to boot msg 2017-10-02 22:55:56 +02:00
MightyPork a514601897 use async buffer for apars respond other than http 2017-10-02 22:20:38 +02:00
MightyPork 935e05a56a Disable Rx pin pull-up, caused malfunction with cheap adapters 2017-10-02 19:51:58 +02:00
MightyPork a72e2bed63 makefile improvements 2017-10-02 03:18:43 +02:00
MightyPork 679f0adbae Merge branch 'http-comm' into work 2017-10-02 01:40:45 +02:00
MightyPork 791f6e716f finished basic d2d and external, adde flags etc 2017-10-02 01:40:26 +02:00
MightyPork 9a5bc0a274 Add codename 2017-10-01 22:56:13 +02:00
MightyPork dca02976a7 Fixed a horrible MegaBug and added HTTP message sending + D2D sending (no Rx yet) 2017-10-01 19:46:14 +02:00
MightyPork 6665b5c183 CAN and SUB correctly abort current sequence, including string. Start of adding PM's 2017-10-01 14:09:39 +02:00
MightyPork 55afcca955 fix incorrect DCS command handling 2017-10-01 13:47:22 +02:00
MightyPork 7ffc947f1a added client + testing command ESC#7 2017-10-01 12:54:52 +02:00
MightyPork 0eeed9c7d9 add %debugbar% to terminal page 2017-10-01 03:42:15 +02:00
MightyPork 253473daed version bump for next rel 2017-10-01 03:35:52 +02:00
MightyPork 9321cd4ebb made it survive idiots cat'ing image files etc 2017-10-01 03:35:31 +02:00
MightyPork faffa68794 use binary messages and fix bugs in utf sanitizer 2017-10-01 03:23:04 +02:00
MightyPork a6ee808a1d add SKIP tag to defines 2017-09-30 22:59:46 +02:00
MightyPork 5150cc32eb align serializer with front-end changes 2017-09-30 22:57:40 +02:00
MightyPork 42d5f6bc89 decopt 12 configurable 2017-09-30 19:11:34 +02:00
MightyPork d2fe37403f Fix htop display bugs - wrong serialization 2017-09-30 17:48:42 +02:00
MightyPork f6c2b06d8a notify about mouse attrs change 2017-09-30 15:10:52 +02:00
MightyPork b6c5648690 allow zero redraw delay 2017-09-30 15:02:19 +02:00
MightyPork 062b0e6188 fix messed up bg color encoding 2017-09-30 13:52:28 +02:00
MightyPork c83b68405f Merge branch 'partial-updates' into work 2017-09-30 02:55:54 +02:00
MightyPork 97552b30c3 fix some bugs + optimize stuff 2017-09-30 02:40:24 +02:00
MightyPork 1c23e79c59 partial update, may be a bit buggy in places 2017-09-30 01:18:03 +02:00
MightyPork b70c65868b dump context and stop listening for a bit on rx of bad UTF 2017-09-29 21:57:20 +02:00
MightyPork 1614194d76 Removed one NOTIFY_LOCK() that shouldn't have been there 2017-09-29 20:49:52 +02:00
MightyPork 7bca46384e Merge branch 'new-message-format' into work 2017-09-28 23:11:28 +02:00
MightyPork 03c7f4d0ad removed bugs 2017-09-28 22:09:30 +02:00
MightyPork da6c5bf5be re-arranged attr bits and changed string encoding for labels and title to use terminator rather than byte length 2017-09-28 21:50:51 +02:00
MightyPork 2e0648be8e re-ordered cell attr bits for shorter serialize word length 2017-09-28 21:13:10 +02:00
MightyPork 404ccaeb20 new message format 2017-09-28 20:58:45 +02:00
MightyPork 7300e0003c Merge branch 'master' into work 2017-09-26 17:42:55 +02:00
MightyPork 8c55202b42 added exampe of the main config file 2017-09-26 17:41:49 +02:00
MightyPork 65977bfbae Overclock, fix buffer overflow causing total lock-up, small tweaks
removed the main make config file from VCS so it can be easier edited
added overclocking option to enable 160 MHz mode
2017-09-26 17:41:24 +02:00
MightyPork 74d52a0c61 add confirmation messages to form saving 2017-09-26 15:01:22 +02:00
MightyPork 0fae510207 change version to 2.0.0 to indicate the breaking change 2017-09-26 13:43:08 +02:00
MightyPork 1a8d3cb332 show real sdk version, make tpw setting actually work 2017-09-26 11:30:58 +02:00
MightyPork ca5a7e3de9 Merge remote-tracking branch 'origin/work' 2017-09-26 10:53:35 +02:00
MightyPork fa5489ccfc support for truecolor default colors 2017-09-25 01:53:59 +02:00
MightyPork 502babf7b0 updated front-end ref 2017-09-24 20:50:01 +02:00
MightyPork 47f6cd38cd Merge branch 'utf-removal-fix' into work 2017-09-24 20:36:38 +02:00
MightyPork 0f10d84a59 disable non-error ansi logging 2017-09-24 20:36:09 +02:00
MightyPork cb71586138 remove spammy debug 2017-09-24 20:35:55 +02:00
MightyPork 58bd27a082 FIXED A BUG 2017-09-24 20:19:40 +02:00
MightyPork 7eb1bdeec6 fixed off-by one etc errors in the rewritten functions, fixed a lag due to heartbeat packet 2017-09-24 18:16:21 +02:00
MightyPork 674e89c8c3 fix a bug with backspace when cursor is hanging 2017-09-24 14:57:10 +02:00
MightyPork 62a0925586 fix stuff not removed from utf cache in some operations 2017-09-24 14:17:41 +02:00
MightyPork 4f4ea95c06 Merge branch 'offscale-bg' into work 2017-09-24 03:03:52 +02:00
MightyPork 2ede271c65 removed obsolete microoptimization from parser repeat code 2017-09-24 03:02:28 +02:00
MightyPork 692b2c4d11 fix serializer being completely broken 2017-09-24 00:17:58 +02:00
MightyPork 948bcb62ff Backend changes for offscale default fg and bg + future-proofing
- 16-bit attribs
- added attribs FG and BG, if not set, default colors
- added attrib INVERT, removed server-side color flipping
- added screen attrib REVERSE_VIDEO (can't be done server-side if we want it to work with off-scale colors)
- changed Attribs encoding to 3B
- removed old migrations, made useless by the flash wipe caused by adding Admin PW section
- some cleaning in CSI parser
- removed function to set SGR inverse separately (now use normal SGR setter)
- added functions to set fg and bg to defaults
2017-09-23 20:50:37 +02:00
MightyPork 7607b639b0 Merge branch 'passwords-stuff' into work 2017-09-23 02:14:27 +02:00
MightyPork 2e8ec41115 lib bump 2017-09-23 02:13:02 +02:00
MightyPork 80e8e4f7da added username config for basic auth 2017-09-23 01:59:53 +02:00
MightyPork 357a9d43e3 password changing, changed default pw to "adminpw", added settings revert if validation fails 2017-09-23 01:33:55 +02:00
MightyPork a464a73a0e cgi form handlers for setting password stuff 2017-09-22 01:05:13 +02:00
MightyPork 38b3ce2dc8 cgi handler for setting passwords (not tested) 2017-09-22 01:00:52 +02:00
MightyPork 37de3a095d configurable pw lock (not yet tested and not connected to front-end) 2017-09-22 00:48:57 +02:00
MightyPork 82961568b8 increased sysconbf allocated flash size (safe now bc we alrady have a breaking change that wipes old settings) 2017-09-22 00:33:03 +02:00
MightyPork 3849e161e5 add pwlock settings to sysconf 2017-09-22 00:30:16 +02:00
MightyPork 43241c2679 admin pw now lives in a separate config block. BREAKING CHANGE! will wipe old settings :( 2017-09-22 00:22:46 +02:00
MightyPorkandGitHub 3bfd51061e added browser support list 2017-09-19 01:44:52 +02:00
MightyPork 781cf8825b remove theme max num chekc to allow adding themes purely by front-end 2017-09-18 01:58:15 +02:00
MightyPork 079b522540 implemented focus tracking reports 2017-09-17 20:18:57 +02:00
MightyPork 471aa95c34 add timezone to the about page 2017-09-17 19:18:56 +02:00
MightyPork ac56b1dc61 added timezone to bootlog 2017-09-17 19:14:24 +02:00
MightyPork 82fd9e5ad1 raise utf8 cache to max of 160 characters, re-enable buttons by default 2017-09-17 19:10:16 +02:00
MightyPork 92a454a2cf renamed input debug log funcs to imp_ to avoid build collision if DEBUG_WS is enabled also 2017-09-16 01:46:41 +02:00
MightyPork 0012c92c4a added front-end patch 2017-09-14 23:24:35 +02:00
MightyPorkandGitHub 0e8af5712b add info about the mailing list 2017-09-14 01:34:47 +02:00
MightyPork 184c772b57 update frontend ref 2017-09-13 02:30:34 +02:00
MightyPork 62888d4473 update frontend to use branch master 2017-09-13 01:33:10 +02:00
64 changed files with 5446 additions and 1686 deletions
+3
View File
@@ -12,6 +12,7 @@ html_compressed/
ldscript_memspecific.ld
eagle.app.sym
node_modules
esphttpdconfig.mk
# Garbage added by CLion
.idea/
@@ -19,3 +20,5 @@ cmake-build-debug/
.sass-cache
*.map
!.gitignore
+43 -5
View File
@@ -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,21 @@ 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/ini_parser.h
user/ini_parser.c
user/ini_parser.rl
user/utf8.h
user/utf8.c
user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c)
include_directories(include)
include_directories(libesphttpd/esphttpclient)
include_directories(user)
include_directories(libesphttpd/include)
include_directories(libesphttpd/espfs)
@@ -153,16 +166,41 @@ include_directories(esp_iot_sdk_v1.5.2/include)
add_definitions(
-D__ets__
-DICACHE_FLASH
-DDEBUG_LOGBUF_SIZE=2048
-DUSE_OPTIMIZE_PRINTF=1
-DHTTPD_MAX_CONNECTIONS=5
-DHTTPD_STACKSIZE=1000
-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
-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})
+13 -2
View File
@@ -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 += -DADMIN_PASSWORD=$(ADMIN_PASSWORD)
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 $@"
+63 -20
View File
@@ -5,21 +5,24 @@
![Photo][photo-hw]<br>
*Fig 1: Breadboard adapter developed for ESPTerm*
As of release 1.0, ESPTerm **passes most of VTTEST test cases** (from the main menu and some Xterm specific), making it
functionally comparable to eg. gnome-terminal, terminator, konsole, GtkTerm or PuTTY.
ESPTerm **passes most of VTTEST test cases**, making it functionally comparable to eg.
gnome-terminal, terminator, konsole, GtkTerm or PuTTY.
ESPTerm is **capable of running Midnight Commander** through agetty, **including full
mouse support**, provided agetty is made to believe it's Xterm, which shows ESPTerm is sufficiently well
implemented to work with ncurses.
mouse support**, provided agetty is made to believe it's Xterm, which shows ESPTerm
is compatible with ncurses.
In addition to control sequences that manipulate the terminal or user input, there
is a set of simple **networking commands** for device-to-device message exchange and
interacting with remote servers.
To see what escape sequences are supported, check out this [annotated Xterm manual page][xterm-compare]
which was used for reference, or the built-in help page ([online demo][demo-help])
The terminal screen can be accessed using any web browser, even on a phone or tablet.
It works with ESP-01, ESP-01S, ESP-12 and likely many other modules (I use an ESP-12
on a LoLin NodeMCU board from eBay for development).
It works with ESP-01, ESP-01S, ESP-12 and likely many other modules.
With ESPTerm, you can add remote access via WiFi to any embeded project, all you need is
a serial port and some imagination!
UART and some imagination!
## Try it online
@@ -30,6 +33,28 @@ emulator backend that runs on the ESP8266. The web version will be updated to ma
after each minor release (and sometimes in between for testing; the version currently being show-cased
can be read on the About page of the demo).
### Browser support
Use the above linked online demo to verify compatibility with your browser.
To our knowledge, ESPTerm **works with**:
- Google Chrome (desktop, mobile)
- Firefox (desktop, mobile not tested)
- Safari (desktop, mobile)
- Chromium
- Opera
- Brave
- Konqueror
It **does not work with**:
- Microsoft Edge - runs, but is extremely laggy
- Internet Explorer (any version) - crashes, missing JS features
- Opera Mini - crashes, missing JS and CSS features
- Blackberry browser - not tested, but unlikely
- Safari on old iOS versions - missing features
- WebOS browser
- Old Android Browser (before 4.4?) - not tested, likely missing JS features
## Main features
- **Almost complete VT102 emulation** with some extras from Xterm, eg.
@@ -38,6 +63,10 @@ can be read on the About page of the demo).
- Full UTF-8 support, alternate character sets
- Standard mouse tracking modes
- You can dynamically set screen title, button labels...
- **Networking commands**
- Command for sending a message to another ESPTerm
- API endpoint (`/api/v1/msg`) for receiving messages sent e.g. from a script on PC, web browser or CURL
- Command for requesting remote servers and getting back response headers and/or body
- **Web Terminal Interface**
- Real-time screen update via WebSocket
- Mouse and keyboard input, works also on mobile
@@ -47,6 +76,25 @@ can be read on the About page of the demo).
- **User-friendly comprehensive WiFi configuration** (Demo: [WiFi][demo-wifi], [network][demo-network] config)
- Static IP, DHCP, channel selection, power
- SSID search utility for finding your existing network
- **Basic security features**
- Possibility to password-lock parts of the web interface
- Admin password for some sensitive operations (can be changed!)
- Configurable AP password & Hidden attribute
- **Advanced settings storage**
- Stored in Flash
- Seamlesly updated and usually backwards compatible in minor releases
- Settings can be saved as defaults and later easily restored (ideal e.g. for classroom use, saving
good tested settings before giving the module to students)
## Bugs? Ideas?
To ask any questions or discuss new features you'd like to see added, it's best to use
the **[ESPTerm Dev mailing list](https://groups.google.com/forum/#!forum/espterm-dev)**
Subscribe to the mailing list to also receive new release announcements.
If you found a bug (that happens alot!), please submit it to our [bug tracker](https://github.com/espterm/espterm-firmware/issues).
We also use it to track planned ideas. If you don't want to create a GitHub account for that, just send it to the mailing list.
## Running ESPTerm
@@ -78,36 +126,31 @@ It can happen that some changes to the WiFi or network config make the module in
- To reset all settings to defaults, hold the button a couple seconds until the LED flashes rapidly, then release it.
- You can cancel this wipe/reset operation (when triggered by accident) by pressing Reset or disconnecting the power supply.
### Config files
### Config banks
ESPTerm has two config "files", one for defaults and one for the currently used settings. In the case of the terminal
config, there is also a third, temporary file for changes done via ESC commands.
When you get your settings *just right*, you can store them as defaults, which can then be at any time restored
by holding the BOOT (GPIO0) button. You can do this on the System Settings page. This asks for an "admin password",
which you can define when building the firmware in the `esphttpdconfig.mk` file.
The default password is `19738426`. This password can't presently be changed without re-flashing the firmware.
which can (and should!) be changed. This password can't be easily recovered when forgotten.
You can also restore everything (except the saved defaults) to "factory defaults", there is a button for this
on the System Settings page. Those are the initial values in the config files.
on the System Settings page. Those are the initial values you would get after a clean install.
## Research resources
Developing ESPTerm wasn't an easy task, because the information is scattered across many places and the existing
terminal emulators I originally used for reference (terminator, Konsole) are not implemented correctly in some details.
Developing the terminal emulator was complicated by the information being scattered across many places and the existing
implementations I used for reference often got some details wrong or didn't implement certain features at all. Xterm proved to be by far the most complete implementation.
A great tool for checking my implementation has proven to be [VTTTEST][vttest] and Xterm as a reference
implementation that is probably the most complete emulator available, although it's cumbersome to use and its age
really shows in the looks.
A great tool for checking my code has proven to be [VTTTEST][vttest]. ESPTerm passes most of the tests on the main page and some additional Xterm specific ones, like Mouse Tracking.
I've comnpiled a list of those I found most helpful here: [VT100 emulation resources][resources]
Here is a list of useful [VT100 emulation resources][resources] I've collected for reference.
## Development
ESPTerm's firmware is written in C and is based on SpriteTM's `libesphttpd` http server library forked to
[MightyPork/libesphttpd][httpdlib]. This fork includes various improvements
and changes required by the project.
[MightyPork/libesphttpd][httpdlib]. This fork includes various improvements and changes required by the project.
### Installation for development
+7
View File
@@ -3,3 +3,10 @@
echo "-- Building parser from Ragel source --"
ragel -L -G0 user/ansi_parser.rl -o user/ansi_parser.c
ragel -L -G0 user/ini_parser.rl -o user/ini_parser.c
sed -i "s/static const char _ansi_actions\[\]/static const char _ansi_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
sed -i "s/static const char _ansi_eof_actions\[\]/static const char _ansi_eof_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
sed -i "s/static const char _ini_actions\[\]/static const char _ini_actions\[\] ESP_CONST_DATA/" user/ini_parser.c
sed -i "s/static const char _ini_eof_actions\[\]/static const char _ini_eof_actions\[\] ESP_CONST_DATA/" user/ini_parser.c
@@ -38,15 +38,16 @@ OUTPUT_TYPE = combined
# SPI flash size, in K
ESP_SPI_FLASH_SIZE_K = 1024
# Admin password, used to store settings to flash as defaults
ADMIN_PASSWORD = "19738426"
GLOBAL_CFLAGS = \
-DASYNC_LOG=1 \
-DDEBUG_INI=1 \
-DDEBUG_D2D=0 \
-DDEBUG_ROUTER=0 \
-DDEBUG_CAPTDNS=0 \
-DDEBUG_HTTP=0 \
-DDEBUG_HTTPC=0 \
-DDEBUG_ESPFS=0 \
-DDEBUG_PERSIST=0 \
-DDEBUG_PERSIST=1 \
-DDEBUG_UTFCACHE=0 \
-DDEBUG_CGI=0 \
-DDEBUG_WIFI=0 \
@@ -59,5 +60,6 @@ GLOBAL_CFLAGS = \
-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
+14
View File
@@ -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
View File
@@ -1 +0,0 @@
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
xtensa-lx106-elf-gcc -E -Iuser -Ilibesphttpd/include -Iesp_iot_sdk_v1.5.2/include -Iinclude $@
+21
View File
@@ -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]
+26
View File
@@ -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%'
+3
View File
@@ -0,0 +1,3 @@
*
!.gitignore
!flash-tpl.sh
Executable
+70
View File
@@ -0,0 +1,70 @@
#!/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
buildlang hu
else
buildlang ${ESP_LANG}
fi
+43 -37
View File
@@ -5,11 +5,12 @@
#include "ansi_parser_callbacks.h"
#include "ascii.h"
#include "apars_logging.h"
#include "screen.h"
/* Ragel constants block */
/* #line 12 "user/ansi_parser.c" */
static const char _ansi_actions[] = {
/* #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,
7, 1, 8, 1, 9, 1, 10, 1,
@@ -17,7 +18,7 @@ static const char _ansi_actions[] = {
15
};
static const char _ansi_eof_actions[] = {
static const char _ansi_eof_actions[] ESP_CONST_DATA = {
0, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 0, 0, 0, 0
};
@@ -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" */
}
+5
View File
@@ -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;
+16 -10
View File
@@ -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;
+34 -1
View File
@@ -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
+21 -23
View File
@@ -551,31 +551,27 @@ do_csi_sgr(CSI_Data *opts)
else if (n >= SGR_FG_BRT_START && n <= SGR_FG_BRT_END) screen_set_fg((Color) ((n - SGR_FG_BRT_START) + 8)); // AIX bright fg
else if (n >= SGR_BG_BRT_START && n <= SGR_BG_BRT_END) screen_set_bg((Color) ((n - SGR_BG_BRT_START) + 8)); // AIX bright bg
// reset color
else if (n == SGR_FG_DEFAULT) screen_set_fg(termconf_live.default_fg); // default fg
else if (n == SGR_BG_DEFAULT) screen_set_bg(termconf_live.default_bg); // default bg
else if (n == SGR_FG_DEFAULT) screen_set_default_fg();
else if (n == SGR_BG_DEFAULT) screen_set_default_bg();
// 256 colors
else if (n == SGR_FG_256 || n == SGR_BG_256) {
if (i < count-2) {
if (opts->n[i + 1] == 5) {
u8 color = (u8) opts->n[i + 2];
bool fg = n == SGR_FG_256;
if (fg) {
screen_set_fg(color);
} else {
screen_set_bg(color);
}
}
else {
ansi_warn("SGR syntax err");
apars_show_context();
break; // abandon further
}
i += 2;
} else {
if (i >= count-2) {
ansi_warn("SGR syntax err");
apars_show_context();
break; // abandon further
}
if (opts->n[i + 1] != 5) {
ansi_warn("SGR syntax err");
apars_show_context();
break; // abandon further
}
u8 color = (u8) opts->n[i + 2];
bool fg = n == SGR_FG_256;
if (fg) screen_set_fg(color);
else screen_set_bg(color);
i += 2;
}
// -- set attr --
else if (n == SGR_BOLD) screen_set_sgr(ATTR_BOLD, 1);
@@ -585,7 +581,7 @@ do_csi_sgr(CSI_Data *opts)
else if (n == SGR_BLINK || n == SGR_BLINK_FAST) screen_set_sgr(ATTR_BLINK, 1); // 6 - rapid blink, not supported
else if (n == SGR_STRIKE) screen_set_sgr(ATTR_STRIKE, 1);
else if (n == SGR_FRAKTUR) screen_set_sgr(ATTR_FRAKTUR, 1);
else if (n == SGR_INVERSE) screen_set_sgr_inverse(1);
else if (n == SGR_INVERSE) screen_set_sgr(ATTR_INVERSE, 1);
else if (n == SGR_CONCEAL) screen_set_sgr_conceal(1);
else if (n == SGR_OVERLINE) screen_set_sgr(ATTR_OVERLINE, 1);
// -- clear attr --
@@ -595,7 +591,7 @@ do_csi_sgr(CSI_Data *opts)
else if (n == SGR_NO_UNDERLINE) screen_set_sgr(ATTR_UNDERLINE, 0);
else if (n == SGR_NO_BLINK) screen_set_sgr(ATTR_BLINK, 0);
else if (n == SGR_NO_STRIKE) screen_set_sgr(ATTR_STRIKE, 0);
else if (n == SGR_NO_INVERSE) screen_set_sgr_inverse(0);
else if (n == SGR_NO_INVERSE) screen_set_sgr(ATTR_INVERSE, 0);
else if (n == SGR_NO_CONCEAL) screen_set_sgr_conceal(0);
else if (n == SGR_NO_OVERLINE) screen_set_sgr(ATTR_OVERLINE, 0);
else {
@@ -713,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);
@@ -761,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);
+5 -3
View File
@@ -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).
+1 -1
View File
@@ -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
+69 -3
View File
@@ -16,6 +16,45 @@
#include "ansi_parser.h"
#include "cgi_sockets.h"
/**
* Handle ESPTerm-specific command
*
* @param n0 - top-level, 20-39
* @param n1 - sub-command
* @param buffer - buffer past the second command and its semicolon
*/
void ICACHE_FLASH_ATTR
handle_espterm_osc(int n0, int n1, char *buffer)
{
if (n0 == 27) {
// Miscellaneous
if (n1 == 1) {
screen_set_backdrop(buffer);
}
else if (n1 == 2) {
screen_set_button_count(atoi(buffer));
}
else goto bad;
}
else if (n0 == 28) {
// Button label
screen_set_button_text(n1, buffer);
}
else if (n0 == 29) {
// Button message
screen_set_button_message(n1, buffer);
}
else if (n0 == 30) {
// Button color
screen_set_button_color(n1, buffer);
}
else goto bad;
return;
bad:
ansi_warn("No ESPTerm option at %d.%d", n0, n1);
}
/**
* Helper function to parse incoming OSC (Operating System Control)
* @param buffer - the OSC body (after OSC and before ST)
@@ -23,8 +62,9 @@
void ICACHE_FLASH_ATTR
apars_handle_osc(char *buffer)
{
const char *origbuf = buffer;
int n = 0;
char c = 0;
char c;
while ((c = *buffer++) != 0) {
if (c >= '0' && c <= '9') {
n = (n * 10 + (c - '0'));
@@ -49,20 +89,46 @@ apars_handle_osc(char *buffer)
buffer[0] = 'G';
notify_growl(buffer);
}
else if (n >= 20 && n < 40) {
// New-style ESPTerm OSC
int n0 = n;
// Find sub-command
n = 0;
while ((c = *buffer++) != 0) {
if (c >= '0' && c <= '9') {
n = (n * 10 + (c - '0'));
} else {
break;
}
}
if (c == ';') {
handle_espterm_osc(n0, n, buffer);
} else {
ansi_warn("BAD OSC: %s", origbuf);
}
}
else if (n == 70) {
// ESPTerm: backdrop
ansi_warn("OSC 70 is deprecated, use 27;1");
screen_set_backdrop(buffer);
}
else if (n >= 81 && n <= 85) {
// ESPTerm: action button label
ansi_warn("OSC 8x is deprecated, use 28;x");
screen_set_button_text(n - 80, buffer);
}
else if (n >= 91 && n <= 95) {
// ESPTerm: action button message
strncpy(termconf_live.btn_msg[n - 91], buffer, TERM_BTN_MSG_LEN);
ansi_warn("OSC 9x is deprecated, use 29;x");
screen_set_button_message(n - 90, buffer);
}
else {
ansi_noimpl("OSC %d ; %s ST", n, buffer);
}
}
else {
ansi_warn("BAD OSC: %s", buffer);
ansi_warn("BAD OSC: %s", origbuf);
apars_show_context();
}
}
+34
View File
@@ -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);
}
+10
View File
@@ -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
+21 -2
View File
@@ -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 ---
@@ -92,17 +94,34 @@ apars_handle_space_cmd(char c)
void ICACHE_FLASH_ATTR apars_handle_hash_cmd(char c)
{
switch(c) {
case '1': // Double height, single width, top half (CUSTOM!)
screen_set_line_attr(1, 2, 1);
break;
case '2': // Double height, single width, bottom half (CUSTOM!)
screen_set_line_attr(1, 1, 2);
break;
case '3': // Double size, top half
case '4': // Single size, bottom half
screen_set_line_attr(2, 2, 1);
break;
case '4': // Double size, bottom half
screen_set_line_attr(2, 1, 2);
break;
case '5': // Single width, single height
screen_set_line_attr(1, 1, 1);
break;
case '6': // Double width
ansi_noimpl("Double Size Line");
screen_set_line_attr(2, 1, 1);
break;
case '8':
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);
}
+3 -1
View File
@@ -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
+166 -26
View File
@@ -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();
}
+15
View File
@@ -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
+324
View File
@@ -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;
}
+25
View File
@@ -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
+8 -12
View File
@@ -25,18 +25,14 @@ 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, "debugbar")) {
sprintf(buff, "%d", termconf->debugbar);
tplSend(connData, buff, -1);
}
return HTTPD_CGI_DONE;
}
@@ -48,19 +44,19 @@ 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);
}
else if (streq(token, "time")) {
tplSend(connData, __TIME__, -1);
tplSend(connData, __TIME__" "__TIMEZONE__, -1);
}
else if (streq(token, "vers_httpd")) {
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")) {
tplSend(connData, GIT_HASH_BACKEND, -1);
+27 -161
View File
@@ -9,6 +9,7 @@ configuring the network settings
#include "persist.h"
#include "helpers.h"
#include "cgi_logging.h"
#include "config_xmacros.h"
#define SET_REDIR_SUC "/cfg/network"
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
@@ -41,140 +42,19 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
return HTTPD_CGI_DONE;
}
WiFiConfigBundle *wificonf_backup = malloc(sizeof(WiFiConfigBundle));
WiFiConfChangeFlags *wcf_backup = malloc(sizeof(WiFiConfChangeFlags));
memcpy(wificonf_backup, wificonf, sizeof(WiFiConfigBundle));
memcpy(wcf_backup, &wifi_change_flags, sizeof(WiFiConfChangeFlags));
// ---- AP DHCP server lease time ----
if (GET_ARG("ap_dhcp_time")) {
cgi_dbg("Setting DHCP lease time to: %s min.", buff);
int min = atoi(buff);
if (min >= 1 && min <= 2880) {
if (wificonf->ap_dhcp_time != min) {
wificonf->ap_dhcp_time = (u16) min;
wifi_change_flags.ap = true;
}
} else {
cgi_warn("Lease time %s out of allowed range 1-2880.", buff);
redir_url += sprintf(redir_url, "ap_dhcp_time,");
}
}
#define XSTRUCT wificonf
#define X XSET_CGI_FUNC
XTABLE_WIFICONF
#undef X
// ---- AP DHCP start and end IP ----
if (GET_ARG("ap_dhcp_start")) {
cgi_dbg("Setting DHCP range start IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_dhcp_range.start_ip.addr != ip) {
wificonf->ap_dhcp_range.start_ip.addr = ip;
wifi_change_flags.ap = true;
}
} else {
cgi_warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_dhcp_start,");
}
}
if (GET_ARG("ap_dhcp_end")) {
cgi_dbg("Setting DHCP range end IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_dhcp_range.end_ip.addr != ip) {
wificonf->ap_dhcp_range.end_ip.addr = ip;
wifi_change_flags.ap = true;
}
} else {
cgi_warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_dhcp_end,");
}
}
// ---- AP local address & config ----
if (GET_ARG("ap_addr_ip")) {
cgi_dbg("Setting AP local IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_addr.ip.addr != ip) {
wificonf->ap_addr.ip.addr = ip;
wificonf->ap_addr.gw.addr = ip; // always the same, we're the router here
wifi_change_flags.ap = true;
}
} else {
cgi_warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "ap_addr_ip,");
}
}
if (GET_ARG("ap_addr_mask")) {
cgi_dbg("Setting AP local IP netmask to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->ap_addr.netmask.addr != ip) {
// ideally this should be checked to match the IP.
// Let's hope users know what they're doing
wificonf->ap_addr.netmask.addr = ip;
wifi_change_flags.ap = true;
}
} else {
cgi_warn("Bad IP mask: %s", buff);
redir_url += sprintf(redir_url, "ap_addr_mask,");
}
}
// ---- Station enable/disable DHCP ----
// DHCP enable / disable (disable means static IP is enabled)
if (GET_ARG("sta_dhcp_enable")) {
cgi_dbg("DHCP enable = %s", buff);
int enable = atoi(buff);
if (wificonf->sta_dhcp_enable != enable) {
wificonf->sta_dhcp_enable = (bool)enable;
wifi_change_flags.sta = true;
}
}
// ---- Station IP config (Static IP) ----
if (GET_ARG("sta_addr_ip")) {
cgi_dbg("Setting Station mode static IP to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->sta_addr.ip.addr != ip) {
wificonf->sta_addr.ip.addr = ip;
wifi_change_flags.sta = true;
}
} else {
cgi_warn("Bad IP: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_ip,");
}
}
if (GET_ARG("sta_addr_mask")) {
cgi_dbg("Setting Station mode static IP netmask to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0 && ip != 0xFFFFFFFFUL) {
if (wificonf->sta_addr.netmask.addr != ip) {
wificonf->sta_addr.netmask.addr = ip;
wifi_change_flags.sta = true;
}
} else {
cgi_warn("Bad IP mask: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_mask,");
}
}
if (GET_ARG("sta_addr_gw")) {
cgi_dbg("Setting Station mode static IP default gateway to: \"%s\"", buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0) {
if (wificonf->sta_addr.gw.addr != ip) {
wificonf->sta_addr.gw.addr = ip;
wifi_change_flags.sta = true;
}
} else {
cgi_warn("Bad gw IP: %s", buff);
redir_url += sprintf(redir_url, "sta_addr_gw,");
}
}
(void) redir_url;
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK
@@ -189,12 +69,19 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
os_timer_setfn(&timer, applyNetSettingsLaterCb, NULL);
os_timer_arm(&timer, 1000, false);
httpdRedirect(connData, SET_REDIR_SUC);
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
} else {
cgi_warn("Some WiFi settings did not validate, asking for correction");
memcpy(wificonf, wificonf_backup, sizeof(WiFiConfigBundle));
memcpy(&wifi_change_flags, wcf_backup, sizeof(WiFiConfChangeFlags));
// Some errors, appended to the URL as ?err=
httpdRedirect(connData, redir_url_buf);
}
free(wificonf_backup);
free(wcf_backup);
return HTTPD_CGI_DONE;
}
@@ -202,7 +89,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiNetworkSetParams(HttpdConnData *connData)
//Template code for the WLAN page.
httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *token, void **arg)
{
char buff[20];
char buff[64];
u8 mac[6];
if (token == NULL) {
@@ -212,34 +99,13 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplNetwork(HttpdConnData *connData, char *toke
strcpy(buff, ""); // fallback
if (streq(token, "ap_dhcp_time")) {
sprintf(buff, "%d", wificonf->ap_dhcp_time);
}
else if (streq(token, "ap_dhcp_start")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr));
}
else if (streq(token, "ap_dhcp_end")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr));
}
else if (streq(token, "ap_addr_ip")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr));
}
else if (streq(token, "ap_addr_mask")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr));
}
else if (streq(token, "sta_dhcp_enable")) {
sprintf(buff, "%d", wificonf->sta_dhcp_enable);
}
else if (streq(token, "sta_addr_ip")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr));
}
else if (streq(token, "sta_addr_mask")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr));
}
else if (streq(token, "sta_addr_gw")) {
sprintf(buff, IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr));
}
else if (streq(token, "sta_mac")) {
#define XSTRUCT wificonf
#define X XGET_CGI_FUNC
XTABLE_WIFICONF
#undef X
// non-config
if (streq(token, "sta_mac")) {
wifi_get_macaddr(STATION_IF, mac);
sprintf(buff, MACSTR, MAC2STR(mac));
}
+428 -7
View File
@@ -7,15 +7,18 @@ Cgi/template routines for configuring non-wifi settings
#include "persist.h"
#include "helpers.h"
#include "cgi_logging.h"
#include "version.h"
#include "screen.h"
#include "config_xmacros.h"
#include "ini_parser.h"
#define SET_REDIR_SUC "/cfg/admin"
#define SET_REDIR_SUC "/cfg/system"
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
static bool ICACHE_FLASH_ATTR
verify_admin_pw(const char *pw)
{
// This is not really for security, but to prevent someone who
// shouldn't touch those settings from fucking it up.
return streq(pw, STR(ADMIN_PASSWORD)); // the PW comes from the makefile
return streq(pw, persist.admin.pw);
}
httpd_cgi_state ICACHE_FLASH_ATTR
@@ -36,7 +39,7 @@ cgiPersistWriteDefaults(HttpdConnData *connData)
persist_set_as_default();
httpdRedirect(connData, SET_REDIR_SUC);
httpdRedirect(connData, SET_REDIR_SUC "?msg=Default%20settings%20updated.");
return HTTPD_CGI_DONE;
}
// if pw failed, show the same error as if it's wrong
@@ -57,7 +60,7 @@ cgiPersistRestoreDefaults(HttpdConnData *connData)
persist_restore_default();
httpdRedirect(connData, SET_REDIR_SUC);
httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20saved%20defaults.");
return HTTPD_CGI_DONE;
}
@@ -73,6 +76,424 @@ cgiPersistRestoreHard(HttpdConnData *connData)
// Defaults are not changed.
persist_load_hard_default();
httpdRedirect(connData, SET_REDIR_SUC);
httpdRedirect(connData, SET_REDIR_SUC "?msg=All%20settings%20restored%20to%20factory%20defaults.");
return HTTPD_CGI_DONE;
}
// -------------- Export --------------
#define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (data), (len))) return false; } while (0)
/* encode for double-quoted string */
int ICACHE_FLASH_ATTR httpdSend_dblquot(HttpdConnData *conn, const char *data, int len)
{
int start = 0, end = 0;
char c;
if (conn->conn==NULL) return 0;
if (len < 0) len = (int) strlen(data);
if (len==0) return 0;
for (end = 0; end < len; end++) {
c = data[end];
if (c == 0) {
// we found EOS
break;
}
if (c == '"' || c == '\'' || c == '\\' || c == '\n' || c == '\r' || c == '\x1b') {
if (start < end) httpdSend_orDie(conn, data + start, end - start);
start = end + 1;
}
if (c == '"') httpdSend_orDie(conn, "\\\"", 2);
else if (c == '\'') httpdSend_orDie(conn, "\\'", 2);
else if (c == '\\') httpdSend_orDie(conn, "\\\\", 2);
else if (c == '\n') httpdSend_orDie(conn, "\\n", 2);
else if (c == '\r') httpdSend_orDie(conn, "\\r", 2);
else if (c == '\x1b') httpdSend_orDie(conn, "\\e", 2);
}
if (start < end) httpdSend_orDie(conn, data + start, end - start);
return 1;
}
/**
* Export settings to INI
*
* @param connData
* @return status
*/
httpd_cgi_state ICACHE_FLASH_ATTR
cgiPersistExport(HttpdConnData *connData)
{
char buff[256];
u8 mac[6];
int step = (int) connData->cgiData;
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
if (step == 0) {
wifi_get_macaddr(SOFTAP_IF, mac);
sprintf(buff, "attachment; filename=ESPTerm_%02X%02X%02X_%s.ini",
mac[3], mac[4], mac[5], FW_VERSION);
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Disposition", buff);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
sprintf(buff, "# == ESPTerm config export ==\r\n"
"# Device: %02X%02X%02X - %s\r\n"
"# Version: %s\r\n",
mac[3], mac[4], mac[5],
termconf->title,
FW_VERSION);
httpdSend(connData, buff, -1);
}
// do not export SSID if unchanged - embeds unique ID that should
// not be overwritten in target.
char defSSID[20];
sprintf(defSSID, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]);
bool quoted;
#define X(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
do { if (allow) { \
xget(buff, deref XSTRUCT->name); \
if (streq(#name, "ap_ssid") && streq(buff, defSSID)) break; \
\
quoted = false; \
quoted |= streq(#type, "char"); \
quoted |= streq(#type, "uchar"); \
if (strstarts(#name, "bm")) quoted=false; \
\
httpdSend(connData, "\r\n"#name " = ", -1); \
if (quoted) { \
httpdSend(connData, "\"", 1); \
httpdSend_dblquot(connData, buff, -1); \
httpdSend(connData, "\"", 1); \
} else { \
httpdSend(connData, buff, -1); \
} \
} } while(0);
#define admin 1
#define tpl 1
if (step == 1) {
httpdSend(connData, "\r\n[system]", -1);
#define XSTRUCT sysconf
XTABLE_SYSCONF
#undef XSTRUCT
httpdSend(connData, "\r\n", -1);
}
if (step == 2) {
httpdSend(connData, "\r\n[wifi]", -1);
#define XSTRUCT wificonf
XTABLE_WIFICONF
#undef XSTRUCT
httpdSend(connData, "\r\n", -1);
}
if (step == 3) {
httpdSend(connData, "\r\n[terminal]", -1);
#define XSTRUCT termconf
XTABLE_TERMCONF
#undef XSTRUCT
httpdSend(connData, "\r\n", -1);
return HTTPD_CGI_DONE;
}
#undef X
connData->cgiData = (void *) (step + 1);
return HTTPD_CGI_MORE;
}
// -------------- IMPORT --------------
struct IniUpload {
TerminalConfigBundle *term_backup;
WiFiConfigBundle *wifi_backup;
SystemConfigBundle *sys_backup;
bool term_ok;
bool wifi_ok;
bool sys_ok;
bool term_any;
bool wifi_any;
bool sys_any;
};
static void ICACHE_FLASH_ATTR iniCb(const char *section, const char *key, const char *value, void *userData)
{
HttpdConnData *connData = (HttpdConnData *)userData;
struct IniUpload *state;
if (!connData || !connData->cgiData) {
error("userData or state is NULL!");
return;
}
state = connData->cgiData;
// cgi_dbg("%s.%s = %s", section, key, value);
/** used for INI */
#define X(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
if (streq(#name, key)) { \
found = true; \
type *_p = (type *) &XSTRUCT->name; \
enum xset_result res = xset(#name, _p, value, (const void*) (xsarg)); \
if (res == XSET_SET) { changed = true; xnotify; } \
else if (res == XSET_FAIL) { ok = false; } \
break; \
}
// notify flag, unused here
bool uart_changed = false;
bool found = false;
bool ok = true;
bool changed = false;
if (streq(section, "terminal")) {
do {
#define XSTRUCT termconf
XTABLE_TERMCONF
#undef XSTRUCT
} while (0);
if (found) {
if (!ok) state->term_ok = false;
state->term_any |= changed;
}
}
else if (streq(section, "wifi")) {
do {
#define XSTRUCT wificonf
XTABLE_WIFICONF
#undef XSTRUCT
} while (0);
if (found) {
if (!ok) state->wifi_ok = false;
state->wifi_any |= changed;
}
}
else if (streq(section, "system")) {
do {
#define XSTRUCT sysconf
XTABLE_SYSCONF
#undef XSTRUCT
} while (0);
if (found) {
if (!ok) state->sys_ok = false;
state->sys_any |= changed;
}
}
if (!found) cgi_warn("Unknown key %s.%s!", section, key);
#undef X
}
static void ICACHE_FLASH_ATTR
freeIniUploadStruct(HttpdConnData *connData)
{
cgi_dbg("Free struct...");
struct IniUpload *state;
if (connData && connData->cgiData) {
state = connData->cgiData;
if (state->sys_backup != NULL) free(state->sys_backup);
if (state->wifi_backup != NULL) free(state->wifi_backup);
if (state->term_backup != NULL) free(state->term_backup);
free(state);
connData->cgiData = NULL;
}
}
static httpd_cgi_state ICACHE_FLASH_ATTR
postRecvHdl(HttpdConnData *connData, char *data, int len)
{
struct IniUpload *state = connData->cgiData;
if (!state) return HTTPD_CGI_DONE;
cgi_dbg("INI parse - postRecvHdl");
if (len>0) {
// Discard the boundary marker (there is only one)
char *bdr = connData->post->multipartBoundary;
char *foundat = strstr(data, bdr);
if (foundat != NULL) {
*foundat = '#'; // make it a comment
}
ini_parse(data, (size_t) len);
}
cgi_dbg("Closing parser");
ini_parse_end();
cgi_dbg("INI parse - end.");
// abort if bad screen size
bool tooLarge = (termconf->width*termconf->height > MAX_SCREEN_SIZE);
state->term_ok &= !tooLarge;
if (tooLarge) cgi_warn("Bad term screen size!");
bool suc = state->term_ok && state->wifi_ok && state->sys_ok;
bool any = state->term_any || state->wifi_any || state->sys_any;
cgi_dbg("Evaluating results...");
if (!state->term_ok) cgi_warn("Terminal settings rejected.");
if (!state->wifi_ok) cgi_warn("WiFi settings rejected.");
if (!state->sys_ok) cgi_warn("System settings rejected.");
if (!suc) {
cgi_warn("Some validation failed, reverting all!");
memcpy(termconf, state->term_backup, sizeof(TerminalConfigBundle));
memcpy(wificonf, state->wifi_backup, sizeof(WiFiConfigBundle));
memcpy(sysconf, state->sys_backup, sizeof(SystemConfigBundle));
}
else {
if (state->term_any) {
cgi_dbg("Applying terminal settings");
terminal_apply_settings();
}
if (state->sys_any) {
cgi_dbg("Applying system settings");
sysconf_apply_settings();
}
if (state->wifi_any) {
cgi_dbg("Applying WiFi settings (scheduling...)");
wifimgr_apply_settings_later(2000);
}
if (any) {
cgi_dbg("Persisting results");
persist_store();
} else {
cgi_warn("Nothing written.");
}
}
cgi_dbg("Redirect");
char buff[100];
char *b = buff;
if (suc) {
if (any) {
httpdRedirect(connData, SET_REDIR_SUC"?msg=Settings%20loaded%20and%20applied.");
} else {
httpdRedirect(connData, SET_REDIR_SUC"?msg=No%20settings%20changed.");
}
} else {
b += sprintf(b, SET_REDIR_SUC"?errmsg=Errors%%20in:%%20");
bool comma = false;
if (!state->sys_ok) {
b += sprintf(b, "System%%20config");
comma = true;
}
if (!state->wifi_ok) {
if (comma) b += sprintf(b, ",%%20");
b += sprintf(b, "WiFi%%20config");
}
if (!state->term_ok) {
if (comma) b += sprintf(b, ",%%20");
b += sprintf(b, "Terminal%%20config");
}
httpdRedirect(connData, buff);
}
// Clean up.
freeIniUploadStruct(connData);
return HTTPD_CGI_DONE;
}
/**
* Import settings from INI
*
* @param connData
* @return status
*/
httpd_cgi_state ICACHE_FLASH_ATTR
cgiPersistImport(HttpdConnData *connData)
{
struct IniUpload *state = NULL;
if (connData->conn == NULL) {
//Connection aborted. Clean up.
freeIniUploadStruct(connData);
return HTTPD_CGI_DONE;
}
char *start = strstr(connData->post->buff, "\r\n\r\n");
if (start == NULL) {
error("Malformed attachment POST!");
httpdStartResponse(connData, 400);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
httpdSend(connData, "Bad format.", -1);
return HTTPD_CGI_DONE;
}
cgi_info("Starting INI parser for uploaded file...");
state = malloc(sizeof(struct IniUpload));
if (!state) {
error("state struct alloc fail");
return HTTPD_CGI_DONE;
}
state->sys_backup = NULL;
state->wifi_backup = NULL;
state->term_backup = NULL;
connData->cgiData = state;
cgi_dbg("Allocating backup buffers");
state->sys_backup = malloc(sizeof(SystemConfigBundle));
state->wifi_backup = malloc(sizeof(WiFiConfigBundle));
state->term_backup = malloc(sizeof(TerminalConfigBundle));
cgi_dbg("Copying orig data");
memcpy(state->sys_backup, sysconf, sizeof(SystemConfigBundle));
memcpy(state->wifi_backup, wificonf, sizeof(WiFiConfigBundle));
memcpy(state->term_backup, termconf, sizeof(TerminalConfigBundle));
state->sys_ok = true;
state->wifi_ok = true;
state->term_ok = true;
state->sys_any = false;
state->wifi_any = false;
state->term_any = false;
cgi_dbg("Parser starts!");
ini_parse_begin(iniCb, connData);
size_t datalen = (size_t) connData->post->buffLen - (start - connData->post->buff);
ini_parse(start, datalen);
connData->recvHdl = postRecvHdl;
// special case for too short ini
int bytes_remain = connData->post->len;
bytes_remain -= connData->post->buffLen;
if (bytes_remain <= 0) {
return connData->recvHdl(connData, NULL, 0);
}
// continues in recvHdl
return HTTPD_CGI_MORE;
}
+2
View File
@@ -6,5 +6,7 @@
httpd_cgi_state cgiPersistWriteDefaults(HttpdConnData *connData);
httpd_cgi_state cgiPersistRestoreDefaults(HttpdConnData *connData);
httpd_cgi_state cgiPersistRestoreHard(HttpdConnData *connData);
httpd_cgi_state cgiPersistExport(HttpdConnData *connData);
httpd_cgi_state cgiPersistImport(HttpdConnData *connData);
#endif
+119 -65
View File
@@ -8,7 +8,6 @@
#include "ansi_parser.h"
#include "jstring.h"
#include "uart_driver.h"
#include "cgi_logging.h"
// Heartbeat interval in ms
#define HB_TIME 1000
@@ -17,20 +16,29 @@
// 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 term_active_clients = 0;
// we're trying to do a kind of mutex here, without the actual primitives
// this might glitch, very rarely.
// it's recommended to put some delay between setting labels and updating the screen.
static void resetHeartbeatTimer(void);
/**
* Cooldown delay is over
* @param arg
@@ -46,31 +54,29 @@ 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, 5, 0);
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) {
cgiWebsocketSend(ws, sock_buff, (int) strlen(sock_buff), flg);
} else {
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), flg);
resetHeartbeatTimer();
}
if (cont == HTTPD_CGI_DONE) break;
@@ -78,45 +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)
{
char sock_buff[SOCK_BUF_LEN];
int max_bl, total_bl;
cgiWebsockMeasureBacklog(URL_WS_UPDATE, &max_bl, &total_bl);
if (!notify_available || notify_cooldown) {
inp_dbg("Notify broadcast +%02Xh?", pendingBroadcastTopics);
if (!notify_available || notify_cooldown || (max_bl > 2048)) { // do not send if we have anything significant backlogged
// postpone a little
TIMER_START(&notifyLabelsTim, notifyLabelsTimCb, 1, 0);
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);
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), 0);
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)
{
// here's some potential for a race error with the other broadcast functions :C
cgiWebsockBroadcast(URL_WS_UPDATE, "B", 1, 0);
if (term_active_clients == 0) return;
screen_notifyChange(TOPIC_BELL); // XXX has latency, maybe better to send directly
}
@@ -124,9 +130,12 @@ send_beep(void)
void ICACHE_FLASH_ATTR
notify_growl(char *msg)
{
// 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);
if (term_active_clients == 0) return;
// 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();
}
@@ -135,22 +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)
{
// this is not the most ideal/cleanest implementation
// PRs are welcome for a nicer update "queue" solution
if (termconf->display_tout_ms == 0) termconf->display_tout_ms = SCR_DEF_DISPLAY_TOUT_MS;
if (term_active_clients == 0) return;
// NOTE: the timers are restarted if already running
inp_dbg("Notify +%02Xh", topics);
if (topic == CHANGE_LABELS) {
// separate timer from content change timer, to avoid losing that update
TIMER_START(&notifyLabelsTim, notifyLabelsTimCb, termconf->display_tout_ms, 0);
}
else if (topic == CHANGE_CONTENT) {
// throttle delay
TIMER_START(&notifyContentTim, notifyContentTimCb, termconf->display_tout_ms, 0);
}
pendingBroadcastTopics |= topics;
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
}
/**
@@ -161,7 +168,7 @@ void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic)
* @param button - which button, 1-based. 0=none
* @param mods - modifier keys bitmap: meta|alt|shift|ctrl
*/
void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mods)
static void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mods)
{
// one-based
x++;
@@ -230,12 +237,12 @@ void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mo
}
/** Socket received a message */
void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
static void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
{
// Add terminator if missing (seems to randomly happen)
data[len] = 0;
ws_dbg("Sock RX str: %s, len %d", data, len);
inp_dbg("Sock RX str: %s, len %d", data, len);
int y, x, m, b;
u8 btnNum;
@@ -253,7 +260,7 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
// 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;
@@ -264,15 +271,15 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
case 'b':
// action button press
btnNum = (u8) (data[1]);
if (btnNum > 0 && btnNum < 10) {
UART_SendAsync(termconf_live.btn_msg[btnNum-1], -1);
if (btnNum > 0 && btnNum <= TERM_BTN_COUNT) {
UART_SendAsync(TERM_BM_N(&termconf_live, btnNum-1), -1);
}
break;
case 'i':
// requests initial load
ws_dbg("Client requests initial load");
notifyContentTimCb(ws);
inp_dbg("Client requests initial load");
updateNotify_do(ws, TOPIC_INITIAL|TOPIC_FLAG_NOCLEAN);
break;
case 'm':
@@ -290,39 +297,86 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
break;
default:
ws_warn("Bad command.");
inp_warn("Bad command.");
}
}
/** Send a heartbeat msg */
void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused)
static void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused)
{
if (notify_available) {
// 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);
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
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);
} else {
// postpone...
TIMER_START(&heartbeatTim, heartbeatTimCb, 10, 0);
inp_dbg("postpone heartbeat");
}
}
}
static void ICACHE_FLASH_ATTR resetHeartbeatTimer(void)
{
TIMER_START(&heartbeatTim, heartbeatTimCb, HB_TIME, 0);
}
static void ICACHE_FLASH_ATTR closeSockCb(Websock *ws)
{
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);
}
// stop the timer
os_timer_disarm(&heartbeatTim);
inp_dbg("Stop HB timer");
}
}
/** Socket connected for updates */
void ICACHE_FLASH_ATTR updateSockConnect(Websock *ws)
{
ws_info("Socket connected to "URL_WS_UPDATE);
inp_info("Socket connected to "URL_WS_UPDATE);
ws->recvCb = updateSockRx;
ws->closeCb = closeSockCb;
TIMER_START(&heartbeatTim, heartbeatTimCb, HB_TIME, 1);
if (term_active_clients == 0) {
if (mouse_tracking.focus_tracking) {
UART_SendAsync("\x1b[I", 3);
}
resetHeartbeatTimer();
}
term_active_clients++;
}
ETSTimer xonTim;
void ICACHE_FLASH_ATTR notify_empty_txbuf_cb(void *unused)
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;
}
void notify_empty_txbuf(void)
{
if (browser_wants_xon) {
+8 -6
View File
@@ -15,15 +15,17 @@ 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 ws_warn warn
#define ws_dbg dbg
#define ws_info info
#define inp_warn warn
#define inp_dbg dbg
#define inp_info info
#else
#define ws_warn(...)
#define ws_dbg(...)
#define ws_info(...)
#define inp_warn(...)
#define inp_dbg(...)
#define inp_info(...)
#endif
#endif //CGI_SOCKETS_H
+107 -54
View File
@@ -6,7 +6,6 @@
#include "cgi_system.h"
#include "persist.h"
#include "syscfg.h"
#include "uart_driver.h"
#include "ansi_parser.h"
#include "cgi_logging.h"
@@ -51,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;
}
@@ -67,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;
}
@@ -78,7 +77,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiPing(HttpdConnData *connData)
httpd_cgi_state ICACHE_FLASH_ATTR
cgiSystemCfgSetParams(HttpdConnData *connData)
{
char buff[50];
char buff[65];
char buff2[65];
char redir_url_buf[100];
char *redir_url = redir_url_buf;
@@ -90,53 +90,89 @@ cgiSystemCfgSetParams(HttpdConnData *connData)
return HTTPD_CGI_DONE;
}
if (GET_ARG("uart_baud")) {
cgi_dbg("Baud rate: %s", buff);
int baud = atoi(buff);
if (baud == BIT_RATE_300 ||
baud == BIT_RATE_600 ||
baud == BIT_RATE_1200 ||
baud == BIT_RATE_2400 ||
baud == BIT_RATE_4800 ||
baud == BIT_RATE_9600 ||
baud == BIT_RATE_19200 ||
baud == BIT_RATE_38400 ||
baud == BIT_RATE_57600 ||
baud == BIT_RATE_74880 ||
baud == BIT_RATE_115200 ||
baud == BIT_RATE_230400 ||
baud == BIT_RATE_460800 ||
baud == BIT_RATE_921600 ||
baud == BIT_RATE_1843200 ||
baud == BIT_RATE_3686400) {
sysconf->uart_baudrate = (u32) baud;
} else {
cgi_warn("Bad baud rate %s", buff);
redir_url += sprintf(redir_url, "uart_baud,");
}
}
AdminConfigBlock *admin_backup = malloc(sizeof(AdminConfigBlock));
SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle));
memcpy(admin_backup, &persist.admin, sizeof(AdminConfigBlock));
memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle));
if (GET_ARG("uart_parity")) {
cgi_dbg("Parity: %s", buff);
int parity = atoi(buff);
if (parity >= 0 && parity <= 2) {
sysconf->uart_parity = (UartParityMode) parity;
} else {
cgi_warn("Bad parity %s", buff);
redir_url += sprintf(redir_url, "uart_parity,");
// flags for the template builder
bool uart_changed = false; //!< this is set in uart notify, for use in terminal settings (dummy here)
bool admin = false;
const bool tpl = false; // this optionally disables some fields
do {
// Check admin PW
if (GET_ARG("pw")) {
if (!streq(buff, persist.admin.pw)) {
warn("Bad admin pw!");
redir_url += sprintf(redir_url, "pw,");
break; // Abort
} else {
admin = true;
}
}
}
if (GET_ARG("uart_stopbits")) {
cgi_dbg("Stop bits: %s", buff);
int stopbits = atoi(buff);
if (stopbits >= 1 && stopbits <= 3) {
sysconf->uart_stopbits = (UartStopBitsNum) stopbits;
} else {
cgi_warn("Bad stopbits %s", buff);
redir_url += sprintf(redir_url, "uart_stopbits,");
// Changing admin PW
if (admin && GET_ARG("admin_pw")) {
if (strlen(buff)) {
cgi_dbg("admin_pw: %s", buff);
strcpy(buff2, buff);
if (!GET_ARG("admin_pw2")) {
cgi_warn("Missing repeated admin_pw %s", buff);
redir_url += sprintf(redir_url, "admin_pw2,");
break; // Abort
}
if (!streq(buff, buff2)) {
cgi_warn("Bad repeated admin_pw %s", buff);
redir_url += sprintf(redir_url, "admin_pw2,");
break; // Abort
}
if (strlen(buff) >= 64) {
cgi_warn("Too long admin_pw %s", buff);
redir_url += sprintf(redir_url, "admin_pw,");
break; // Abort
}
cgi_dbg("Changing admin PW!");
strncpy(persist.admin.pw, buff, 64);
break; // this is the only field in this form
}
}
}
// Reject filled but unconfirmed access PW
if (admin && GET_ARG("access_pw")) {
if (strlen(buff)) {
cgi_dbg("access_pw: %s", buff);
strcpy(buff2, buff);
if (!GET_ARG("access_pw2")) {
cgi_warn("Missing repeated access_pw %s", buff);
redir_url += sprintf(redir_url, "access_pw2,");
break; // Abort
}
if (!streq(buff, buff2)) {
cgi_warn("Bad repeated access_pw %s", buff);
redir_url += sprintf(redir_url, "access_pw2,");
break; // Abort
}
}
}
// Settings in the system config block
#define XSTRUCT sysconf
#define X XSET_CGI_FUNC
XTABLE_SYSCONF
#undef X
#undef XSTRUCT
} while (0);
(void)redir_url;
(void)uart_changed; // unused
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK
@@ -145,12 +181,20 @@ cgiSystemCfgSetParams(HttpdConnData *connData)
sysconf_apply_settings();
persist_store();
httpdRedirect(connData, SET_REDIR_SUC);
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
} else {
cgi_warn("Some settings did not validate, asking for correction");
// revert any possible changes
memcpy(&persist.admin, admin_backup, sizeof(AdminConfigBlock));
memcpy(sysconf, sysconf_backup, sizeof(SystemConfigBundle));
// Some errors, appended to the URL as ?err=
httpdRedirect(connData, redir_url_buf);
}
free(admin_backup);
free(sysconf_backup);
return HTTPD_CGI_DONE;
}
@@ -168,14 +212,23 @@ tplSystemCfg(HttpdConnData *connData, char *token, void **arg)
strcpy(buff, ""); // fallback
if (streq(token, "uart_baud")) {
sprintf(buff, "%d", sysconf->uart_baudrate);
const bool admin = false;
const bool tpl=true;
#define XSTRUCT sysconf
#define X XGET_CGI_FUNC
XTABLE_SYSCONF
#undef X
#undef XSTRUCT
if (streq(token, "def_access_name")) {
sprintf(buff, "%s", DEF_ACCESS_NAME);
}
else if (streq(token, "uart_parity")) {
sprintf(buff, "%d", sysconf->uart_parity);
else if (streq(token, "def_access_pw")) {
sprintf(buff, "%s", DEF_ACCESS_PW);
}
else if (streq(token, "uart_stopbits")) {
sprintf(buff, "%d", sysconf->uart_stopbits);
else if (streq(token, "def_admin_pw")) {
sprintf(buff, "%s", DEFAULT_ADMIN_PW);
}
tplSend(connData, buff, -1);
+59 -311
View File
@@ -9,6 +9,8 @@ Cgi/template routines for configuring non-wifi settings
#include "screen.h"
#include "helpers.h"
#include "cgi_logging.h"
#include "uart_driver.h"
#include "serial.h"
#define SET_REDIR_SUC "/cfg/term"
#define SET_REDIR_ERR SET_REDIR_SUC"?err="
@@ -21,249 +23,47 @@ cgiTermCfgSetParams(HttpdConnData *connData)
{
char buff[50];
char redir_url_buf[100];
int32 n, w, h;
bool notify_screen_content = 0, notify_screen_labels = 0;
bool shall_clear_screen = false;
ScreenNotifyTopics topics = 0;
char *redir_url = redir_url_buf;
redir_url += sprintf(redir_url, SET_REDIR_ERR);
// we'll test if anything was printed by looking for \0 in failed_keys_buf
// we'll test if anything was printed by looking for \0 in redir_url
SystemConfigBundle *sysconf_backup = malloc(sizeof(SystemConfigBundle));
TerminalConfigBundle *termconf_backup = malloc(sizeof(TerminalConfigBundle));
memcpy(sysconf_backup, sysconf, sizeof(SystemConfigBundle));
memcpy(termconf_backup, termconf, sizeof(TerminalConfigBundle));
if (connData->conn == NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
#define XSTRUCT termconf
#define X XSET_CGI_FUNC
XTABLE_TERMCONF
#undef X
#undef XSTRUCT
// width and height must always go together so we can do max size validation
if (GET_ARG("term_width")) {
cgi_dbg("Default screen width: %s", buff);
w = atoi(buff);
if (w > 1) {
if (GET_ARG("term_height")) {
cgi_dbg("Default screen height: %s", buff);
h = atoi(buff);
if (h > 1) {
if (w * h <= MAX_SCREEN_SIZE) {
if (termconf->width != w || termconf->height != h) {
termconf->width = w;
termconf->height = h;
shall_clear_screen = true; // this causes a notify
}
} else {
cgi_warn("Bad dimensions: %d x %d (total %d)", w, h, w*h);
redir_url += sprintf(redir_url, "term_width,term_height,");
}
} else {
cgi_warn("Bad height: \"%s\"", buff);
redir_url += sprintf(redir_url, "term_width,");
}
} else {
cgi_warn("Missing height arg!");
// this wont happen normally when the form is used
redir_url += sprintf(redir_url, "term_width,term_height,");
}
} else {
cgi_warn("Bad width: \"%s\"", buff);
redir_url += sprintf(redir_url, "term_width,");
}
u32 siz = termconf->width*termconf->height;
if (siz == 0 || siz > MAX_SCREEN_SIZE) {
cgi_warn("Bad dimensions: %d x %d (total %d)", termconf->width, termconf->height, termconf->width*termconf->height);
redir_url += sprintf(redir_url, "term_width,term_height,");
}
if (GET_ARG("default_bg")) {
cgi_dbg("Screen default BG: %s", buff);
n = atoi(buff);
if (n >= 0 && n < 16) {
if (termconf->default_bg != n) {
termconf->default_bg = (u8) n;
shall_clear_screen = true;
}
} else {
cgi_warn("Bad color %s", buff);
redir_url += sprintf(redir_url, "default_bg,");
}
}
// Settings from SysConf - UART
bool uart_changed = false;
// restrict what keys are allowed
#define admin 0
#define tpl 0
#define XSTRUCT sysconf
#define X XSET_CGI_FUNC
XTABLE_SYSCONF
#undef X
#undef XSTRUCT
if (GET_ARG("default_fg")) {
cgi_dbg("Screen default FG: %s", buff);
n = atoi(buff);
if (n >= 0 && n < 16) {
if (termconf->default_fg != n) {
termconf->default_fg = (u8) n;
shall_clear_screen = true;
}
} else {
cgi_warn("Bad color %s", buff);
redir_url += sprintf(redir_url, "default_fg,");
}
}
if (GET_ARG("parser_tout_ms")) {
cgi_dbg("Parser timeout: %s ms", buff);
n = atoi(buff);
if (n >= 0) {
termconf->parser_tout_ms = n;
} else {
cgi_warn("Bad parser timeout %s", buff);
redir_url += sprintf(redir_url, "parser_tout_ms,");
}
}
if (GET_ARG("display_tout_ms")) {
cgi_dbg("Display update idle timeout: %s ms", buff);
n = atoi(buff);
if (n > 0) {
termconf->display_tout_ms = n;
} else {
cgi_warn("Bad update timeout %s", buff);
redir_url += sprintf(redir_url, "display_tout_ms,");
}
}
if (GET_ARG("display_cooldown_ms")) {
cgi_dbg("Display update cooldown: %s ms", buff);
n = atoi(buff);
if (n > 0) {
termconf->display_cooldown_ms = n;
} else {
cgi_warn("Bad cooldown %s", buff);
redir_url += sprintf(redir_url, "display_cooldown_ms,");
}
}
if (GET_ARG("fn_alt_mode")) {
cgi_dbg("FN alt mode: %s", buff);
n = atoi(buff);
termconf->fn_alt_mode = (bool)n;
notify_screen_content = true;
}
if (GET_ARG("want_all_fn")) {
cgi_dbg("AllFN mode: %s", buff);
n = atoi(buff);
termconf->want_all_fn = (bool)n;
}
if (GET_ARG("crlf_mode")) {
cgi_dbg("CRLF mode: %s", buff);
n = atoi(buff);
termconf->crlf_mode = (bool)n;
notify_screen_content = true;
}
if (GET_ARG("show_buttons")) {
cgi_dbg("Show buttons: %s", buff);
n = atoi(buff);
termconf->show_buttons = (bool)n;
notify_screen_content = true;
}
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;
}
if (GET_ARG("loopback")) {
cgi_dbg("Loopback: %s", buff);
n = atoi(buff);
termconf->loopback = (bool)n;
}
if (GET_ARG("theme")) {
cgi_dbg("Screen color theme: %s", buff);
n = atoi(buff);
if (n >= 0 && n <= 5) { // ALWAYS ADJUST WHEN ADDING NEW THEME!
termconf->theme = (u8) n;
// this can't be notified, page must reload.
} else {
cgi_warn("Bad theme num: %s", buff);
redir_url += sprintf(redir_url, "theme,");
}
}
if (GET_ARG("cursor_shape")) {
cgi_dbg("Cursor shape: %s", buff);
n = atoi(buff);
if (n >= 0 && n <= 6 && n != 1) {
termconf->cursor_shape = (enum CursorShape) n;
notify_screen_content = true;
} else {
cgi_warn("Bad cursor_shape num: %s", buff);
redir_url += sprintf(redir_url, "cursor_shape,");
}
}
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;
}
for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) {
sprintf(buff, "btn%d", btn_i);
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;
}
sprintf(buff, "bm%d", btn_i);
if (GET_ARG(buff)) {
cgi_dbg("Button%d message (ASCII): \"%s\"", btn_i, buff);
// parse: comma,space or semicolon separated decimal values of ASCII codes
char c;
char *cp = buff;
int char_i = 0;
int acu = 0;
bool lastsp = 1;
char buff_bm[TERM_BTN_MSG_LEN];
while ((c = *cp++) != 0) {
if (c == ' ' || c == ',' || c == ';') {
if(lastsp) continue;
if (acu==0 || acu>255) {
cgi_warn("Bad value! %d", acu);
redir_url += sprintf(redir_url, "bm%d,", btn_i);
break;
}
if (char_i >= TERM_BTN_MSG_LEN-1) {
cgi_warn("Too long! %d", acu);
redir_url += sprintf(redir_url, "bm%d,", btn_i);
break;
}
cgi_dbg("acu %d", acu);
buff_bm[char_i++] = (char)acu;
// prepare for next char
acu = 0;
lastsp = 1;
} else if (c>='0'&&c<='9') {
lastsp = 0;
acu *= 10;
acu += c - '0';
} else {
cgi_warn("Bad syntax!");
redir_url += sprintf(redir_url, "bm%d,", btn_i);
break;
}
}
if (lastsp && char_i == 0) {
cgi_warn("Required!");
redir_url += sprintf(redir_url, "bm%d,", btn_i);
}
if (!lastsp) {
buff_bm[char_i++] = (char)acu;
}
buff_bm[char_i] = 0;
cgi_dbg("%s, chari = %d", buff_bm, char_i);
strncpy(termconf->btn_msg[btn_i-1], buff_bm, TERM_BTN_MSG_LEN);
}
}
(void)redir_url;
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK
@@ -271,26 +71,37 @@ cgiTermCfgSetParams(HttpdConnData *connData)
persist_store();
bool shall_clear_screen = false;
shall_clear_screen |= (termconf_backup->width != termconf->width);
shall_clear_screen |= (termconf_backup->height != termconf->height);
shall_clear_screen |= (termconf_backup->ascii_debug != termconf->ascii_debug);
if (shall_clear_screen) {
terminal_apply_settings();
} else {
terminal_apply_settings_noclear();
}
if (notify_screen_content) {
screen_notifyChange(CHANGE_CONTENT);
if (uart_changed) {
sysconf_apply_settings();
}
if (notify_screen_labels) {
screen_notifyChange(CHANGE_LABELS);
}
// Trigger a full reload
screen_notifyChange(TOPIC_INITIAL);
httpdRedirect(connData, SET_REDIR_SUC);
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
} else {
cgi_warn("Some settings did not validate, asking for correction");
memcpy(sysconf, sysconf_backup, sizeof(SystemConfigBundle));
memcpy(termconf, termconf_backup, sizeof(TerminalConfigBundle));
// Some errors, appended to the URL as ?err=
httpdRedirect(connData, redir_url_buf);
}
free(sysconf_backup);
free(termconf_backup);
return HTTPD_CGI_DONE;
}
@@ -298,9 +109,8 @@ cgiTermCfgSetParams(HttpdConnData *connData)
httpd_cgi_state ICACHE_FLASH_ATTR
tplTermCfg(HttpdConnData *connData, char *token, void **arg)
{
#define BUFLEN TERM_TITLE_LEN
#define BUFLEN 100 // large enough for backdrop
char buff[BUFLEN];
char buff2[10];
if (token == NULL) {
// We're done
@@ -309,80 +119,18 @@ tplTermCfg(HttpdConnData *connData, char *token, void **arg)
strcpy(buff, ""); // fallback
if (streq(token, "term_width")) {
sprintf(buff, "%d", termconf->width);
}
else if (streq(token, "term_height")) {
sprintf(buff, "%d", termconf->height);
}
else if (streq(token, "parser_tout_ms")) {
sprintf(buff, "%d", termconf->parser_tout_ms);
}
else if (streq(token, "display_tout_ms")) {
sprintf(buff, "%d", termconf->display_tout_ms);
}
else if (streq(token, "display_cooldown_ms")) {
sprintf(buff, "%d", termconf->display_cooldown_ms);
}
else if (streq(token, "fn_alt_mode")) {
sprintf(buff, "%d", (int)termconf->fn_alt_mode);
}
else if (streq(token, "want_all_fn")) {
sprintf(buff, "%d", (int)termconf->want_all_fn);
}
else if (streq(token, "crlf_mode")) {
sprintf(buff, "%d", (int)termconf->crlf_mode);
}
else if (streq(token, "show_buttons")) {
sprintf(buff, "%d", (int)termconf->show_buttons);
}
else if (streq(token, "show_config_links")) {
sprintf(buff, "%d", (int)termconf->show_config_links);
}
else if (streq(token, "loopback")) {
sprintf(buff, "%d", (int)termconf->loopback);
}
else if (streq(token, "theme")) {
sprintf(buff, "%d", termconf->theme);
}
else if (streq(token, "default_bg")) {
sprintf(buff, "%d", termconf->default_bg);
}
else if (streq(token, "default_fg")) {
sprintf(buff, "%d", termconf->default_fg);
}
else if (streq(token, "cursor_shape")) {
sprintf(buff, "%d", termconf->cursor_shape);
}
else if (streq(token, "term_title")) {
strncpy_safe(buff, termconf->title, BUFLEN);
}
else {
for (int btn_i = 1; btn_i <= TERM_BTN_COUNT; btn_i++) {
sprintf(buff2, "btn%d", btn_i);
if (streq(token, buff2)) {
strncpy_safe(buff, termconf->btn[btn_i-1], BUFLEN);
break;
}
#define XSTRUCT termconf
#define X XGET_CGI_FUNC
XTABLE_TERMCONF
#undef X
#undef XSTRUCT
sprintf(buff2, "bm%d", btn_i);
if (streq(token, buff2)) {
char c;
char *bp = buff;
char *cp = termconf->btn_msg[btn_i-1];
int n = 0;
while((c = *cp++) != 0) {
if(n>0) {
*bp = ',';
bp++;
}
bp += sprintf(bp, "%d", (int)c);
n++;
}
break;
}
}
}
// for uart
#define XSTRUCT sysconf
#define X XGET_CGI_FUNC
XTABLE_SYSCONF
#undef X
#undef XSTRUCT
tplSend(connData, buff, -1);
return HTTPD_CGI_DONE;
+29 -203
View File
@@ -19,6 +19,7 @@ Cgi/template routines for the /wifi url.
#include "wifimgr.h"
#include "persist.h"
#include "helpers.h"
#include "config_xmacros.h"
#include "cgi_logging.h"
#define SET_REDIR_SUC "/cfg/wifi"
@@ -66,46 +67,6 @@ int ICACHE_FLASH_ATTR rssi2perc(int rssi)
return r;
}
/**
* Convert Auth type to string
*/
const ICACHE_FLASH_ATTR char *auth2str(AUTH_MODE auth)
{
switch (auth) {
case AUTH_OPEN:
return "Open";
case AUTH_WEP:
return "WEP";
case AUTH_WPA_PSK:
return "WPA";
case AUTH_WPA2_PSK:
return "WPA2";
case AUTH_WPA_WPA2_PSK:
return "WPA/WPA2";
default:
return "Unknown";
}
}
/**
* Convert WiFi opmode to string
*/
const ICACHE_FLASH_ATTR char *opmode2str(WIFI_MODE opmode)
{
switch (opmode) {
case NULL_MODE:
return "Disabled";
case STATION_MODE:
return "Client";
case SOFTAP_MODE:
return "AP only";
case STATIONAP_MODE:
return "Client+AP";
default:
return "Unknown";
}
}
/**
* Callback the code calls when a wlan ap scan is done. Basically stores the result in
* the static cgiWifiAps struct.
@@ -355,21 +316,22 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
return HTTPD_CGI_DONE;
}
WiFiConfigBundle *wificonf_backup = malloc(sizeof(WiFiConfigBundle));
WiFiConfChangeFlags *wcf_backup = malloc(sizeof(WiFiConfChangeFlags));
memcpy(wificonf_backup, wificonf, sizeof(WiFiConfigBundle));
memcpy(wcf_backup, &wifi_change_flags, sizeof(WiFiConfChangeFlags));
bool sta_turned_on = false;
bool sta_ssid_pw_changed = false;
#define XSTRUCT wificonf
#define X XSET_CGI_FUNC
XTABLE_WIFICONF
#undef X
// ---- WiFi opmode ----
if (GET_ARG("opmode")) {
cgi_dbg("Setting WiFi opmode to: %s", buff);
int mode = atoi(buff);
if (mode > NULL_MODE && mode < MAX_MODE) {
wificonf->opmode = (WIFI_MODE) mode;
} else {
cgi_warn("Bad opmode value \"%s\"", buff);
redir_url += sprintf(redir_url, "opmode,");
}
}
// those are helpers, not a real prop
if (GET_ARG("ap_enable")) {
cgi_dbg("Enable AP: %s", buff);
@@ -394,113 +356,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
}
}
// ---- AP transmit power ----
if (GET_ARG("tpw")) {
cgi_dbg("Setting AP power to: %s", buff);
int tpw = atoi(buff);
if (tpw >= 0 && tpw <= 82) { // 0 actually isn't 0 but quite low. 82 is very strong
if (wificonf->tpw != tpw) {
wificonf->tpw = (u8) tpw;
wifi_change_flags.ap = true;
}
} else {
cgi_warn("tpw %s out of allowed range 0-82.", buff);
redir_url += sprintf(redir_url, "tpw,");
}
}
// ---- AP channel (applies in AP-only mode) ----
if (GET_ARG("ap_channel")) {
cgi_info("ap_channel = %s", buff);
int channel = atoi(buff);
if (channel > 0 && channel < 15) {
if (wificonf->ap_channel != channel) {
wificonf->ap_channel = (u8) channel;
wifi_change_flags.ap = true;
}
} else {
cgi_warn("Bad channel value \"%s\", allowed 1-14", buff);
redir_url += sprintf(redir_url, "ap_channel,");
}
}
// ---- SSID name in AP mode ----
if (GET_ARG("ap_ssid")) {
// Replace all invalid ASCII with underscores
int i;
for (i = 0; i < 32; i++) {
char c = buff[i];
if (c == 0) break;
if (c < 32 || c >= 127) buff[i] = '_';
}
buff[i] = 0;
if (strlen(buff) > 0) {
if (!streq(wificonf->ap_ssid, buff)) {
cgi_info("Setting SSID to \"%s\"", buff);
strncpy_safe(wificonf->ap_ssid, buff, SSID_LEN);
wifi_change_flags.ap = true;
}
} else {
cgi_warn("Bad SSID len.");
redir_url += sprintf(redir_url, "ap_ssid,");
}
}
// ---- AP password ----
if (GET_ARG("ap_password")) {
// Users are free to use any stupid shit in ther password,
// but it may lock them out.
if (strlen(buff) == 0 || (strlen(buff) >= 8 && strlen(buff) < PASSWORD_LEN-1)) {
if (!streq(wificonf->ap_password, buff)) {
cgi_info("Setting AP password to \"%s\"", buff);
strncpy_safe(wificonf->ap_password, buff, PASSWORD_LEN);
wifi_change_flags.ap = true;
}
} else {
cgi_warn("Bad password len.");
redir_url += sprintf(redir_url, "ap_password,");
}
}
// ---- Hide AP network (do not announce) ----
if (GET_ARG("ap_hidden")) {
cgi_dbg("AP hidden = %s", buff);
int hidden = atoi(buff);
if (hidden != wificonf->ap_hidden) {
wificonf->ap_hidden = (hidden != 0);
wifi_change_flags.ap = true;
}
}
// ---- Station SSID (to connect to) ----
if (GET_ARG("sta_ssid")) {
if (!streq(wificonf->sta_ssid, buff)) {
// No verification needed, at worst it fails to connect
cgi_info("Setting station SSID to: \"%s\"", buff);
strncpy_safe(wificonf->sta_ssid, buff, SSID_LEN);
wifi_change_flags.sta = true;
sta_ssid_pw_changed = true;
}
}
// ---- Station password (empty for none is allowed) ----
if (GET_ARG("sta_password")) {
if (!streq(wificonf->sta_password, buff)) {
// No verification needed, at worst it fails to connect
cgi_info("Setting station password to: \"%s\"", buff);
strncpy_safe(wificonf->sta_password, buff, PASSWORD_LEN);
wifi_change_flags.sta = true;
sta_ssid_pw_changed = true;
}
}
(void)redir_url;
if (redir_url_buf[strlen(SET_REDIR_ERR)] == 0) {
// All was OK
@@ -528,17 +384,23 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
httpdRedirect(connData, "/cfg/wifi/connecting");
}
else {
httpdRedirect(connData, SET_REDIR_SUC);
httpdRedirect(connData, SET_REDIR_SUC "?msg=Settings%20saved%20and%20applied.");
}
} else {
cgi_warn("Some WiFi settings did not validate, asking for correction");
memcpy(wificonf, wificonf_backup, sizeof(WiFiConfigBundle));
memcpy(&wifi_change_flags, wcf_backup, sizeof(WiFiConfChangeFlags));
// Some errors, appended to the URL as ?err=
httpdRedirect(connData, redir_url_buf);
}
free(wificonf_backup);
free(wcf_backup);
return HTTPD_CGI_DONE;
}
//Template code for the WLAN page.
httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg)
{
@@ -553,39 +415,18 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token,
strcpy(buff, ""); // fallback
if (streq(token, "opmode_name")) {
strcpy(buff, opmode2str(wificonf->opmode));
}
else if (streq(token, "opmode")) {
sprintf(buff, "%d", wificonf->opmode);
}
else if (streq(token, "sta_enable")) {
#define XSTRUCT wificonf
#define X XGET_CGI_FUNC
XTABLE_WIFICONF
#undef X
// non-config
if (streq(token, "sta_enable")) {
sprintf(buff, "%d", (wificonf->opmode & STATION_MODE) != 0);
}
else if (streq(token, "ap_enable")) {
sprintf(buff, "%d", (wificonf->opmode & SOFTAP_MODE) != 0);
}
else if (streq(token, "tpw")) {
sprintf(buff, "%d", wificonf->tpw);
}
else if (streq(token, "ap_channel")) {
sprintf(buff, "%d", wificonf->ap_channel);
}
else if (streq(token, "ap_ssid")) {
sprintf(buff, "%s", wificonf->ap_ssid);
}
else if (streq(token, "ap_password")) {
sprintf(buff, "%s", wificonf->ap_password);
}
else if (streq(token, "ap_hidden")) {
sprintf(buff, "%d", wificonf->ap_hidden);
}
else if (streq(token, "sta_ssid")) {
sprintf(buff, "%s", wificonf->sta_ssid);
}
else if (streq(token, "sta_password")) {
sprintf(buff, "%s", wificonf->sta_password);
}
else if (streq(token, "sta_rssi")) {
sprintf(buff, "%d", wifi_station_get_rssi());
}
@@ -603,22 +444,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);
+336 -127
View File
@@ -19,143 +19,352 @@
* Based on rxvt-unicode screen.C table.
*/
static const u16 codepage_0[] ESP_CONST_DATA =
{// Unicode ASCII SYM
{ // Unicode ASCII SYM
// %%BEGIN:0%%
0x2666, // 96 `
0x2592, // 97 a
0x2409, // 98 b HT
0x240c, // 99 c FF
0x240d, // 100 d CR
0x240a, // 101 e LF
0x00b0, // 102 f °
0x00b1, // 103 g ±
0x2424, // 104 h NL
0x240b, // 105 i VT
0x2518, // 106 j
0x2510, // 107 k
0x250c, // 108 l
0x2514, // 109 m
0x253c, // 110 n
0x23ba, // 111 o
0x23bb, // 112 p
0x2500, // 113 q
0x23bc, // 114 r
0x23bd, // 115 s
0x251c, // 116 t
0x2524, // 117 u
0x2534, // 118 v
0x252c, // 119 w
0x2502, // 120 x
0x2264, // 121 y
0x2265, // 122 z
0x03c0, // 123 { π
0x2260, // 124 |
0x20a4, // 125 } £
0x00b7, // 126 ~ ·
u'', // 0x2666 96 `
u'', // 0x2592 97 a
u'', // 0x2409 98 b
u'', // 0x240c 99 c FF
u'', // 0x240d 100 d CR
u'', // 0x240a 101 e LF
u'°', // 0x00b0 102 f
u'±', // 0x00b1 103 g
u'', // 0x2424 104 h NL
u'', // 0x240b 105 i VT
u'', // 0x2518 106 j
u'', // 0x2510 107 k
u'', // 0x250c 108 l
u'', // 0x2514 109 m
u'', // 0x253c 110 n
u'', // 0x23ba 111 o
u'', // 0x23bb 112 p
u'', // 0x2500 113 q
u'', // 0x23bc 114 r
u'', // 0x23bd 115 s
u'', // 0x251c 116 t
u'', // 0x2524 117 u
u'', // 0x2534 118 v
u'', // 0x252c 119 w
u'', // 0x2502 120 x
u'', // 0x2264 121 y
u'', // 0x2265 122 z
u'π', // 0x03c0 123 {
u'', // 0x2260 124 |
u'£', // 0x20a4 125 }
u'·', // 0x00b7 126 ~
// %%END:0%%
};
#define CODEPAGE_1_BEGIN 33
#define CODEPAGE_1_END 126
// DOS, thin and double lines, arrows, angles, diagonals, thick border
static const u16 codepage_1[] ESP_CONST_DATA =
{// Unicode ASCII SYM DOS
{// Unicode ASCII DOS
// %%BEGIN:1%%
0x263A, // 33 ! (1) - low ASCII symbols from DOS, moved to +32
0x263B, // 34 " (2)
0x2665, // 35 # (3)
0x2666, // 36 $ (4)
0x2663, // 37 % (5)
0x2660, // 38 & (6)
0x2022, // 39 ' (7) - inverse dot and circle left out, can be done with SGR
0x231B, // 40 ( ⌛ - hourglass (timer icon)
0x25CB, // 41 ) (9)
0x21AF, // 42 * ↯ - electricity (lightning monitor...)
0x266A, // 43 + (13)
0x266B, // 44 , (14)
0x263C, // 45 - (15)
0x2302, // 46 . (127)
0x2622, // 47 / ☢ - radioactivity (geiger counter...)
0x2591, // 48 0 (176) - this block is kept aligned and ordered from DOS, moved -128
0x2592, // 49 1 (177)
0x2593, // 50 2 (178)
0x2502, // 51 3 (179)
0x2524, // 52 4 (180)
0x2561, // 53 5 (181)
0x2562, // 54 6 (182)
0x2556, // 55 7 (183)
0x2555, // 56 8 (184)
0x2563, // 57 9 (185)
0x2551, // 58 : (186)
0x2557, // 59 ; (187)
0x255D, // 60 < (188)
0x255C, // 61 = (189)
0x255B, // 62 > (190)
0x2510, // 63 ? (191)
0x2514, // 64 @ (192)
0x2534, // 65 A (193)
0x252C, // 66 B (194)
0x251C, // 67 C (195)
0x2500, // 68 D (196)
0x253C, // 69 E (197)
0x255E, // 70 F (198)
0x255F, // 71 G (199)
0x255A, // 72 H (200)
0x2554, // 73 I (201)
0x2569, // 74 J (202)
0x2566, // 75 K (203)
0x2560, // 76 L (204)
0x2550, // 77 M (205)
0x256C, // 78 N (206)
0x2567, // 79 O (207)
0x2568, // 80 P (208)
0x2564, // 81 Q (209)
0x2565, // 82 R (210)
0x2559, // 83 S (211)
0x2558, // 84 T (212)
0x2552, // 85 U (213)
0x2553, // 86 V (214)
0x256B, // 87 W (215)
0x256A, // 88 X (216)
0x2518, // 89 Y (217)
0x250C, // 90 Z (218)
0x2588, // 91 [ (219)
0x2584, // 92 \ (220)
0x258C, // 93 ] (221)
0x2590, // 94 ^ (222)
0x2580, // 95 _ (223)
0x2195, // 96 ` (18) - moved from low DOS ASCII
0x2191, // 97 a (24)
0x2193, // 98 b (25)
0x2192, // 99 c (26)
0x2190, // 100 d (27)
0x2194, // 101 e (29)
0x25B2, // 102 f (30)
0x25BC, // 103 g (31)
0x25BA, // 104 h (16)
0x25C4, // 105 i (17)
0x25E2, // 106 j ◢ - added for slanted corners
0x25E3, // 107 k
0x25E4, // 108 l
0x25E5, // 109 m
0x256D, // 110 n ╭ - rounded corners
0x256E, // 111 o
0x256F, // 112 p
0x2570, // 113 q
0x0, // 114 r - free positions for future expansion
0x0, // 115 s
0x0, // 116 t
0x0, // 117 u
0x0, // 118 v
0x0, // 119 w
0x0, // 120 x
0x0, // 121 y
0x0, // 122 z
0x0, // 123 {
0x0, // 124 |
0x2714, // 125 } ✔ - checkboxes or checklist items
0x2718, // 126 ~
u'', // 0x263A, 33 ! (1) - low ASCII symbols from DOS, moved to +32
u'', // 0x263B, 34 " (2)
u'', // 0x2665, 35 # (3)
u'', // 0x2666, 36 $ (4)
u'', // 0x2663, 37 % (5)
u'', // 0x2660, 38 & (6)
u'', // 0x2022, 39 ' (7) - inverse dot and circle left out, can be done with SGR
u'', // 0x231B, 40 ( - hourglass (timer icon)
u'', // 0x25CB, 41 ) (9)
u'', // 0x21AF, 42 * - electricity (lightning monitor...)
u'', // 0x266A, 43 + (13)
u'', // 0x266B, 44 , (14)
u'', // 0x263C, 45 - (15)
u'', // 0x2302, 46 . (127)
u'', // 0x2622, 47 / - radioactivity (geiger counter...)
u'', // 0x2591, 48 0 (176) - this block is kept aligned and ordered from DOS, moved -128
u'', // 0x2592, 49 1 (177)
u'', // 0x2593, 50 2 (178)
u'', // 0x2502, 51 3 (179)
u'', // 0x2524, 52 4 (180)
u'', // 0x2561, 53 5 (181)
u'', // 0x2562, 54 6 (182)
u'', // 0x2556, 55 7 (183)
u'', // 0x2555, 56 8 (184)
u'', // 0x2563, 57 9 (185)
u'', // 0x2551, 58 : (186)
u'', // 0x2557, 59 ; (187)
u'', // 0x255D, 60 < (188)
u'', // 0x255C, 61 = (189)
u'', // 0x255B, 62 > (190)
u'', // 0x2510, 63 ? (191)
u'', // 0x2514, 64 @ (192)
u'', // 0x2534, 65 A (193)
u'', // 0x252C, 66 B (194)
u'', // 0x251C, 67 C (195)
u'', // 0x2500, 68 D (196)
u'', // 0x253C, 69 E (197)
u'', // 0x255E, 70 F (198)
u'', // 0x255F, 71 G (199)
u'', // 0x255A, 72 H (200)
u'', // 0x2554, 73 I (201)
u'', // 0x2569, 74 J (202)
u'', // 0x2566, 75 K (203)
u'', // 0x2560, 76 L (204)
u'', // 0x2550, 77 M (205)
u'', // 0x256C, 78 N (206)
u'', // 0x2567, 79 O (207)
u'', // 0x2568, 80 P (208)
u'', // 0x2564, 81 Q (209)
u'', // 0x2565, 82 R (210)
u'', // 0x2559, 83 S (211)
u'', // 0x2558, 84 T (212)
u'', // 0x2552, 85 U (213)
u'', // 0x2553, 86 V (214)
u'', // 0x256B, 87 W (215)
u'', // 0x256A, 88 X (216)
u'', // 0x2518, 89 Y (217)
u'', // 0x250C, 90 Z (218)
u'', // 0x2588, 91 [ (219)
u'', // 0x2584, 92 \ (220)
u'', // 0x258C, 93 ] (221)
u'', // 0x2590, 94 ^ (222)
u'', // 0x2580, 95 _ (223)
u'', // 0x2195, 96 ` (18) - moved from low DOS ASCII
u'', // 0x2191, 97 a (24)
u'', // 0x2193, 98 b (25)
u'', // 0x2192, 99 c (26)
u'', // 0x2190, 100 d (27)
u'', // 0x2194, 101 e (29)
u'', // 0x25B2, 102 f (30)
u'', // 0x25BC, 103 g (31)
u'', // 0x25BA, 104 h (16)
u'', // 0x25C4, 105 i (17)
u'', // 0x25E2, 106 j - added for slanted corners
u'', // 0x25E3, 107 k
u'', // 0x25E4, 108 l
u'', // 0x25E5, 109 m
u'', // 0x256D, 110 n - rounded corners
u'', // 0x256E, 111 o
u'', // 0x256F, 112 p
u'', // 0x2570, 113 q
u'', // 0x0, 114 r - right up diagonal
u'', // 0x0, 115 s - right down diagonal
u'', // 0x0, 116 t
u'', // 0x0, 117 u
u'', // 0x0, 118 v
u'', // 0x0, 119 w
u'', // 0x0, 120 x
0xE0B0, // powerline right triangle (filled), 121 y
0xE0B1, // powerline right triangle (hollow), 122 z
0xE0B2, // powerline left triangle (filled), 123 {
0xE0B3, // powerline left triangle (hollow), 124 | - reserved
u'', // 0x2714, 125 } - checkboxes or checklist items
u'', // 0x2718, 126 ~
// %%END:1%%
};
#define CODEPAGE_2_BEGIN 33
#define CODEPAGE_2_END 126
// blocks, thick and split lines, line butts
static const u16 codepage_2[] ESP_CONST_DATA =
{// Unicode ASCII DOS
// %%BEGIN:2%%
u'', // 0x2581, 33 ! - those are ordered this way to allow easy calculating of the right code (for graphs)
u'', // 0x2582, 34 "
u'', // 0x2583, 35 #
u'', // 0x2584, 36 $
u'', // 0x2585, 37 %
u'', // 0x2586, 38 &
u'', // 0x2587, 39 ' - 7-eighths
u'', // 0x2588, 40 ( - full block, shared by both sequences
u'', // 0x2589, 41 ) - those grow thinner, to re-use the full block
u'', // 0x258A, 42 *
u'', // 0x258B, 43 +
u'', // 0x258C, 44 ,
u'', // 0x258D, 45 -
u'', // 0x258E, 46 .
u'', // 0x258F, 47 /
u'', // 0x2594, 48 0 - complementary symbols
u'', // 0x2595, 49 1
u'', // 0x2590, 50 2
u'', // 0x2580, 51 3
u'', // 0x2598, 52 4 - top-left, top-right, bottom-right, bottom-left
u'', // 0x259D, 53 5
u'', // 0x2597, 54 6
u'', // 0x2596, 55 7
u'', // 0x259F, 56 8
u'', // 0x2599, 57 9
u'', // 0x259B, 58 :
u'', // 0x259C, 59 ;
u'', // 0x259E, 60 < - complementary diagonals
u'', // 0x259A, 61 =
u'', // 0x, 62 > - here are thick and thin lines and their joins. it's really quite arbitrary, based on the unicode order, excluding single lines
u'', // 0x, 63 ?
u'', // 0x, 64 @
u'', // 0x, 65 A
u'', // 0x, 66 B
u'', // 0x, 67 C
u'', // 0x, 68 D
u'', // 0x, 69 E
u'', // 0x, 70 F
u'', // 0x, 71 G
u'', // 0x, 72 H
u'', // 0x, 73 I
u'', // 0x, 74 J
u'', // 0x, 75 K
u'', // 0x, 76 L
u'', // 0x, 77 M
u'', // 0x, 78 N
u'', // 0x, 79 O
u'', // 0x, 80 P
u'', // 0x, 81 Q
u'', // 0x, 82 R
u'', // 0x, 83 S
u'', // 0x, 84 T
u'', // 0x, 85 U
u'', // 0x, 86 V
u'', // 0x, 87 W
u'', // 0x, 88 X
u'', // 0x, 89 Y
u'', // 0x, 90 Z
u'', // 0x, 91 [
u'', // 0x, 92 \ .
u'', // 0x, 93 ]
u'', // 0x, 94 ^
u'', // 0x, 95 _
u'', // 0x, 96 `
u'', // 0x, 97 a
u'', // 0x, 98 b
u'', // 0x, 99 c
u'', // 0x, 100 d
u'', // 0x, 101 e
u'', // 0x, 102 f
u'', // 0x, 103 g
u'', // 0x, 104 h
u'', // 0x, 105 i
u'', // 0x, 106 j
u'', // 0x, 107 k
u'', // 0x, 108 l
u'', // 0x, 109 m
u'', // 0x, 110 n
u'', // 0x, 111 o
u'', // 0x, 112 p
u'', // 0x, 113 q
u'', // 0x, 114 r
u'', // 0x, 115 s
u'', // 0x, 116 t
u'', // 0x, 117 u
u'', // 0x, 118 v
u'', // 0x, 119 w - butts
u'', // 0x, 120 x
u'', // 0x, 121 y
u'', // 0x, 122 z
u'', // 0x, 123 {
u'', // 0x, 124 |
u'', // 0x, 125 }
u'', // 0x, 126 ~
// %%END:2%%
};
#define CODEPAGE_3_BEGIN 33
#define CODEPAGE_3_END 48
// dashed lines, split straight lines
static const u16 codepage_3[] ESP_CONST_DATA =
{// Unicode ASCII DOS
// %%BEGIN:3%%
u'', // 0x, 33 !
u'', // 0x, 34 "
u'', // 0x, 35 #
u'', // 0x, 36 $
u'', // 0x, 37 %
u'', // 0x, 38 &
u'', // 0x, 39 '
u'', // 0x, 40 (
u'', // 0x, 41 )
u'', // 0x, 42 *
u'', // 0x, 43 +
u'', // 0x, 44 ,
u'', // 0x, 45 -
u'', // 0x, 46 .
u'', // 0x, 47 /
u'', // 0x, 48 0
// %%END:3%%
// u'\0', // 0x, 49 1
// u'\0', // 0x, 50 2
// u'\0', // 0x, 51 3
// u'\0', // 0x, 52 4
// u'\0', // 0x, 53 5
// u'\0', // 0x, 54 6
// u'\0', // 0x, 55 7
// u'\0', // 0x, 56 8
// u'\0', // 0x, 57 9
// u'\0', // 0x, 58 :
// u'\0', // 0x, 59 ;
// u'\0', // 0x, 60 <
// u'\0', // 0x, 61 =
// u'\0', // 0x, 62 >
// u'\0', // 0x, 63 ?
// u'\0', // 0x, 64 @
// u'\0', // 0x, 65 A
// u'\0', // 0x, 66 B
// u'\0', // 0x, 67 C
// u'\0', // 0x, 68 D
// u'\0', // 0x, 69 E
// u'\0', // 0x, 70 F
// u'\0', // 0x, 71 G
// u'\0', // 0x, 72 H
// u'\0', // 0x, 73 I
// u'\0', // 0x, 74 J
// u'\0', // 0x, 75 K
// u'\0', // 0x, 76 L
// u'\0', // 0x, 77 M
// u'\0', // 0x, 78 N
// u'\0', // 0x, 79 O
// u'\0', // 0x, 80 P
// u'\0', // 0x, 81 Q
// u'\0', // 0x, 82 R
// u'\0', // 0x, 83 S
// u'\0', // 0x, 84 T
// u'\0', // 0x, 85 U
// u'\0', // 0x, 86 V
// u'\0', // 0x, 87 W
// u'\0', // 0x, 88 X
// u'\0', // 0x, 89 Y
// u'\0', // 0x, 90 Z
// u'\0', // 0x, 91 [
// u'\0', // 0x, 92 \ .
// u'\0', // 0x, 93 ]
// u'\0', // 0x, 94 ^
// u'\0', // 0x, 95 _
// u'\0', // 0x, 96 `
// u'\0', // 0x, 97 a
// u'\0', // 0x, 98 b
// u'\0', // 0x, 99 c
// u'\0', // 0x, 100 d
// u'\0', // 0x, 101 e
// u'\0', // 0x, 102 f
// u'\0', // 0x, 103 g
// u'\0', // 0x, 104 h
// u'\0', // 0x, 105 i
// u'\0', // 0x, 106 j
// u'\0', // 0x, 107 k
// u'\0', // 0x, 108 l
// u'\0', // 0x, 109 m
// u'\0', // 0x, 110 n
// u'\0', // 0x, 111 o
// u'\0', // 0x, 112 p
// u'\0', // 0x, 113 q
// u'\0', // 0x, 114 r
// u'\0', // 0x, 115 s
// u'\0', // 0x, 116 t
// u'\0', // 0x, 117 u
// u'\0', // 0x, 118 v
// u'\0', // 0x, 119 w
// u'\0', // 0x, 120 x
// u'\0', // 0x, 121 y
// u'\0', // 0x, 122 z
// u'\0', // 0x, 123 {
// u'\0', // 0x, 124 |
// u'\0', // 0x, 125 }
// u'\0', // 0x, 126 ~
};
#endif //ESPTERM_CHARACTER_SETS_H_H
+144
View File
@@ -0,0 +1,144 @@
//
// Created by MightyPork on 2017/10/22.
//
#include "config_xmacros.h"
#include "cgi_logging.h"
void ICACHE_FLASH_ATTR xget_dec(char *buff, u32 value)
{
sprintf(buff, "%d", value);
}
void ICACHE_FLASH_ATTR xget_bool(char *buff, bool value)
{
sprintf(buff, "%d", value?1:0);
}
void ICACHE_FLASH_ATTR xget_ustring(char *buff, const u8 *value)
{
sprintf(buff, "%s", (const char *) value);
}
void ICACHE_FLASH_ATTR xget_string(char *buff, const char *value)
{
sprintf(buff, "%s", value);
}
void ICACHE_FLASH_ATTR xget_ip(char *buff, const struct ip_addr *value)
{
sprintf(buff, IPSTR, GOOD_IP2STR(value->addr));
}
// ------------- XSET -------------
enum xset_result ICACHE_FLASH_ATTR
xset_ip(const char *name, struct ip_addr *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
u32 ip = ipaddr_addr(buff);
if (ip != 0 && ip != 0xFFFFFFFFUL) {
if (field->addr != ip) {
field->addr = ip;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad IP: %s", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_bool(const char *name, bool *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
bool enable = (atoi(buff) != 0);
if (*field != enable) {
*field = enable;
return XSET_SET;
}
return XSET_UNCHANGED;
}
enum xset_result ICACHE_FLASH_ATTR
xset_u8(const char *name, u8 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
u32 val = (u32) atoi(buff);
if (val <= 255) {
if (*field != val) {
*field = (u8) val;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad value, max 255: %s", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_u32(const char *name, u32 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
u32 val = (u32) atoi(buff);
if (*field != val) {
*field = (u32) val;
return XSET_SET;
}
return XSET_UNCHANGED;
}
enum xset_result ICACHE_FLASH_ATTR
xset_u16(const char *name, u16 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
u16 val = (u16) atoi(buff);
if (*field != val) {
*field = (u16) val;
return XSET_SET;
}
return XSET_UNCHANGED;
}
enum xset_result ICACHE_FLASH_ATTR
xset_string(const char *name, char *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
u32 maxlen = (u32) arg;
if (arg > 0 && (u32)strlen(buff) > maxlen) {
cgi_warn("String too long, max %d", maxlen);
return XSET_FAIL;
}
if (!streq(field, buff)) {
strncpy_safe(field, buff, (u32)arg);
return XSET_SET;
}
return XSET_UNCHANGED;
}
enum xset_result ICACHE_FLASH_ATTR
xset_ustring(const char *name, uchar *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
u32 maxlen = (u32) arg;
if (arg > 0 && (u32)strlen(buff) > maxlen) {
cgi_warn("String too long, max %d", maxlen);
return XSET_FAIL;
}
if (!streq(field, buff)) {
strncpy_safe(field, buff, (u32)arg);
return XSET_SET;
}
return XSET_UNCHANGED;
}
+97
View File
@@ -0,0 +1,97 @@
//
// Created by MightyPork on 2017/10/22.
//
#ifndef ESPTERM_CONFIG_XMACROS_H
#define ESPTERM_CONFIG_XMACROS_H
#include <esp8266.h>
#include <helpers.h>
typedef unsigned char uchar;
#define XJOIN(a, b) a##b
/**Do nothing xnotify */
#define xnoop()
/**
* XGET interface
*
* @param buff - buffer where the value should be printed
* @param value - value to render to the buffer
*/
static inline bool xget_dummy(char *buff, u32 value)
{
sprintf(buff, "unused %d", value);
return false;
}
void xget_dec(char *buff, u32 value);
void xget_bool(char *buff, bool value);
void xget_ustring(char *buff, const u8 *value);
void xget_string(char *buff, const char *value);
void xget_ip(char *buff, const struct ip_addr *value);
void xget_dhcp(char *buff, const struct dhcps_lease *value);
/**
* XSET interface
*
* @param name - field name (for debug)
* @param field - pointer to the target field
* @param buff - field with the value to be set
* @param arg - arbitrary argument, used to modify behavior
*
* @return xset_result
*/
enum xset_result {
XSET_FAIL = 0,
XSET_SET = 1,
XSET_UNCHANGED = 2,
XSET_NONE = 3,
};
// Dummy for unimplemented setters
static inline enum xset_result xset_dummy(const char *name, void *field, const char *buff, const void *arg)
{
return XSET_UNCHANGED;
}
enum xset_result xset_ip(const char *name, struct ip_addr *field, const char *buff, const void *arg);
enum xset_result xset_bool(const char *name, bool *field, const char *buff, const void *arg);
enum xset_result xset_u8(const char *name, u8 *field, const char *buff, const void *arg);
enum xset_result xset_u32(const char *name, u32 *field, const char *buff, const void *arg);
enum xset_result xset_u16(const char *name, u16 *field, const char *buff, const void *arg);
// static string arrays are not &'d, so we don't get **
/** @param arg - max string length */
enum xset_result xset_string(const char *name, char *field, const char *buff, const void *arg);
enum xset_result xset_ustring(const char *name, u8 *field, const char *buff, const void *arg);
/**
* Helper template macro for CGI functions that load GET args to structs using XTABLE
*
* If 'name' is found in connData->getArgs, xset() is called.
* If the result is SET, xnotify() is fired. Else, 'name,' is appended to the redir_url buffer.
*/
#define XSET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
if ((allow) && GET_ARG(#name)) { \
type *_p = (type *) &XSTRUCT->name; \
enum xset_result res = xset(#name, _p, buff, (const void*) (xsarg)); \
if (res == XSET_SET) { xnotify; } \
else if (res == XSET_FAIL) { redir_url += sprintf(redir_url, #name","); } \
}
#define XGET_CGI_FUNC(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
if ((allow) && streq(token, #name)) xget(buff, deref XSTRUCT->name);
#define XSTRUCT_FIELD(type, name, suffix, deref, xget, xset, xsarg, xnotify, allow) \
type name suffix;
#define XDUMP_FIELD(type, name, suffix, deref, xget, allow, xset, xsarg, xnotify) \
{ xget(buff, deref XSTRUCT->name); dbg(#name " = %s", buff); }
#endif //ESPTERM_CONFIG_XMACROS_H
+526
View File
@@ -0,0 +1,526 @@
/* #line 1 "user/ini_parser.rl" */
/* Ragel constants block */
#include "ini_parser.h"
// Ragel setup
/* #line 10 "user/ini_parser.c" */
static const char _ini_actions[] ESP_CONST_DATA = {
0, 1, 1, 1, 2, 1, 3, 1,
4, 1, 5, 1, 6, 1, 7, 1,
8, 1, 9, 1, 10, 1, 11, 1,
13, 2, 0, 4, 2, 12, 4
};
static const char _ini_eof_actions[] ESP_CONST_DATA = {
0, 23, 5, 5, 15, 15, 15, 15,
19, 19, 0, 0, 0, 0, 0, 0,
0
};
static const int ini_start = 1;
static const int ini_first_final = 12;
static const int ini_error = 0;
static const int ini_en_section = 2;
static const int ini_en_keyvalue = 4;
static const int ini_en_comment = 8;
static const int ini_en_discard2eol = 10;
static const int ini_en_main = 1;
/* #line 10 "user/ini_parser.rl" */
// Persistent state
static int8_t cs = -1; //!< Ragel's Current State variable
static uint32_t buff_i = 0; //!< Write pointer for the buffers
static char value_quote = 0; //!< Quote character of the currently collected value
static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character
static IniParserCallback keyCallback = NULL; //!< Currently assigned callback
static void *userdata = NULL; //!< Currently assigned user data for the callback
// Buffers
static char keybuf[INI_KEY_MAX];
static char secbuf[INI_KEY_MAX];
static char valbuf[INI_VALUE_MAX];
// See header for doxygen!
void ICACHE_FLASH_ATTR
ini_parse_reset_partial(void)
{
buff_i = 0;
value_quote = 0;
value_nextesc = false;
}
void ICACHE_FLASH_ATTR
ini_parse_reset(void)
{
ini_parse_reset_partial();
keybuf[0] = secbuf[0] = valbuf[0] = 0;
/* #line 67 "user/ini_parser.c" */
{
cs = ini_start;
}
/* #line 41 "user/ini_parser.rl" */
}
void ICACHE_FLASH_ATTR
ini_parser_error(const char* msg)
{
ini_error("Parser error: %s", msg);
ini_parse_reset_partial();
}
void ICACHE_FLASH_ATTR
ini_parse_begin(IniParserCallback callback, void *userData)
{
keyCallback = callback;
userdata = userData;
ini_parse_reset();
}
void ICACHE_FLASH_ATTR
*ini_parse_end(void)
{
ini_parse("\n", 1);
if (keyCallback) {
keyCallback = NULL;
}
void *ud = userdata;
userdata = NULL;
return ud;
}
void ICACHE_FLASH_ATTR
ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
{
ini_parse_begin(callback, userData);
ini_parse(text, len);
ini_parse_end();
}
static void ICACHE_FLASH_ATTR
rtrim_buf(char *buf, int32_t end)
{
if (end > 0) {
while ((uint8_t)buf[--end] < 33);
end++; // go past the last character
}
buf[end] = 0;
}
void ICACHE_FLASH_ATTR
ini_parse(const char *newstr, size_t len)
{
int32_t i;
char c;
bool isnl;
bool isquot;
// Load new data to Ragel vars
const uint8_t *p;
const uint8_t *eof;
const uint8_t *pe;
if (len == 0) while(newstr[++len] != 0); // alternative to strlen
p = (const uint8_t *) newstr;
eof = NULL;
pe = (const uint8_t *) (newstr + len);
// Init Ragel on the first run
if (cs == -1) {
ini_parse_reset();
}
// The parser
/* #line 152 "user/ini_parser.c" */
{
const char *_acts;
unsigned int _nacts;
if ( p == pe )
goto _test_eof;
if ( cs == 0 )
goto _out;
_resume:
switch ( cs ) {
case 1:
switch( (*p) ) {
case 32u: goto tr1;
case 35u: goto tr3;
case 58u: goto tr0;
case 59u: goto tr3;
case 61u: goto tr0;
case 91u: goto tr4;
}
if ( (*p) < 9u ) {
if ( (*p) <= 8u )
goto tr0;
} else if ( (*p) > 13u ) {
if ( 14u <= (*p) && (*p) <= 31u )
goto tr0;
} else
goto tr1;
goto tr2;
case 0:
goto _out;
case 12:
goto tr0;
case 2:
switch( (*p) ) {
case 9u: goto tr6;
case 32u: goto tr6;
case 93u: goto tr5;
}
if ( (*p) <= 31u )
goto tr5;
goto tr7;
case 3:
if ( (*p) == 93u )
goto tr8;
if ( (*p) > 8u ) {
if ( 10u <= (*p) && (*p) <= 31u )
goto tr5;
} else
goto tr5;
goto tr7;
case 13:
goto tr5;
case 4:
switch( (*p) ) {
case 10u: goto tr10;
case 58u: goto tr11;
case 61u: goto tr11;
}
goto tr9;
case 5:
switch( (*p) ) {
case 9u: goto tr13;
case 10u: goto tr14;
case 13u: goto tr15;
case 32u: goto tr13;
}
goto tr12;
case 6:
switch( (*p) ) {
case 10u: goto tr14;
case 13u: goto tr15;
}
goto tr12;
case 14:
goto tr10;
case 7:
if ( (*p) == 10u )
goto tr14;
goto tr10;
case 8:
switch( (*p) ) {
case 10u: goto tr17;
case 13u: goto tr18;
}
goto tr16;
case 15:
goto tr19;
case 9:
if ( (*p) == 10u )
goto tr17;
goto tr19;
case 10:
switch( (*p) ) {
case 10u: goto tr21;
case 13u: goto tr22;
}
goto tr20;
case 16:
goto tr23;
case 11:
if ( (*p) == 10u )
goto tr21;
goto tr23;
}
tr23: cs = 0; goto _again;
tr0: cs = 0; goto f0;
tr5: cs = 0; goto f4;
tr10: cs = 0; goto f7;
tr19: cs = 0; goto f11;
tr1: cs = 1; goto _again;
tr6: cs = 2; goto _again;
tr7: cs = 3; goto f5;
tr9: cs = 4; goto f8;
tr13: cs = 5; goto _again;
tr11: cs = 5; goto f9;
tr12: cs = 6; goto f10;
tr15: cs = 7; goto _again;
tr16: cs = 8; goto _again;
tr18: cs = 9; goto _again;
tr20: cs = 10; goto _again;
tr22: cs = 11; goto _again;
tr2: cs = 12; goto f1;
tr3: cs = 12; goto f2;
tr4: cs = 12; goto f3;
tr8: cs = 13; goto f6;
tr14: cs = 14; goto f10;
tr17: cs = 15; goto f12;
tr21: cs = 16; goto f13;
f5: _acts = _ini_actions + 1; goto execFuncs;
f6: _acts = _ini_actions + 3; goto execFuncs;
f4: _acts = _ini_actions + 5; goto execFuncs;
f1: _acts = _ini_actions + 7; goto execFuncs;
f8: _acts = _ini_actions + 9; goto execFuncs;
f9: _acts = _ini_actions + 11; goto execFuncs;
f10: _acts = _ini_actions + 13; goto execFuncs;
f7: _acts = _ini_actions + 15; goto execFuncs;
f12: _acts = _ini_actions + 17; goto execFuncs;
f11: _acts = _ini_actions + 19; goto execFuncs;
f13: _acts = _ini_actions + 21; goto execFuncs;
f0: _acts = _ini_actions + 23; goto execFuncs;
f3: _acts = _ini_actions + 25; goto execFuncs;
f2: _acts = _ini_actions + 28; goto execFuncs;
execFuncs:
_nacts = *_acts++;
while ( _nacts-- > 0 ) {
switch ( *_acts++ ) {
case 0:
/* #line 130 "user/ini_parser.rl" */
{
buff_i = 0;
{cs = 2;goto _again;}
}
break;
case 1:
/* #line 135 "user/ini_parser.rl" */
{
if (buff_i >= INI_KEY_MAX) {
ini_parser_error("Section name too long");
{cs = 10;goto _again;}
}
keybuf[buff_i++] = (*p);
}
break;
case 2:
/* #line 143 "user/ini_parser.rl" */
{
// we need a separate buffer for the result, otherwise a failed
// partial parse would corrupt the section string
rtrim_buf(keybuf, buff_i);
for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c;
secbuf[i] = 0;
{cs = 1;goto _again;}
}
break;
case 3:
/* #line 155 "user/ini_parser.rl" */
{
ini_parser_error("Syntax error in [section]");
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
}
break;
case 4:
/* #line 162 "user/ini_parser.rl" */
{
buff_i = 0;
keybuf[buff_i++] = (*p); // add the first char
{cs = 4;goto _again;}
}
break;
case 5:
/* #line 168 "user/ini_parser.rl" */
{
if (buff_i >= INI_KEY_MAX) {
ini_parser_error("Key too long");
{cs = 10;goto _again;}
}
keybuf[buff_i++] = (*p);
}
break;
case 6:
/* #line 176 "user/ini_parser.rl" */
{
rtrim_buf(keybuf, buff_i);
// --- Value begin ---
buff_i = 0;
value_quote = 0;
value_nextesc = false;
}
break;
case 7:
/* #line 185 "user/ini_parser.rl" */
{
isnl = ((*p) == '\r' || (*p) == '\n');
isquot = ((*p) == '\'' || (*p) == '"');
// detect our starting quote
if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) {
value_quote = (*p);
goto valueCharDone;
}
if (buff_i >= INI_VALUE_MAX) {
ini_parser_error("Value too long");
{cs = 10;goto _again;}
}
// end of string - clean up and report
if ((!value_nextesc && (*p) == value_quote) || isnl) {
if (isnl && value_quote) {
ini_parser_error("Unterminated string");
{cs = 1;goto _again;}
}
// unquoted: trim from the end
if (!value_quote) {
rtrim_buf(valbuf, buff_i);
} else {
valbuf[buff_i] = 0;
}
if (keyCallback) {
keyCallback(secbuf, keybuf, valbuf, userdata);
}
// we don't want to discard to eol if the string was terminated by eol
// - would delete the next line
if (isnl) {cs = 1;goto _again;} else {cs = 10;goto _again;}
}
c = (*p);
// escape...
if (value_nextesc) {
if ((*p) == 'n') c = '\n';
else if ((*p) == 'r') c = '\r';
else if ((*p) == 't') c = '\t';
else if ((*p) == 'e') c = '\033';
}
// collecting characters...
if (value_nextesc || (*p) != '\\') { // is quoted, or is not a quoting backslash - literal character
valbuf[buff_i++] = c;
}
value_nextesc = (!value_nextesc && (*p) == '\\');
valueCharDone:;
}
break;
case 8:
/* #line 247 "user/ini_parser.rl" */
{
ini_parser_error("Syntax error in key=value");
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
}
break;
case 9:
/* #line 257 "user/ini_parser.rl" */
{ {cs = 1;goto _again;} }
break;
case 10:
/* #line 258 "user/ini_parser.rl" */
{
ini_parser_error("Syntax error in comment");
if((*p) == '\n') {cs = 1;goto _again;} else {cs = 10;goto _again;}
}
break;
case 11:
/* #line 265 "user/ini_parser.rl" */
{ {cs = 1;goto _again;} }
break;
case 12:
/* #line 273 "user/ini_parser.rl" */
{ {cs = 8;goto _again;} }
break;
case 13:
/* #line 276 "user/ini_parser.rl" */
{
ini_parser_error("Syntax error in root");
{cs = 10;goto _again;}
}
break;
/* #line 458 "user/ini_parser.c" */
}
}
goto _again;
_again:
if ( cs == 0 )
goto _out;
if ( ++p != pe )
goto _resume;
_test_eof: {}
if ( p == eof )
{
const char *__acts = _ini_actions + _ini_eof_actions[cs];
unsigned int __nacts = (unsigned int) *__acts++;
while ( __nacts-- > 0 ) {
switch ( *__acts++ ) {
case 3:
/* #line 155 "user/ini_parser.rl" */
{
ini_parser_error("Syntax error in [section]");
if((*p) == '\n') {cs = 1; if ( p == pe )
goto _test_eof;
goto _again;} else {cs = 10; if ( p == pe )
goto _test_eof;
goto _again;}
}
break;
case 8:
/* #line 247 "user/ini_parser.rl" */
{
ini_parser_error("Syntax error in key=value");
if((*p) == '\n') {cs = 1; if ( p == pe )
goto _test_eof;
goto _again;} else {cs = 10; if ( p == pe )
goto _test_eof;
goto _again;}
}
break;
case 10:
/* #line 258 "user/ini_parser.rl" */
{
ini_parser_error("Syntax error in comment");
if((*p) == '\n') {cs = 1; if ( p == pe )
goto _test_eof;
goto _again;} else {cs = 10; if ( p == pe )
goto _test_eof;
goto _again;}
}
break;
case 13:
/* #line 276 "user/ini_parser.rl" */
{
ini_parser_error("Syntax error in root");
{cs = 10; if ( p == pe )
goto _test_eof;
goto _again;}
}
break;
/* #line 517 "user/ini_parser.c" */
}
}
}
_out: {}
}
/* #line 283 "user/ini_parser.rl" */
}
+65
View File
@@ -0,0 +1,65 @@
#ifndef INIPARSE_STREAM_H
#define INIPARSE_STREAM_H
#include <esp8266.h>
#ifdef DEBUG_INI
#define ini_error(fmt, ...) error("[INI] "#fmt, ##__VA_ARGS__)
#else
#define ini_error(fmt, ...)
#endif
// buffer sizes
#define INI_KEY_MAX 64
#define INI_VALUE_MAX 256
/**
* INI parser callback, called for each found key-value pair.
*
* @param section - current section, empty string for global keys
* @param key - found key (trimmed of whitespace)
* @param value - value, trimmed of quotes or whitespace
* @param userData - opaque user data pointer, general purpose
*/
typedef void (*IniParserCallback)(const char *section, const char *key, const char *value, void *userData);
/**
* Begin parsing a stream
*
* @param callback - key callback to assign
* @param userData - optional user data that willb e passed to the callback
*/
void ini_parse_begin(IniParserCallback callback, void *userData);
/**
* End parse stream.
* Flushes what remains in the buffer and removes callback.
*
* @returns userData or NULL if none
*/
void* ini_parse_end(void);
/**
* Parse a string (needn't be complete line or file)
*
* @param data - string to parse
* @param len - string length (0 = use strlen)
*/
void ini_parse(const char *data, size_t len);
/**
* Parse a complete file loaded to string
*
* @param text - entire file as string
* @param len - file length (0 = use strlen)
* @param callback - key callback
* @param userData - optional user data for key callback
*/
void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData);
/**
* Explicitly reset the parser
*/
void ini_parse_reset(void);
#endif // INIPARSE_STREAM_H
+284
View File
@@ -0,0 +1,284 @@
/* Ragel constants block */
#include "ini_parser.h"
// Ragel setup
%%{
machine ini;
write data;
alphtype unsigned char;
}%%
// Persistent state
static int8_t cs = -1; //!< Ragel's Current State variable
static uint32_t buff_i = 0; //!< Write pointer for the buffers
static char value_quote = 0; //!< Quote character of the currently collected value
static bool value_nextesc = false; //!< Next character is escaped, trated specially, and if quote, as literal quote character
static IniParserCallback keyCallback = NULL; //!< Currently assigned callback
static void *userdata = NULL; //!< Currently assigned user data for the callback
// Buffers
static char keybuf[INI_KEY_MAX];
static char secbuf[INI_KEY_MAX];
static char valbuf[INI_VALUE_MAX];
// See header for doxygen!
void ICACHE_FLASH_ATTR
ini_parse_reset_partial(void)
{
buff_i = 0;
value_quote = 0;
value_nextesc = false;
}
void ICACHE_FLASH_ATTR
ini_parse_reset(void)
{
ini_parse_reset_partial();
keybuf[0] = secbuf[0] = valbuf[0] = 0;
%% write init;
}
void ICACHE_FLASH_ATTR
ini_parser_error(const char* msg)
{
ini_error("Parser error: %s", msg);
ini_parse_reset_partial();
}
void ICACHE_FLASH_ATTR
ini_parse_begin(IniParserCallback callback, void *userData)
{
keyCallback = callback;
userdata = userData;
ini_parse_reset();
}
void ICACHE_FLASH_ATTR
*ini_parse_end(void)
{
ini_parse("\n", 1);
if (keyCallback) {
keyCallback = NULL;
}
void *ud = userdata;
userdata = NULL;
return ud;
}
void ICACHE_FLASH_ATTR
ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
{
ini_parse_begin(callback, userData);
ini_parse(text, len);
ini_parse_end();
}
static void ICACHE_FLASH_ATTR
rtrim_buf(char *buf, int32_t end)
{
if (end > 0) {
while ((uint8_t)buf[--end] < 33);
end++; // go past the last character
}
buf[end] = 0;
}
void ICACHE_FLASH_ATTR
ini_parse(const char *newstr, size_t len)
{
int32_t i;
char c;
bool isnl;
bool isquot;
// Load new data to Ragel vars
const uint8_t *p;
const uint8_t *eof;
const uint8_t *pe;
if (len == 0) while(newstr[++len] != 0); // alternative to strlen
p = (const uint8_t *) newstr;
eof = NULL;
pe = (const uint8_t *) (newstr + len);
// Init Ragel on the first run
if (cs == -1) {
ini_parse_reset();
}
// The parser
%%{
#/ *
ispace = [ \t]; # inline space
wchar = any - 0..8 - 10..31;
#apos = '\'';
#quot = '\"';
nonl = [^\r\n];
nl = '\r'? '\n';
# ---- [SECTION] ----
action sectionStart {
buff_i = 0;
fgoto section;
}
action sectionChar {
if (buff_i >= INI_KEY_MAX) {
ini_parser_error("Section name too long");
fgoto discard2eol;
}
keybuf[buff_i++] = fc;
}
action sectionEnd {
// we need a separate buffer for the result, otherwise a failed
// partial parse would corrupt the section string
rtrim_buf(keybuf, buff_i);
for (i = 0; (c = keybuf[i]) != 0; i++) secbuf[i] = c;
secbuf[i] = 0;
fgoto main;
}
section :=
(
ispace* <: ((wchar - ']')+ @sectionChar) ']' @sectionEnd
) $!{
ini_parser_error("Syntax error in [section]");
if(fc == '\n') fgoto main; else fgoto discard2eol;
};
# ---- KEY=VALUE ----
action keyStart {
buff_i = 0;
keybuf[buff_i++] = fc; // add the first char
fgoto keyvalue;
}
action keyChar {
if (buff_i >= INI_KEY_MAX) {
ini_parser_error("Key too long");
fgoto discard2eol;
}
keybuf[buff_i++] = fc;
}
action keyEnd {
rtrim_buf(keybuf, buff_i);
// --- Value begin ---
buff_i = 0;
value_quote = 0;
value_nextesc = false;
}
action valueChar {
isnl = (fc == '\r' || fc == '\n');
isquot = (fc == '\'' || fc == '"');
// detect our starting quote
if (isquot && !value_nextesc && buff_i == 0 && value_quote == 0) {
value_quote = fc;
goto valueCharDone;
}
if (buff_i >= INI_VALUE_MAX) {
ini_parser_error("Value too long");
fgoto discard2eol;
}
// end of string - clean up and report
if ((!value_nextesc && fc == value_quote) || isnl) {
if (isnl && value_quote) {
ini_parser_error("Unterminated string");
fgoto main;
}
// unquoted: trim from the end
if (!value_quote) {
rtrim_buf(valbuf, buff_i);
} else {
valbuf[buff_i] = 0;
}
if (keyCallback) {
keyCallback(secbuf, keybuf, valbuf, userdata);
}
// we don't want to discard to eol if the string was terminated by eol
// - would delete the next line
if (isnl) fgoto main; else fgoto discard2eol;
}
c = fc;
// escape...
if (value_nextesc) {
if (fc == 'n') c = '\n';
else if (fc == 'r') c = '\r';
else if (fc == 't') c = '\t';
else if (fc == 'e') c = '\033';
}
// collecting characters...
if (value_nextesc || fc != '\\') { // is quoted, or is not a quoting backslash - literal character
valbuf[buff_i++] = c;
}
value_nextesc = (!value_nextesc && fc == '\\');
valueCharDone:;
}
# use * for key, first char is already consumed.
keyvalue :=
(
([^\n=:]* @keyChar %keyEnd)
[=:] ispace* <: nonl* @valueChar nl @valueChar
) $!{
ini_parser_error("Syntax error in key=value");
if(fc == '\n') fgoto main; else fgoto discard2eol;
};
# ---- COMMENT ----
comment :=
(
nonl* nl
@{ fgoto main; }
) $!{
ini_parser_error("Syntax error in comment");
if(fc == '\n') fgoto main; else fgoto discard2eol;
};
# ---- CLEANUP ----
discard2eol := nonl* nl @{ fgoto main; };
# ---- ROOT ----
main :=
(space*
(
'[' @sectionStart |
[#;] @{ fgoto comment; } |
(wchar - [\t =:]) @keyStart
)
) $!{
ini_parser_error("Syntax error in root");
fgoto discard2eol;
};
write exec;
#*/
}%%
}
+25 -7
View File
@@ -97,19 +97,27 @@ compute_checksum(AppConfigBundle *bundle)
return calculateCRC32((uint8_t *) bundle, sizeof(AppConfigBundle) - 4);
}
static void ICACHE_FLASH_ATTR
set_admin_block_defaults(void)
{
persist_info("[Persist] Initing admin config block");
strcpy(persist.admin.pw, DEFAULT_ADMIN_PW);
persist.admin.version = ADMINCONF_VERSION;
}
/**
* Load, verify and apply persistent config
*/
void ICACHE_FLASH_ATTR
persist_load(void)
{
persist_info("[Persist] Loading stored settings from FLASH...");
persist_info("[Persist] Loading settings from FLASH...");
persist_dbg("AppConfigBundle memory map:");
persist_dbg("> WiFiConfigBundle at %4d (error %2d)", wconf_at, wconf_at - 0);
persist_dbg("> SystemConfigBundle at %4d (error %2d)", sconf_at, sconf_at - WIFICONF_SIZE);
persist_dbg("> TerminalConfigBundle at %4d (error %2d)", tconf_at, tconf_at - WIFICONF_SIZE - SYSCONF_SIZE);
persist_dbg("> Checksum at %4d (error %2d)", cksum_at, cksum_at - (APPCONF_SIZE - 4));
persist_dbg("Persist memory map:");
persist_dbg("> wifi at %4d (error %2d)", wconf_at, wconf_at - 0);
persist_dbg("> sys at %4d (error %2d)", sconf_at, sconf_at - WIFICONF_SIZE);
persist_dbg("> term at %4d (error %2d)", tconf_at, tconf_at - WIFICONF_SIZE - SYSCONF_SIZE);
persist_dbg("> cksum at %4d (error %2d)", cksum_at, cksum_at - (APPCONF_SIZE - 4));
persist_dbg("> Total size = %d bytes (error %d)", sizeof(AppConfigBundle), APPCONF_SIZE - sizeof(AppConfigBundle));
bool hard_reset = false;
@@ -120,7 +128,8 @@ persist_load(void)
// Verify checksums
if (hard_reset ||
(compute_checksum(&persist.defaults) != persist.defaults.checksum) ||
(compute_checksum(&persist.current) != persist.current.checksum)) {
(compute_checksum(&persist.current) != persist.current.checksum) ||
(persist.admin.version != 0 && (calculateCRC32((uint8_t *) &persist.admin, sizeof(AdminConfigBlock) - 4) != persist.admin.checksum))) {
error("[Persist] Checksum verification: FAILED");
hard_reset = true;
} else {
@@ -135,10 +144,18 @@ persist_load(void)
// write them also as defaults
memcpy(&persist.defaults, &persist.current, sizeof(AppConfigBundle));
// reset admin pw
set_admin_block_defaults();
persist_store();
// this also stores them to flash and applies to modules
} else {
if (persist.admin.version == 0) {
set_admin_block_defaults();
persist_store();
}
apply_live_settings();
}
@@ -153,6 +170,7 @@ persist_store(void)
// Update checksums before write
persist.current.checksum = compute_checksum(&persist.current);
persist.defaults.checksum = compute_checksum(&persist.defaults);
persist.admin.checksum = calculateCRC32((uint8_t *) &persist.admin, sizeof(AdminConfigBlock) - 4);
if (!system_param_save_with_protect(PERSIST_SECTOR_ID, &persist, sizeof(PersistBlock))) {
error("[Persist] Store to flash failed!");
+16 -3
View File
@@ -14,11 +14,13 @@
#include "screen.h"
#include "syscfg.h"
#define DEFAULT_ADMIN_PW "adminpw"
// Changing this could be used to force-erase the config area
// after a firmware upgrade
#define CHECKSUM_SALT 3
#define CHECKSUM_SALT 6
#define APPCONF_SIZE 2048
#define APPCONF_SIZE 1900
/** Struct for current or default settings */
typedef struct { // the entire block should be 1024 bytes long (for compatibility across upgrades)
@@ -41,7 +43,7 @@ typedef struct { // the entire block should be 1024 bytes long (for compatibilit
// it grew to a different memory area.
uint8_t _filler_end[
APPCONF_SIZE
- sizeof(uint32_t) // checksum
- 4 // checksum
- WIFICONF_SIZE
- SYSCONF_SIZE
- TERMCONF_SIZE
@@ -50,10 +52,21 @@ typedef struct { // the entire block should be 1024 bytes long (for compatibilit
uint32_t checksum; // computed before write and tested on load. If it doesn't match, values are reset to hard defaults.
} AppConfigBundle;
#define ADMINCONF_VERSION 1
#define ADMINCONF_SIZE 256
typedef struct {
u8 version;
char pw[64];
uint8_t _filler[ADMINCONF_SIZE-64-4];
uint32_t checksum;
} AdminConfigBlock;
/** This is the entire data block stored in FLASH */
typedef struct {
AppConfigBundle defaults; // defaults are stored here
AppConfigBundle current; // active settings adjusted by the user
AdminConfigBlock admin;
} PersistBlock;
// Persist holds the data currently loaded from the flash
+85 -25
View File
@@ -12,12 +12,84 @@
#include "cgi_network.h"
#include "cgi_term_cfg.h"
#include "cgi_persist.h"
#include "syscfg.h"
#include "persist.h"
#include "api.h"
#include "cgi_d2d.h"
#define WIFI_PROTECT 0
#define WIFI_AUTH_NAME "wifi"
#define WIFI_AUTH_PASS "nicitel"
/**
* Password for WiFi config
*/
static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen)
{
if (no == 0) {
os_strcpy(user, (const char *) sysconf->access_name);
os_strcpy(pass, (const char *) sysconf->access_pw);
return 1;
}
if (no == 1) {
os_strcpy(user, "admin");
os_strcpy(pass, persist.admin.pw);
return 1;
}
return 0;
}
static int wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen);
httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData)
{
bool protect = false;
http_dbg("Route, %s, pwlock=%d", connData->url, sysconf->pwlock);
switch (sysconf->pwlock) {
case PWLOCK_ALL:
protect = true;
break;
case PWLOCK_SETTINGS_NOTERM:
protect = strstarts(connData->url, "/cfg") &&
!strstarts(connData->url, "/cfg/term");
break;
case PWLOCK_SETTINGS_ALL:
protect = strstarts(connData->url, "/cfg");
break;
case PWLOCK_MENUS:
protect = strstarts(connData->url, "/cfg") ||
strstarts(connData->url, "/about") ||
strstarts(connData->url, "/help");
break;
default:
case PWLOCK_NONE:
break;
}
// pages outside the normal scope
if (sysconf->pwlock > PWLOCK_NONE) {
if (strstarts(connData->url, "/api/v1/reboot")) protect = true;
}
if (sysconf->pwlock > PWLOCK_SETTINGS_NOTERM) {
if (strstarts(connData->url, "/api/v1/clear")) protect = true;
}
if (sysconf->access_pw[0] == 0) {
http_dbg("Access PW is nil, no protection.");
protect = false;
}
if (protect) {
http_dbg("Page is protected!");
connData->cgiArg = wifiPassFn;
return authBasic(connData);
} else {
http_dbg("Not protected");
return HTTPD_CGI_NOTFOUND;
}
}
/**
* Application routes
@@ -25,6 +97,7 @@ static int wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen,
const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
// redirect func for the captive portal
ROUTE_CGI_ARG("*", cgiRedirectApClientToHostname, "esp-terminal.ap"),
ROUTE_CGI("*", cgiOptionalPwLock),
// --- Web pages ---
ROUTE_TPL_FILE("/", tplScreen, "/term.tpl"),
@@ -35,14 +108,13 @@ 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),
// --- WiFi config --- (TODO make this conditional and configurable)
#if WIFI_PROTECT
ROUTE_AUTH("/wifi*", wifiPassFn),
#endif
// 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"),
ROUTE_TPL_FILE("/cfg/wifi/?", tplWlan, "/cfg_wifi.tpl"),
@@ -59,6 +131,8 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
ROUTE_TPL_FILE("/cfg/system/?", tplSystemCfg, "/cfg_system.tpl"),
ROUTE_CGI("/cfg/system/set", cgiSystemCfgSetParams),
ROUTE_CGI("/cfg/system/export", cgiPersistExport),
ROUTE_CGI("/cfg/system/import", cgiPersistImport),
ROUTE_CGI("/cfg/system/write_defaults", cgiPersistWriteDefaults),
ROUTE_CGI("/cfg/system/restore_defaults", cgiPersistRestoreDefaults),
ROUTE_CGI("/cfg/system/restore_hard", cgiPersistRestoreHard),
@@ -67,17 +141,3 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = {
ROUTE_END(),
};
// --- Wifi password protection ---
/**
* Password for WiFi config
*/
static int ICACHE_FLASH_ATTR wifiPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen)
{
if (no == 0) {
os_strcpy(user, WIFI_AUTH_NAME);
os_strcpy(pass, WIFI_AUTH_PASS);
return 1;
}
return 0;
}
-3
View File
@@ -6,7 +6,4 @@
extern const HttpdBuiltInUrl routes[];
/** Broadcast screen state to sockets */
void screen_notifyChange();
#endif //ROUTES_H
+1273 -373
View File
File diff suppressed because it is too large Load Diff
+139 -49
View File
@@ -5,6 +5,7 @@
#include <stdbool.h>
#include <esp8266.h>
#include <httpd.h>
#include "config_xmacros.h"
/**
* This module handles the virtual screen and operations on it.
@@ -34,15 +35,12 @@
*
*/
// Size designed for the terminal config structure
// Must be constant to avoid corrupting user config after upgrade
#define TERMCONF_SIZE 300
#define TERMCONF_VERSION 5
#define TERM_BTN_LEN 10
#define TERM_BTN_MSG_LEN 10
#define TERM_TITLE_LEN 64
#define TERM_BTN_COUNT 5
#define TERM_BACKDROP_LEN 100
#define TERM_FONTSTACK_LEN 100
#define SCR_DEF_DISPLAY_TOUT_MS 12
#define SCR_DEF_DISPLAY_COOLDOWN_MS 35
@@ -65,35 +63,85 @@ enum CursorShape {
CURSOR_BAR = 6,
};
#define SCR_DEF_SHOW_BUTTONS 0
#define SCR_DEF_SHOW_BUTTONS 1
#define SCR_DEF_SHOW_MENU 1
#define SCR_DEF_CURSOR_SHAPE CURSOR_BLOCK_BL
#define SCR_DEF_CRLF 0
#define SCR_DEF_ALLFN 0
#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
#define SCR_DEF_BUTTON_COUNT 5
// --- 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 500
#define TERMCONF_VERSION 6
//....Type................Name..Suffix...............Deref..XGET.........Cast..XSET...........NOTIFY..Allow
// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays)
#define XTABLE_TERMCONF \
X(u32, width, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
X(u32, height, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
X(u32, default_bg, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
X(u32, default_fg, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
X(char, title, [TERM_TITLE_LEN], /**/, xget_string, xset_string, TERM_TITLE_LEN, /**/, 1) \
X(char, btn1, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
X(char, btn2, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
X(char, btn3, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
X(char, btn4, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
X(char, btn5, [TERM_BTN_LEN], /**/, xget_string, xset_string, TERM_BTN_LEN, /**/, 1) \
X(u8, theme, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \
X(u32, parser_tout_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
X(u32, display_tout_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
X(bool, fn_alt_mode, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \
X(u32, display_cooldown_ms, /**/, /**/, xget_dec, xset_u32, NULL, /**/, 1) \
X(bool, loopback, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(bool, show_buttons, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(bool, show_config_links, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(char, bm1, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
X(char, bm2, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
X(char, bm3, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
X(char, bm4, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
X(char, bm5, [TERM_BTN_MSG_LEN], /**/, xget_term_bm, xset_term_bm, NULL, /**/, 1) \
X(u32, cursor_shape, /**/, /**/, xget_dec, xset_term_cursorshape, NULL, /**/, 1) \
X(bool, crlf_mode, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(bool, want_all_fn, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(bool, debugbar, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(bool, allow_decopt_12, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(bool, ascii_debug, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
X(char, backdrop, [TERM_BACKDROP_LEN], /**/, xget_string, xset_string, TERM_BACKDROP_LEN, /**/, 1) \
X(u8, button_count, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \
X(u32, bc1, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
X(u32, bc2, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
X(u32, bc3, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
X(u32, bc4, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
X(u32, bc5, /**/, /**/, xget_term_color, xset_term_color, NULL, /**/, 1) \
X(char, font_stack, [TERM_FONTSTACK_LEN], /**/, xget_string, xset_string, TERM_FONTSTACK_LEN, /**/, 1) \
X(u8, font_size, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1)
#define TERM_BM_N(tc, n) ((tc)->bm1+(TERM_BTN_MSG_LEN*n))
#define TERM_BTN_N(tc, n) ((tc)->btn1+(TERM_BTN_LEN*n))
/** Export color for config */
void xget_term_color(char *buff, u32 value);
/** Export button message as stirng for config */
void xget_term_bm(char *buff, char *value);
/** Set button message */
enum xset_result xset_term_bm(const char *name, char *field, const char *buff, const void *arg);
/** Set color */
enum xset_result xset_term_color(const char *name, u32 *field, const char *buff, const void *arg);
/** Set cursor shape */
enum xset_result xset_term_cursorshape(const char *name, u32 *field, const char *buff, const void *arg);
typedef struct {
u32 width;
u32 height;
u8 default_bg; // should be the Color typedef, but this way the size is more explicit
u8 default_fg;
char title[TERM_TITLE_LEN];
char btn[TERM_BTN_COUNT][TERM_BTN_LEN];
u8 theme;
u32 parser_tout_ms;
u32 display_tout_ms;
bool fn_alt_mode; // xterm compatibility mode (alternate codes for some FN keys)
u8 config_version;
u32 display_cooldown_ms;
bool loopback;
bool show_buttons;
bool show_config_links;
char btn_msg[TERM_BTN_COUNT][TERM_BTN_MSG_LEN];
enum CursorShape cursor_shape;
bool crlf_mode;
bool want_all_fn;
#define X XSTRUCT_FIELD
XTABLE_TERMCONF
#undef X
} TerminalConfigBundle;
// Live config
@@ -142,6 +190,11 @@ void screen_resize(int rows, int cols);
void screen_set_title(const char *title);
/** Set a button text */
void screen_set_button_text(int num, const char *text);
void screen_set_button_message(int num, const char *msg);
void screen_set_button_color(int num, const char *buf);
void screen_set_button_count(int count);
/** Change backdrop */
void screen_set_backdrop(const char *url);
// --- Encoding ---
@@ -150,11 +203,39 @@ typedef enum {
CS_A_UKASCII = 'A',
CS_0_DEC_SUPPLEMENTAL = '0',
CS_1_DOS_437 = '1',
CS_2_BLOCKS_LINES = '2',
CS_3_LINES_EXTRA = '3',
} 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_CHANGE_BACKDROP = (1<<8),
TOPIC_CHANGE_STATIC_OPTS = (1<<9),
TOPIC_DOUBLE_LINES = (1<<10),
TOPIC_FLAG_NOCLEAN = (1<<15), // do not clean dirty extents
void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len);
// combos
TOPIC_INITIAL =
TOPIC_CHANGE_SCREEN_OPTS |
TOPIC_CHANGE_STATIC_OPTS |
TOPIC_CHANGE_CONTENT_ALL |
TOPIC_CHANGE_CURSOR |
TOPIC_CHANGE_TITLE |
TOPIC_CHANGE_BACKDROP |
TOPIC_CHANGE_BUTTONS |
TOPIC_DOUBLE_LINES,
};
typedef u16 ScreenNotifyTopics;
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, ScreenNotifyTopics topics, void **data);
// --- Clearing ---
@@ -223,30 +304,40 @@ 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);
// --- Graphic rendition setting ---
typedef uint8_t Color; // 0-16
typedef uint8_t Color;
typedef uint16_t CellAttrs;
#define ATTR_BOLD (1<<0)
#define ATTR_FAINT (1<<1)
#define ATTR_ITALIC (1<<2)
#define ATTR_UNDERLINE (1<<3)
#define ATTR_BLINK (1<<4)
#define ATTR_FRAKTUR (1<<5)
#define ATTR_STRIKE (1<<6)
#define ATTR_OVERLINE (1<<7)
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);
/** Set cursor background coloor */
void screen_set_bg(Color color);
/** Set cursor foreground color to default */
void screen_set_default_fg(void);
/** Set cursor background color to default */
void screen_set_default_bg(void);
/** Enable/disable attrs by bitmask */
void screen_set_sgr(u8 attrs, bool ena);
/** Set the inverse attribute */
void screen_set_sgr_inverse(bool ena);
void screen_set_sgr(CellAttrs attrs, bool ena);
/** Conceal style */
void screen_set_sgr_conceal(bool ena);
/** Reset cursor attribs */
@@ -291,6 +382,9 @@ void screen_tab_reverse(int count);
/** Move left, shift right if at the boundary */
void screen_back_index(int count);
/** Set line attribs; 0-no change, 1,2 - single,double */
void screen_set_line_attr(uint8_t double_w, uint8_t double_h_top, uint8_t double_h_bot);
// --- Printing characters ---
/**
@@ -316,18 +410,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
+13 -10
View File
@@ -4,8 +4,7 @@
#include "ansi_parser.h"
#include "syscfg.h"
#define LOGBUF_SIZE 512
static char logbuf[LOGBUF_SIZE];
static char logbuf[DEBUG_LOGBUF_SIZE];
static u32 lb_nw = 1;
static u32 lb_ls = 0;
static ETSTimer flushLogTimer;
@@ -14,7 +13,7 @@ static void buf_putc(char c)
{
if (lb_ls != lb_nw) {
logbuf[lb_nw++] = c;
if (lb_nw >= LOGBUF_SIZE) lb_nw = 0;
if (lb_nw >= DEBUG_LOGBUF_SIZE) lb_nw = 0;
}
}
@@ -25,11 +24,11 @@ buf_pop(void *unused)
u32 old_ls;
while (quantity > 0) {
// stop when done
if ((lb_ls == lb_nw-1) || (lb_ls == LOGBUF_SIZE-1 && lb_nw == 0)) break;
if ((lb_ls == lb_nw-1) || (lb_ls == DEBUG_LOGBUF_SIZE-1 && lb_nw == 0)) break;
old_ls = lb_ls;
lb_ls++;
if (lb_ls >= LOGBUF_SIZE) lb_ls = 0;
if (lb_ls >= DEBUG_LOGBUF_SIZE) lb_ls = 0;
if (OK == UART_WriteCharCRLF(UART1, logbuf[lb_ls], 5)) {
quantity--;
@@ -41,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, 200);
}
/**
* Init the serial ports
@@ -57,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
@@ -93,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
}
+121 -1
View File
@@ -6,15 +6,129 @@
#include "persist.h"
#include "uart_driver.h"
#include "serial.h"
#include "cgi_logging.h"
SystemConfigBundle * const sysconf = &persist.current.sysconf;
enum xset_result ICACHE_FLASH_ATTR
xset_sys_baudrate(const char *name, u32 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
int baud = atoi(buff);
if (baud == BIT_RATE_300 ||
baud == BIT_RATE_600 ||
baud == BIT_RATE_1200 ||
baud == BIT_RATE_2400 ||
baud == BIT_RATE_4800 ||
baud == BIT_RATE_9600 ||
baud == BIT_RATE_19200 ||
baud == BIT_RATE_38400 ||
baud == BIT_RATE_57600 ||
baud == BIT_RATE_74880 ||
baud == BIT_RATE_115200 ||
baud == BIT_RATE_230400 ||
baud == BIT_RATE_460800 ||
baud == BIT_RATE_921600 ||
baud == BIT_RATE_1843200 ||
baud == BIT_RATE_3686400) {
if (*field != baud) {
*field = (u32) baud;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad baud rate %s", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_sys_parity(const char *name, u8 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
int parity = atoi(buff);
if (parity >= 0 && parity <= 2) {
if (*field != parity) {
*field = (UartParityMode) parity;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad parity %s", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_sys_stopbits(const char *name, u8 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
int stopbits = atoi(buff);
if (stopbits >= 1 && stopbits <= 3) {
if (*field != stopbits) {
*field = (UartParityMode) stopbits;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad stopbits %s", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
int pwlock = atoi(buff);
if (pwlock >= 0 && pwlock < PWLOCK_MAX) {
if (*field != pwlock) {
*field = (enum pwlock) pwlock;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad pwlock %s", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_sys_accesspw(const char *name, uchar *field, const char *buff, const void *arg)
{
// Do not overwrite pw if empty
if (strlen(buff) == 0) return XSET_UNCHANGED;
return xset_ustring(name, field, buff, arg);
}
void ICACHE_FLASH_ATTR
sysconf_apply_settings(void)
{
// !!! Update to current version !!!
// char buff[64];
//#define XSTRUCT sysconf
//#define X XDUMP_FIELD
// XTABLE_SYSCONF
//#undef X
bool changed = false;
if (sysconf->config_version < 1) {
dbg("Upgrading syscfg to v 1");
sysconf->overclock = false;
changed = true;
}
sysconf->config_version = SYSCONF_VERSION;
if (changed) {
persist_store();
}
// uart settings live here, but the CGI handler + form has been moved to the Terminal config page
serialInit();
system_update_cpu_freq((uint8) (sysconf->overclock ? 160 : 80));
}
void ICACHE_FLASH_ATTR
@@ -23,4 +137,10 @@ sysconf_restore_defaults(void)
sysconf->uart_parity = PARITY_NONE;
sysconf->uart_baudrate = BIT_RATE_115200;
sysconf->uart_stopbits = ONE_STOP_BIT;
sysconf->config_version = SYSCONF_VERSION;
sysconf->access_pw[0] = 0;
sysconf->pwlock = PWLOCK_NONE;
strcpy((char *)sysconf->access_pw, DEF_ACCESS_PW);
strcpy((char *)sysconf->access_name, DEF_ACCESS_NAME);
sysconf->overclock = false;
}
+49 -6
View File
@@ -6,17 +6,54 @@
#define ESP_VT100_FIRMWARE_SYSCFG_H
#include <esp8266.h>
#include "config_xmacros.h"
// Size designed for the wifi config structure
// Must be constant to avoid corrupting user config after upgrade
#define SYSCONF_SIZE 200
#define SYSCONF_VERSION 0
#define SYSCONF_SIZE 300
#define SYSCONF_VERSION 1
#define DEF_ACCESS_PW "1234"
#define DEF_ACCESS_NAME "espterm"
enum pwlock {
PWLOCK_NONE = 0,
PWLOCK_SETTINGS_NOTERM = 1,
PWLOCK_SETTINGS_ALL = 2,
PWLOCK_MENUS = 3,
PWLOCK_ALL = 4,
PWLOCK_MAX = 5,
};
//....Type................Name..Suffix...............Deref..XGET............XSET.........................NOTIFY....Allow
// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays)
#define XTABLE_SYSCONF \
X(u32, uart_baudrate, /**/, /**/, xget_dec, xset_sys_baudrate, NULL, uart_changed=true, 1) \
X(u8, uart_parity, /**/, /**/, xget_dec, xset_sys_parity, NULL, uart_changed=true, 1) \
X(u8, uart_stopbits, /**/, /**/, xget_dec, xset_sys_stopbits, NULL, uart_changed=true, 1) \
\
X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1) \
\
X(u8, pwlock, /**/, /**/, xget_dec, xset_sys_pwlock, NULL, /**/, admin|tpl) \
X(uchar, access_pw, [64], /**/, xget_ustring, xset_sys_accesspw, NULL, /**/, admin) \
X(uchar, access_name, [32], /**/, xget_ustring, xset_ustring, NULL, /**/, admin|tpl) \
\
X(bool, overclock, /**/, /**/, xget_bool, xset_bool, NULL, /**/, 1) \
typedef struct {
u32 uart_baudrate;
u8 uart_parity;
u8 uart_stopbits;
u8 config_version;
#define X XSTRUCT_FIELD
XTABLE_SYSCONF
#undef X
// u32 uart_baudrate;
// u8 uart_parity;
// u8 uart_stopbits;
// u8 config_version;
// enum pwlock pwlock : 8; // page access lock
// char access_pw[64]; // access password
// char access_name[32]; // access name
// bool overclock;
} SystemConfigBundle;
extern SystemConfigBundle * const sysconf;
@@ -25,4 +62,10 @@ void sysconf_apply_settings(void);
void sysconf_restore_defaults(void);
enum xset_result xset_sys_baudrate(const char *name, u32 *field, const char *buff, const void *arg);
enum xset_result xset_sys_parity(const char *name, u8 *field, const char *buff, const void *arg);
enum xset_result xset_sys_stopbits(const char *name, u8 *field, const char *buff, const void *arg);
enum xset_result xset_sys_pwlock(const char *name, u8 *field, const char *buff, const void *arg);
enum xset_result xset_sys_accesspw(const char *name, uchar *field, const char *buff, const void *arg);
#endif //ESP_VT100_FIRMWARE_SYSCFG_H
+64 -47
View File
@@ -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 512 //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,24 +175,33 @@ 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) {
UART_WriteChar(UART1, '%', 100);
}
else {
fifo_len = (uint8) (pRxBuffer->Space - 1);
UART_WriteChar(UART1, '#', 10);
// discard contents of the FIFO - would loop forever
buf_idx = 0;
while (buf_idx < fifo_len) {
buf_idx++;
fifo_data = (uint8) (READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
*(pRxBuffer->pInPos++) = fifo_data;
if (pRxBuffer->pInPos == (pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize)) {
pRxBuffer->pInPos = pRxBuffer->pUartBuff;
}
(void)fifo_data; // pretend we use it
}
pRxBuffer->Space -= fifo_len;
return;
}
if (pRxBuffer->Space >= UART_FIFO_LEN) {
uart_rx_intr_enable(UART0);
buf_idx = 0;
while (buf_idx < fifo_len) {
buf_idx++;
fifo_data = (uint8) (READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
*(pRxBuffer->pInPos++) = fifo_data;
if (pRxBuffer->pInPos == (pRxBuffer->pUartBuff + pRxBuffer->UartBuffSize)) {
pRxBuffer->pInPos = pRxBuffer->pUartBuff;
}
}
pRxBuffer->Space -= fifo_len;
//
// this is called by the processing routine, no need here
// if (pRxBuffer->Space >= UART_FIFO_LEN) {
// uart_rx_intr_enable(UART0);
// }
}
u16 ICACHE_FLASH_ATTR UART_AsyncTxGetEmptySpace(void)
@@ -183,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, '^', 100);
}
// }
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);
@@ -247,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);
@@ -258,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;
@@ -269,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");
// }
}
+5 -1
View File
@@ -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);
+5 -4
View File
@@ -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 4
#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
+40 -17
View File
@@ -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;
}
@@ -79,7 +89,6 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg)
// Deferred init
static void user_start(void *unused);
static void user_start2(void *unused);
static ETSTimer userStartTimer;
static ETSTimer prHeapTimer;
@@ -87,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
@@ -94,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__);
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();
@@ -112,16 +131,10 @@ 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);
}
static void ICACHE_FLASH_ATTR user_start(void *unused)
{
// Load and apply stored settings, or defaults if stored settings are invalid
@@ -129,10 +142,20 @@ 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
dbg("tsize=%d", sizeof(TerminalConfigBundle));
#if DEBUG_HEAP
// Heap use timer & blink
TIMER_START(&prHeapTimer, prHeapTimerCb, HEAP_TIMER_MS, 1);
#endif
}
// ---- unused funcs removed from sdk to save space ---
+35 -5
View File
@@ -20,7 +20,8 @@ static UnicodeCacheSlot cache[UNICODE_CACHE_SIZE];
* @return
*/
void ICACHE_FLASH_ATTR
unicode_cache_clear(void) {
unicode_cache_clear(void)
{
utfc_dbg("utf8 cache clear!");
for (int slot = 0; slot < UNICODE_CACHE_SIZE; slot++) {
cache[slot].count=0;
@@ -35,7 +36,8 @@ unicode_cache_clear(void) {
* @return the obtained look-up reference
*/
UnicodeCacheRef ICACHE_FLASH_ATTR
unicode_cache_add(const u8 *bytes) {
unicode_cache_add(const u8 *bytes)
{
if (bytes[0] < 32) {
utfc_warn("utf8 cache bad char '%c'", bytes[0]);
return '?';
@@ -69,6 +71,27 @@ unicode_cache_add(const u8 *bytes) {
return ID_TO_REF(slot);
}
/**
* Increment a reference
*
* @param ref - reference
* @return success
*/
bool ICACHE_FLASH_ATTR
unicode_cache_inc(UnicodeCacheRef ref)
{
if (!IS_UNICODE_CACHE_REF(ref)) return true; // ASCII
int slot = REF_TO_ID(ref);
if (cache[slot].count == 0) {
utfc_warn("utf8 cache inc-after-free ref @ %d", ref);
return false;
}
cache[slot].count++;
utfc_dbg("utf8 cache inc '%.4s' @ %d, %d uses", cache[slot].bytes, slot, cache[slot].count);
return true;
}
/**
* Look up a code point in the cache by reference. Do not change the use counter.
*
@@ -77,7 +100,8 @@ unicode_cache_add(const u8 *bytes) {
* @return true if the look-up succeeded
*/
bool ICACHE_FLASH_ATTR
unicode_cache_retrieve(UnicodeCacheRef ref, u8 *target) {
unicode_cache_retrieve(UnicodeCacheRef ref, u8 *target)
{
if (!IS_UNICODE_CACHE_REF(ref)) {
// ASCII, bypass
target[0] = ref;
@@ -107,7 +131,8 @@ unicode_cache_retrieve(UnicodeCacheRef ref, u8 *target) {
* @return true if the code point was found in the cache
*/
bool ICACHE_FLASH_ATTR
unicode_cache_remove(UnicodeCacheRef ref) {
unicode_cache_remove(UnicodeCacheRef ref)
{
if (!IS_UNICODE_CACHE_REF(ref)) return true; // ASCII, bypass
u8 slot = REF_TO_ID(ref);
@@ -135,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;
+11 -2
View File
@@ -8,7 +8,7 @@
#include <c_types.h>
// 160 is maximum possible
#define UNICODE_CACHE_SIZE 100
#define UNICODE_CACHE_SIZE 160
typedef u8 UnicodeCacheRef;
#define IS_UNICODE_CACHE_REF(c) ((c) < 32 || (c) >= 127)
@@ -28,6 +28,14 @@ void unicode_cache_clear(void);
*/
UnicodeCacheRef unicode_cache_add(const u8 *bytes);
/**
* Increment a reference
*
* @param ref - reference
* @return success
*/
bool unicode_cache_inc(UnicodeCacheRef ref);
/**
* Look up a code point in the cache by reference. Do not change the use counter.
*
@@ -55,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
+17 -5
View File
@@ -5,13 +5,25 @@
#ifndef ESP_VT100_FIRMWARE_VERSION_H
#define ESP_VT100_FIRMWARE_VERSION_H
#define FW_V_MAJOR 1
#define FW_V_MINOR 1
#define FW_V_PATCH 1
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#define FW_V_MAJOR 2
#define FW_V_MINOR 3
#define FW_V_PATCH 0
#define FW_V_SUFFIX ""
#define FW_CODENAME "Cricket" // 2.3
#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
+191 -22
View File
@@ -4,10 +4,143 @@
#include "wifimgr.h"
#include "persist.h"
#include "cgi_logging.h"
#include "config_xmacros.h"
WiFiConfigBundle * const wificonf = &persist.current.wificonf;
WiFiConfChangeFlags wifi_change_flags;
enum xset_result ICACHE_FLASH_ATTR
xset_wifi_lease_time(const char *name, u16 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s min", name, buff);
int min = atoi(buff);
if (min >= 1 && min <= 2880) {
if (*field != min) {
*field = (u16) min;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Lease time %s out of allowed range 1-2880.", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_wifi_opmode(const char *name, u8 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
int mode = atoi(buff);
if (mode > NULL_MODE && mode < MAX_MODE) {
if (*field != mode) {
*field = (WIFI_MODE) mode;
return XSET_SET;
}
return XSET_UNCHANGED; // opmode does not use flags
} else {
cgi_warn("Bad opmode value \"%s\"", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_wifi_tpw(const char *name, u8 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
int tpw = atoi(buff);
if (tpw >= 0 && tpw <= 82) { // 0 actually isn't 0 but quite low. 82 is very strong
if (*field != tpw) {
*field = (u8) tpw;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("tpw %s out of allowed range 0-82.", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
int channel = atoi(buff);
if (channel > 0 && channel < 15) {
if (*field != channel) {
*field = (u8) channel;
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad channel value \"%s\", allowed 1-14", buff);
return XSET_FAIL;
}
}
enum xset_result ICACHE_FLASH_ATTR
xset_wifi_ssid(const char *name, uchar *field, const char *buff, const void *arg)
{
u8 buff2[SSID_LEN];
bool want_subs = arg!=0;
int i;
for (i = 0; i < SSID_LEN; i++) {
char c = buff[i];
if (c == 0) break;
if (want_subs && (c < 32 || c >= 127)) c = '_';
buff2[i] = (u8) c;
}
buff2[i] = 0;
cgi_dbg("Setting %s = %s", name, buff);
if (strlen((char *)buff2) > 0) {
if (!streq(field, buff2)) {
strncpy_safe(field, buff2, SSID_LEN);
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad SSID len.");
return XSET_FAIL;
}
}
/** Set PW - allow len 0 or 8-64 */
enum xset_result ICACHE_FLASH_ATTR
xset_wifi_pwd(const char *name, uchar *field, const char *buff, const void *arg)
{
cgi_dbg("Setting %s = %s", name, buff);
if (strlen(buff) == 0 || (strlen(buff) >= 8 && strlen(buff) < PASSWORD_LEN-1)) {
if (!streq(field, buff)) {
strncpy_safe(field, buff, PASSWORD_LEN);
return XSET_SET;
}
return XSET_UNCHANGED;
} else {
cgi_warn("Bad password len.");
return XSET_FAIL;
}
}
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,
@@ -26,13 +159,11 @@ wifimgr_restore_defaults(void)
wificonf->ap_password[0] = 0; // PSK2 always if password is not null.
wificonf->ap_hidden = false;
IP4_ADDR(&wificonf->ap_addr.ip, 192, 168, 4, 1);
IP4_ADDR(&wificonf->ap_addr.netmask, 255, 255, 255, 0);
wificonf->ap_addr.gw.addr = wificonf->ap_addr.gw.addr;
IP4_ADDR(&wificonf->ap_addr_ip, 192, 168, 4, 1);
IP4_ADDR(&wificonf->ap_addr_mask, 255, 255, 255, 0);
IP4_ADDR(&wificonf->ap_dhcp_range.start_ip, 192, 168, 4, 100);
IP4_ADDR(&wificonf->ap_dhcp_range.end_ip, 192, 168, 4, 200);
wificonf->ap_dhcp_range.enable = 1; // this will never get changed, idk why it's even there
IP4_ADDR(&wificonf->ap_dhcp_start, 192, 168, 4, 100);
IP4_ADDR(&wificonf->ap_dhcp_end, 192, 168, 4, 200);
wificonf->ap_dhcp_time = 120;
// --- Client config ---
@@ -40,9 +171,9 @@ wifimgr_restore_defaults(void)
wificonf->sta_password[0] = 0;
wificonf->sta_dhcp_enable = true;
IP4_ADDR(&wificonf->sta_addr.ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw"
IP4_ADDR(&wificonf->sta_addr.netmask, 255, 255, 255, 0);
IP4_ADDR(&wificonf->sta_addr.gw, 192, 168, 0, 1);
IP4_ADDR(&wificonf->sta_addr_ip, 192, 168, 0, (mac[5] == 1 ? 2 : mac[5])); // avoid being the same as "default gw"
IP4_ADDR(&wificonf->sta_addr_mask, 255, 255, 255, 0);
IP4_ADDR(&wificonf->sta_addr_gw, 192, 168, 0, 1); // a common default...
}
static void ICACHE_FLASH_ATTR
@@ -67,13 +198,18 @@ configure_station(void)
}
else {
wifi_info("[WiFi] Setting up static IP...");
wifi_dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.ip.addr));
wifi_dbg("[WiFi] Client.mask = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.netmask.addr));
wifi_dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr.gw.addr));
wifi_dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_ip.addr));
wifi_dbg("[WiFi] Client.mask = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_mask.addr));
wifi_dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf->sta_addr_gw.addr));
wifi_station_dhcpc_stop();
// Load static IP config
if (!wifi_set_ip_info(STATION_IF, &wificonf->sta_addr)) {
struct ip_info ipstruct;
ipstruct.ip.addr = wificonf->sta_addr_ip.addr;
ipstruct.netmask.addr = wificonf->sta_addr_mask.addr;
ipstruct.gw.addr = wificonf->sta_addr_gw.addr;
if (!wifi_set_ip_info(STATION_IF, &ipstruct)) {
error("[WiFi] Error setting static IP!");
return;
}
@@ -96,7 +232,7 @@ configure_ap(void)
strcpy((char *) conf.password, (char *) wificonf->ap_password);
conf.authmode = (wificonf->ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK);
conf.ssid_len = (uint8_t) strlen((char *) conf.ssid);
conf.ssid_hidden = wificonf->ap_hidden;
conf.ssid_hidden = (uint8) wificonf->ap_hidden;
conf.max_connection = 4; // default 4 (max possible)
conf.beacon_interval = 100; // default 100 ms
@@ -111,24 +247,32 @@ configure_ap(void)
// Set IP
wifi_info("[WiFi] Configuring SoftAP local IP...");
wifi_dbg("[WiFi] SoftAP.ip = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.ip.addr));
wifi_dbg("[WiFi] SoftAP.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.netmask.addr));
wifi_dbg("[WiFi] SoftAP.gw = "IPSTR, GOOD_IP2STR(wificonf->ap_addr.gw.addr));
wifi_dbg("[WiFi] SoftAP.ip = "IPSTR, GOOD_IP2STR(wificonf->ap_addr_ip.addr));
wifi_dbg("[WiFi] SoftAP.mask = "IPSTR, GOOD_IP2STR(wificonf->ap_addr_mask.addr));
wifi_softap_dhcps_stop();
// Configure DHCP
if (!wifi_set_ip_info(SOFTAP_IF, &wificonf->ap_addr)) {
struct ip_info ipstruct;
ipstruct.ip.addr = wificonf->ap_addr_ip.addr;
ipstruct.netmask.addr = wificonf->ap_addr_mask.addr;
ipstruct.gw.addr = wificonf->ap_addr_ip.addr;
if (!wifi_set_ip_info(SOFTAP_IF, &ipstruct)) {
error("[WiFi] IP set fail!");
return;
}
wifi_info("[WiFi] Configuring SoftAP DHCP server...");
wifi_dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.start_ip.addr));
wifi_dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_range.end_ip.addr));
wifi_dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_start.addr));
wifi_dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(wificonf->ap_dhcp_end.addr));
wifi_dbg("[WiFi] DHCP.lease = %d minutes", wificonf->ap_dhcp_time);
if (!wifi_softap_set_dhcps_lease(&wificonf->ap_dhcp_range)) {
struct dhcps_lease dhcpstruct;
dhcpstruct.start_ip = wificonf->ap_dhcp_start;
dhcpstruct.end_ip = wificonf->ap_dhcp_end;
dhcpstruct.enable = 1; // ???
if (!wifi_softap_set_dhcps_lease(&dhcpstruct)) {
error("[WiFi] DHCP address range set fail!");
return;
}
@@ -148,6 +292,21 @@ configure_ap(void)
}
}
static ETSTimer tim;
static void ICACHE_FLASH_ATTR
wifimgr_apply_settings_later_Cb(void *unused)
{
wifimgr_apply_settings();
}
void ICACHE_FLASH_ATTR
wifimgr_apply_settings_later(uint32_t delay_ms)
{
wifi_info("[WiFi] Scheduling settings apply in %d ms", delay_ms);
TIMER_START(&tim, wifimgr_apply_settings_later_Cb, delay_ms, 0);
}
/**
* Register the WiFi event listener, cycle WiFi, apply settings
*/
@@ -156,6 +315,12 @@ wifimgr_apply_settings(void)
{
wifi_info("[WiFi] Initializing...");
// char buff[64];
//#define XSTRUCT wificonf
//#define X XDUMP_FIELD
// XTABLE_WIFICONF
//#undef X
// !!! Update to current version !!!
// Force wifi cycle
@@ -174,7 +339,7 @@ wifimgr_apply_settings(void)
}
if (opmode != wificonf->opmode) {
wifi_set_opmode_current(wificonf->opmode);
wifi_set_opmode_current((WIFI_MODE) wificonf->opmode);
}
// Configure the client
@@ -187,6 +352,10 @@ wifimgr_apply_settings(void)
configure_ap();
}
// tpw seems to be common - but info is scarce
// at any rate seems to do no harm to have it here
system_phy_set_max_tpw(wificonf->tpw);
wifi_change_flags.ap = false;
wifi_change_flags.sta = false;
+52 -19
View File
@@ -8,6 +8,7 @@
#define ESP_VT100_FIRMWARE_WIFI_MANAGER_H
#include <esp8266.h>
#include "config_xmacros.h"
#include "cgi_wifi.h"
#define SSID_LEN 32
@@ -19,6 +20,44 @@
#define WIFICONF_VERSION 0
#define wifimgr_notify_ap() { wifi_change_flags.ap = true; }
#define wifimgr_notify_sta() { wifi_change_flags.ap = true; }
//....Type................Name..Suffix...............Deref..XGET...........XSET.........................NOTIFY................Allow
// Deref is used to pass the field to xget. Cast is used to convert the &'d field to what xset wants (needed for static arrays)
#define XTABLE_WIFICONF \
X(u8, opmode, /**/, /**/, xget_dec, xset_wifi_opmode, NULL, /**/, 1) \
\
X(u8, tpw, /**/, /**/, xget_dec, xset_wifi_tpw, NULL, wifimgr_notify_ap(), 1) \
X(u8, ap_channel, /**/, /**/, xget_dec, xset_wifi_ap_channel, NULL, wifimgr_notify_ap(), 1) \
X(uchar, ap_ssid, [SSID_LEN], /**/, xget_ustring, xset_wifi_ssid, 1, wifimgr_notify_ap(), 1) \
X(uchar, ap_password, [PASSWORD_LEN], /**/, xget_ustring, xset_wifi_pwd, NULL, wifimgr_notify_ap(), 1) \
X(bool, ap_hidden, /**/, /**/, xget_bool, xset_bool, NULL, wifimgr_notify_ap(), 1) \
\
X(u16, ap_dhcp_time, /**/, /**/, xget_dec, xset_wifi_lease_time, NULL, wifimgr_notify_ap(), 1) \
X(u32, unused1, /**/, /**/, xget_dummy, xset_dummy, NULL, /**/, 0) \
X(struct ip_addr, ap_dhcp_start, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \
X(struct ip_addr, ap_dhcp_end, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \
\
X(struct ip_addr, ap_addr_ip, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \
X(struct ip_addr, ap_addr_mask, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_ap(), 1) \
\
\
X(u32, unused2, /**/, /**/, xget_dummy, xset_dummy, NULL, /**/, 0) \
X(uchar, sta_ssid, [SSID_LEN], /**/, xget_ustring, xset_wifi_ssid, 0, wifimgr_notify_sta(), 1) \
X(uchar, sta_password, [PASSWORD_LEN], /**/, xget_ustring, xset_wifi_pwd, NULL, wifimgr_notify_sta(), 1) \
X(bool, sta_dhcp_enable, /**/, /**/, xget_bool, xset_bool, NULL, wifimgr_notify_sta(), 1) \
\
X(struct ip_addr, sta_addr_ip, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \
X(struct ip_addr, sta_addr_mask, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \
X(struct ip_addr, sta_addr_gw, /**/, &, xget_ip, xset_ip, NULL, wifimgr_notify_sta(), 1) \
\
\
X(u8, config_version, /**/, /**/, xget_dec, xset_u8, NULL, /**/, 1)
// unused1 - replaces 'enabled' bit from old dhcps_lease struct
// unused2 - gap after 'ap_gw' which isn't used and doesn't make sense
/**
* A structure holding all configured WiFi parameters
* and the active state.
@@ -26,25 +65,9 @@
* This block can be used eg. for WiFi config backup.
*/
typedef struct {
WIFI_MODE opmode : 8;
u8 tpw;
// AP config
u8 ap_channel;
u8 ap_ssid[SSID_LEN];
u8 ap_password[PASSWORD_LEN];
bool ap_hidden;
//
u16 ap_dhcp_time; // in minutes
struct dhcps_lease ap_dhcp_range;
struct ip_info ap_addr;
// Client config
u8 sta_ssid[SSID_LEN];
u8 sta_password[PASSWORD_LEN];
bool sta_dhcp_enable;
struct ip_info sta_addr;
u8 config_version;
#define X XSTRUCT_FIELD
XTABLE_WIFICONF
#undef X
} WiFiConfigBundle;
typedef struct {
@@ -59,6 +82,16 @@ extern WiFiConfigBundle * const wificonf;
void wifimgr_restore_defaults(void);
void wifimgr_apply_settings(void);
void wifimgr_apply_settings_later(uint32_t delay_ms);
int getStaIpAsString(char *buffer);
enum xset_result xset_wifi_lease_time(const char *name, u16 *field, const char *buff, const void *arg);
enum xset_result xset_wifi_opmode(const char *name, u8 *field, const char *buff, const void *arg);
enum xset_result xset_wifi_tpw(const char *name, u8 *field, const char *buff, const void *arg);
enum xset_result xset_wifi_ap_channel(const char *name, u8 *field, const char *buff, const void *arg);
enum xset_result xset_wifi_ssid(const char *name, uchar *field, const char *buff, const void *arg);
enum xset_result xset_wifi_pwd(const char *name, uchar *field, const char *buff, const void *arg);
#if DEBUG_WIFI
#define wifi_warn warn