Compare commits

...
11 Commits
40 changed files with 2063 additions and 606 deletions
+1
View File
@@ -14,5 +14,6 @@ eagle.app.sym
.idea/
CMakeLists.txt
cmake-build-debug/
.sass-cache
*.map
+1 -1
View File
@@ -65,7 +65,7 @@ LIBS += esphttpd
# compiler flags using during compilation of source files
CFLAGS = -Os -ggdb -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
-nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \
-Wno-address -Wno-unused
-Wno-address -Wno-unused -DHTTPD_MAX_BACKLOG_SIZE=8192
# linker flags used to generate the main object file
LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static
+68 -37
View File
@@ -13,73 +13,104 @@ Those forks include improvements not yet available upstream.
This project aims to be a wireless terminal emulator that'll work with the likes of
Arduino, AVR, PIC, STM8, STM32, mbed etc, anything with UART, even your USB-serial dongle will work.
Connect it to the master device via UART and use the terminal (on your PC or phone) for debug logging,
Connect it to the master device via UART and use the terminal on the built-in web page for debug logging,
remote control etc. It works like a simple LCD screen, in a way.
It lets you make simple UI (manipulating the screen with ANSI sequences) and receive input from buttons on
the webpage (and keyboard on PC). Touch input is a possibility but not currently implemented.
the webpage (and keyboard on PC). Touch input is planned as well.
The screen size should be adjustable up to 25x80 (via a special control sequence) and uses 16 standard colors.
The screen size is adjustable up to 25x80 (via a special control sequence) and uses 16 standard colors
(8 dark and 8 bright).
## Project status
*Still far from finished and also there's like zero documentation, but you can give it a spin if you wish.*
*Almost finished, still possibly buggy, but it looks promising. Most of the features are there now.*
- We have a working **2-way terminal** (UART->ESP->Browser and vice versa) with real-time update via websocket.
This means that what you type in the browser is sent to UART0 and what's received on UART0 is processed by the
ANSI parser and applied to the internal screen buffer. You'll also immediately see the changes in your browser.
ANSI parser and applied to the internal screen buffer. You'll also immediately see the changes in your browser.
There's a filter in the way that discards garbage characters (like unicode and most ASCII outside 32-126).
For a quick test, try connecting the UART0 Rx and Tx to make a loopback interface.
You should then see what you type & can even try out the ANSI sequences.
Arrow keys generate ANSI sequences, ESC sends literal ASCII code 27 - should be all you need to get started.
For a quick test, try connecting the UART0 Rx and Tx with a piece of wire to make a loopback interface.
*NOTE: Use the bare module, not something like LoLin or NodeMCU with a FTDI, it'll interfere*.
You should then directly see what you type & can even try some ANSI sequences, right from the browser.
- All ANSI sequences that make sense, as well as control codes like Backspace and CR / LF are implemented.
Set colors with your usual `\e[31;1m` etc (see Wikipedia). `\e` is the ASCII code 27 (ESC).
Set colors with your usual `\e[31;1m` etc (see Wikipedia). `\e` is the ASCII code 27 (ESC).
Arrow keys generate ANSI sequences, ESC sends literal ASCII code 27 etc. Almost everything can be input
straight from the browser.
- To resize the screen, send `\e]W<rows>;<cols>\a` (it's an OSC code, terminated by ST).
- Buttons pressed in the browser UI send ASCII codes 1..5. Mouse clicks also send events to the server,
but currently don't generate any output in the terminal.
but currently don't generate any output in the terminal, I still hadn't decided on the best encoding.
- There is also currently no way to set up the WiFi, so it'll use whatever you configured the ESP to
in your previous project. This'll be addressed later.
- By tapping the header on the terminal page, you'll open the WiFi config page. It's in essence the
esphttpd's wifi config page, but re-styled and much improved. You can set AP SSID, channel, see the IP
address etc right there.
## Setting it all up
## Development
### Installation for development
- Clone this project with `--recursive`, or afterwards run `git submodule init` and `git submodule update`.
- Install [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk/) and build it with
`make toolchain esptool libhal STANDALONE=n`. Make sure the `xtensa-lx106-elf/bin` folder is on $PATH.
- Install [esptool](https://github.com/espressif/esptool) (it's in Arch community repo and on AUR, too)
`make toolchain esptool libhal STANDALONE=n`.
Make sure the `xtensa-lx106-elf/bin` folder is on $PATH.
- Install [esptool](https://github.com/espressif/esptool) (it's in the Arch community repo and on AUR, too)
- Set up udev rules so you have access to ttyUSB0 without root, eg:
```
KERNEL=="tty[A-Z]*[0-9]*", GROUP="uucp", MODE="0666"
```
- Clone this project with `--recursive`, or afterwards run `git submodule init` and `git submodule update`.
## Development
- Install Ragel if you wish to make modifications to the parser.
If not, comment out it's call in `build_parser.sh`. The `.rl` file is the actual source, the `.c` is generated.
The web resources are in `html_orig`. To prepare for a build, run `compress_html.sh`.
This should pack them and put in `html`.
- Install Ruby and then the `sass` package with `gem install sass` (or try some other implementation, such as
`sassc`)
If you're missing the compression tools, you can simply copy them there manually uncompressed and hope for the best.
- Get the IoT SDK from one of:
serial comm is handled in `serial.c`, sockets etc in `user_main.c`.
```
ESP8266_NONOS_SDK_V2.0.0_16_08_10.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1690"
ESP8266_NONOS_SDK_V2.0.0_16_07_19.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1613"
ESP8266_NONOS_SDK_V1.5.4_16_05_20.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1469"
ESP8266_NONOS_SDK_V1.5.3_16_04_18.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1361"
ESP8266_NONOS_SDK_V1.5.2_16_01_29.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1079"
```
Get the IoT SDK from one of:
It's tested with 1.5.2, 2.0.0 won't work without adjusting the build scripts. Any 1.5.x could be fine.
```
ESP8266_NONOS_SDK_V2.0.0_16_08_10.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1690"
ESP8266_NONOS_SDK_V2.0.0_16_07_19.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1613"
ESP8266_NONOS_SDK_V1.5.4_16_05_20.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1469"
ESP8266_NONOS_SDK_V1.5.3_16_04_18.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1361"
ESP8266_NONOS_SDK_V1.5.2_16_01_29.zip:
wget --content-disposition "http://bbs.espressif.com/download/file.php?id=1079"
```
The `esp-open-sdk` Makefile could also download this for you with the right flags.
It's tested with 1.5.2, 2.0.0 won't work without adjusting the build scripts. Any 1.5.x could be fine.
- Make sure your `esphttpdconfig.mk` is set up properly - link to the SDK etc.
To flash, just run `make flash`. First make sure your `esphttpdconfig.mk` is set up properly - link to sdk etc.
### Web resources
The web resources are in `html_orig`. To prepare for a build, run `build_web.sh`, which packs them and
copies over to `html`. The compression and minification is handled by scripts in libesphttpd, specifically
it runs yuicompressor on js and css and gzip or heatshrink on the other files. The `html` folder is
then embedded in the firmware image.
It's kind of tricky to develop the web resources locally; you might want to try the "split image"
Makefile option, then you can flash just the html portion with `make htmlflash`. I haven't tried this.
### Flashing
The Makefile should automatically build the parser and web resources for you when you run `make`.
Sometimes it does not, particularly with `make -B`. Try just plain `make`. You can always run those
build scripts manually, too.
To flash, just run `make flash`.
+10
View File
@@ -2,6 +2,11 @@
echo "-- Preparing WWW files --"
rm -r html
mkdir -p html/img
mkdir -p html/js
mkdir -p html/css
# Join scripts
DD=html_orig/jssrc
cat $DD/chibi.js \
@@ -11,10 +16,15 @@ cat $DD/chibi.js \
$DD/term.js \
$DD/wifi.js > html/js/app.js
sass --sourcemap=none html_orig/sass/app.scss html_orig/css/app.css
# No need to compress CSS and JS now, we run YUI on it later
cp html_orig/css/app.css html/css/app.css
cp html_orig/term.html html/term.tpl
cp html_orig/wifi.html html/wifi.tpl
cp html_orig/about.html html/about.tpl
cp html_orig/help.html html/help.tpl
cp html_orig/wifi_conn.html html/wifi_conn.tpl
cp html_orig/img/loader.gif html/img/loader.gif
cp html_orig/img/cvut.svg html/img/cvut.svg
cp html_orig/favicon.ico html/favicon.ico
+80
View File
@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>About - ESP8266 Remote Terminal</title>
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
</head>
<body class="page-about">
<h1 onclick="location.href='/'">About</h1>
<div class="Box">
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min">
<h2>ESP8266 Remote Terminal</h2>
<img src="/img/cvut.svg" id="logo2" class="mq-phone">
<p>&copy; Ondřej Hruška, 2017 &lt;<a href="mailto:ondra@ondrovo.com">ondra@ondrovo.com</a>&gt;</p>
<p><a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br>Department of Measurement, FEE CTU</p>
</div>
<div class="Box">
<h2>Firmware</h2>
<table>
<tr>
<th>Firmware</th>
<td>v%vers_fw%, build <i>%date%</i> at <i>%time%</i></td>
</tr>
<tr>
<th>libesphttpd</th>
<td>v%vers_httpd%</td>
</tr>
<tr>
<th>ESP&nbsp;IoT&nbsp;SDK</th>
<td>v%vers_sdk%</td>
</tr>
</table>
</div>
<div class="Box">
<h2>Issues</h2>
<p>
Please report any issues to the <a href="%githubrepo%/issues">bugtracker</a> or send them by e-mail.
</p>
<p>
Firmware updates can be downloaded from the <a href="%githubrepo%/releases">releases page</a>.
Flash the images using <a href="https://github.com/espressif/esptool">esptool</a>.
</p>
</div>
<div class="Box">
<h2>Contributing</h2>
<p>
Submit your improvements and ideas to the project on <a href="%githubrepo%">GitHub</a>.<br>
</p>
</div>
<div class="Box">
<h2>Acknowledgements</h2>
<p>
The webserver is based on a <a href="https://github.com/MightyPork/libesphttpd">fork</a> of the
<a href="https://github.com/Spritetm/esphttpd">esphttpd</a> library by Jeroen Domburg (Sprite_tm).
</p>
<p>
Using (modified) JS library <a href="https://github.com/kylebarrow/chibi">chibi.js</a> by Kyle Barrow as a lightweight jQuery alternative.
</p>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/help">Help</a><!--
--><a href="/wifi">WiFi config</a>
</nav>
</body>
</html>
+167 -4
View File
@@ -461,8 +461,6 @@ h1 {
h2 {
font-size: 1.26563em;
margin-bottom: 0.61805rem; }
h2:first-child {
margin-top: 0; }
td, th {
padding: 0.38198rem;
@@ -498,6 +496,21 @@ tbody td input[type="text"], tbody td input[type="number"] {
#loader.show {
opacity: 1; }
ul > * {
padding-top: .1em;
padding-bottom: .1em; }
#botnav {
padding-top: 1.5em;
text-align: center; }
#botnav a {
padding: 0 0.38198rem;
text-decoration: underline; }
#botnav a, #botnav a:visited, #botnav a:link {
color: #2e4d6e; }
#botnav a:hover {
color: #5abfff; }
button, input[type=submit], .button {
text-align: center;
cursor: pointer;
@@ -668,6 +681,10 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
.page-term h1 {
font-size: 1.42383em; } }
.page-term #screen {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
font-family: monospace;
font-size: 16pt;
white-space: nowrap;
@@ -680,9 +697,11 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
cursor: pointer; }
.page-term #screen span:hover {
outline: 1px solid rgba(255, 255, 255, 0.4); }
@media screen and (max-width: 1000px) {
@media screen and (max-width: 544px) {
.page-term #screen span:hover {
outline: 0 none; } }
.page-term #screen .fg8, .page-term #screen .fg9, .page-term #screen .fg10, .page-term #screen .fg11, .page-term #screen .fg12, .page-term #screen .fg13, .page-term #screen .fg14, .page-term #screen .fg15 {
font-weight: bold; }
.page-term #buttons {
margin-top: 10px;
white-space: nowrap; }
@@ -698,4 +717,148 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
#termwrap {
text-align: center; }
/*# sourceMappingURL=app.css.map */
.page-about .Box {
padding-left: 1rem;
padding-right: 1rem; }
.page-about .Box a {
font-weight: bold; }
.page-about #logo {
float: right;
height: 130px; }
.page-about #logo2 {
max-width: 100%; }
.page-about td {
white-space: normal; }
.colorprev {
margin-top: 0.38198rem;
margin-bottom: 0.38198rem; }
.colorprev span {
display: inline-block;
width: 2em;
padding: 0.38198rem 0;
text-align: center; }
.ansiref, .ansiref td, .ansiref th {
border: 1px solid #666; }
.ansiref th, .ansiref td {
white-space: normal; }
.ansiref th {
background-color: rgba(255, 255, 255, 0.1); }
.ansiref td:nth-child(1) {
font-family: monospace; }
.ansiref.w100 {
width: 100%; }
.ansiref.w100 td:nth-child(1) {
width: 6em; }
.ansiref.w100 td:nth-child(2) {
width: 8em; }
.fg0 {
color: #111213; }
.bg0 {
background-color: #111213; }
.fg1 {
color: #CC0000; }
.bg1 {
background-color: #CC0000; }
.fg2 {
color: #4E9A06; }
.bg2 {
background-color: #4E9A06; }
.fg3 {
color: #C4A000; }
.bg3 {
background-color: #C4A000; }
.fg4 {
color: #3465A4; }
.bg4 {
background-color: #3465A4; }
.fg5 {
color: #75507B; }
.bg5 {
background-color: #75507B; }
.fg6 {
color: #06989A; }
.bg6 {
background-color: #06989A; }
.fg7 {
color: #D3D7CF; }
.bg7 {
background-color: #D3D7CF; }
.fg8 {
color: #555753; }
.bg8 {
background-color: #555753; }
.fg9 {
color: #EF2929; }
.bg9 {
background-color: #EF2929; }
.fg10 {
color: #8AE234; }
.bg10 {
background-color: #8AE234; }
.fg11 {
color: #FCE94F; }
.bg11 {
background-color: #FCE94F; }
.fg12 {
color: #729FCF; }
.bg12 {
background-color: #729FCF; }
.fg13 {
color: #AD7FA8; }
.bg13 {
background-color: #AD7FA8; }
.fg14 {
color: #34E2E2; }
.bg14 {
background-color: #34E2E2; }
.fg15 {
color: #EEEEEC; }
.bg15 {
background-color: #EEEEEC; }
@media screen and (min-width: 545px) {
.mq-phone {
display: none; } }
@media screen and (max-width: 544px) {
.mq-tablet-min {
display: none; } }
@media screen and (min-width: 1001px) {
.mq-tablet-max {
display: none; } }
@media screen and (max-width: 1000px) {
.mq-normal-min {
display: none; } }
+326
View File
@@ -0,0 +1,326 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Help - ESP8266 Remote Terminal</title>
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
</head>
<body class="page-help">
<h1 onclick="location.href='/'">Quick Reference</h1>
<div class="Box">
<h2>Wiring</h2>
<ul>
<li>Communication UART on pins <b>Rx, Tx</b> at 115200-8-1-N</li>
<li>Debug log on pin <b>GPIO2</b> at 115200-8-1-N</li>
<li>Use 3.3V logic, or 5V with protection resistors (10k)</li>
<li>Connect Rx and Tx with a piece of wire to test the terminal alone, you should see what you type in the browser.
NOTE: This won't work if your ESP8266 board has a built-in USB-serial.</li>
</ul>
</div>
<div class="Box">
<h2>Screen</h2>
<ul>
<li>Most ANSI escape sequences are supported.</li>
<li>The max screen size is 2000 characters (eg. <b>25x80</b>), default is <b>10x26</b>. Set using <code>\e]W?;?\a</code> with rows;cols.</li>
<li>The screen will automatically scroll, can be used for log output.</li>
<li>Display update is sent <b>after 20 ms of inactivity</b>.</li>
<li>The browser display needs WebSockets for the real-time updating. It may not work on really old phones / browsers.</li>
</ul>
</div>
<div class="Box">
<h2>Color Reference</h2>
<p>Color is set using <code>\e[?m</code> or <code>\e[?;?m</code> where "?" are numbers from the following tables:</p>
<b>Foreground</b>
<div class="colorprev">
<span class="fg0" style="background:#333">30</span>
<span class="bg0 fg1">31</span>
<span class="bg0 fg2">32</span>
<span class="bg0 fg3">33</span>
<span class="bg0 fg4">34</span>
<span class="bg0 fg5">35</span>
<span class="bg0 fg6">36</span>
<span class="bg0 fg7">37</span>
</div>
<div class="colorprev">
<span class="bg0 fg8">90</span>
<span class="bg0 fg9">91</span>
<span class="bg0 fg10">92</span>
<span class="bg0 fg11">93</span>
<span class="bg0 fg12">94</span>
<span class="bg0 fg13">95</span>
<span class="bg0 fg14">96</span>
<span class="bg0 fg15">97</span>
</div>
<b>Background</b>
<div class="colorprev">
<span class="bg0 fg15">30</span>
<span class="bg1 fg15">31</span>
<span class="bg2 fg15">32</span>
<span class="bg3 fg0">33</span>
<span class="bg4 fg15">34</span>
<span class="bg5 fg15">35</span>
<span class="bg6 fg15">36</span>
<span class="bg7 fg0">37</span>
</div>
<div class="colorprev">
<span class="bg8 fg15">100</span>
<span class="bg9 fg0">101</span>
<span class="bg10 fg0">102</span>
<span class="bg11 fg0">103</span>
<span class="bg12 fg0">104</span>
<span class="bg13 fg0">105</span>
<span class="bg14 fg0">106</span>
<span class="bg15 fg0">107</span>
</div>
<p>Bright foreground can also be set using the "bold" attribute 1 (eg. <code>\e[31;1m</code>). For more details, see the ANSI code reference below.</p>
</div>
<div class="Box">
<h2>User Input</h2>
<p>
All the user types on their keyboard is sent as-is to the UART, including ESC, ANSI sequences for arrow keys and CR-LF for Enter.
The input is limited to ASCII codes 32-126, backspace 8 and tab 9.
</p>
<p>The buttons under the screen send ASCII codes 1, 2, 3, 4, 5.</p>
<p>
<b>Mouse input</b> (click/tap) is sent as <code>\e?;?M</code> with row;column. You can use this for on-screen buttons, menu navigation etc.
<i>Please note this is a custom sequence, not supported by PuTTY or other terminals.</i>
</p>
</div>
<div class="Box">
<h2>Supported ANSI Escape Sequences</h2>
<p>Sequences are started by ASCII code 27 (ESC, <code>\e</code>, <code>\x1b</code>, <code>\033</code>)</p>
<p>Instead of <code>\a</code> (BELL, ASCII 7) in the commands, you can use `\e\` (ESC + backslash). It's the Text Separator code.</p>
<h3>Text Attributes</h3>
<p>
All text attributes are set using <code>\e[...m</code> where the dots are numbers separated by semicolons.
You can combine up to 3 attributes in a single command.
</p>
<table class="ansiref">
<thead>
<tr>
<th>Attribute</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>Reset text attributes to default</td>
</tr>
<tr>
<td>7</td>
<td>Inverse (fg/bg swap when printing)</td>
</tr>
<tr>
<td>27</td>
<td>Inverse OFF</td>
</tr>
<tr>
<td>30-37, 90-97</td>
<td>Foreground color, normal and bright</td>
</tr>
<tr>
<td>40-47, 100-107</td>
<td>Background color, normal and bright</td>
</tr>
</tbody>
</table>
<h3>Cursor</h3>
<p>Movement commands scroll the screen if needed. The coordinates are 1-based, origin top left.</p>
<table class="ansiref w100">
<thead>
<tr>
<th>Code</th>
<th>Params</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>\e[?A</td>
<td>[count]</td>
<td>Move cursor up</td>
</tr>
<tr>
<td>\e[?B</td>
<td>[count]</td>
<td>Move cursor down</td>
</tr>
<tr>
<td>\e[?C</td>
<td>[count]</td>
<td>Move cursor forward (right)</td>
</tr>
<tr>
<td>\e[?D</td>
<td>[count]</td>
<td>Move cursor backward (left)</td>
</tr>
<tr>
<td>\e[?E</td>
<td>[count]</td>
<td>Go N line down, start of line</td>
</tr>
<tr>
<td>\e[?F</td>
<td>[count]</td>
<td>Go N lines up, start of line</td>
</tr>
<tr>
<td>\e[?G</td>
<td>column</td>
<td>Go to column</td>
</tr>
<tr>
<td>\e[?;?G</td>
<td>[row=1];[col=1]</td>
<td>Go to row and column</td>
</tr>
<tr>
<td>\e[6n</td>
<td>--</td>
<td>Query cursor position. Position is sent back as <code>\e[?;?R</code> with row;column.</td>
</tr>
<tr>
<td>\e[s</td>
<td>--</td>
<td>Store position</td>
</tr>
<tr>
<td>\e[u</td>
<td>--</td>
<td>Restore position</td>
</tr>
<tr>
<td>\e7</td>
<td>--</td>
<td>Store position & attributes</td>
</tr>
<tr>
<td>\e8</td>
<td>--</td>
<td>Restore position & attributes</td>
</tr>
<tr>
<td>\e[?25l</td>
<td>--</td>
<td>Hide cursor (literal question mark, lowercase L!)</td>
</tr>
<tr>
<td>\e[?25h</td>
<td>--</td>
<td>Show cursor (literal question mark!)</td>
</tr>
</tbody>
</table>
<h3>Screen</h3>
<table class="ansiref w100">
<thead>
<tr>
<th>Code</th>
<th>Params</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>\e[?J</td>
<td>[mode=0]</td>
<td>Clear screen. Mode: 0 - from cursor, 1 - to cursor, 2 - all</td>
</tr>
<tr>
<td>\e[?K</td>
<td>[mode=0]</td>
<td>Erase line. Mode: 0 - from cursor, 1 - to cursor, 2 - all</td>
</tr>
<tr>
<td>\e[?S</td>
<td>[lines]</td>
<td>Scroll screen content up, add empty line at the bottom</td>
</tr>
<tr>
<td>\e[?T</td>
<td>[lines]</td>
<td>Scroll screen content down, add empty line at the top</td>
</tr>
<tr>
<td>\e]W?;?\a</td>
<td>rows;cols</td>
<td>Set screen size, maximum 25x80 (resp. total 2000 characters). This also clears the screen.</td>
</tr>
</tbody>
</table>
<h3>System Commands</h3>
<table class="ansiref w100">
<thead>
<tr>
<th>Code</th>
<th>Params</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>\ec</td>
<td>--</td>
<td>
"Device Reset" - clear screen, reset attributes, show cursor & move it to 1,1.
The screen size and WiFi settings stay unchanged.
</td>
</tr>
<tr>
<td>\e]FR\a</td>
<td>--</td>
<td>"Factory Reset", emergency code when you mess up the WiFi, restores SSID to unique default, clears stored credentials & enters Client+AP mode.</td>
</tr>
<tr>
<td>\e[5n</td>
<td>--</td>
<td>Query device status, replies with <code>\e[0n</code> "device is OK". Can be used to check if the UART works.</td>
</tr>
</tbody>
</table>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/wifi">WiFi config</a><!--
--><a href="/about">About</a>
</nav>
</body>
</html>
+5
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.8 KiB

+6 -5
View File
@@ -958,9 +958,10 @@ $._loader = function(vis) {
bg = inv ? cell.fg : cell.bg;
// Update
e.innerText = (cell.t+' ')[0];
e.style.color = colorHex(fg);
e.style.backgroundColor = colorHex(bg);
e.style.fontWeight = fg > 7 ? 'bold' : 'normal';
e.classList.className = 'fg'+fg+' bg'+bg;
// e.style.color = colorHex(fg);
// e.style.backgroundColor = colorHex(bg);
// e.style.fontWeight = fg > 7 ? 'bold' : 'normal';
}
/** Show entire screen */
@@ -1286,7 +1287,7 @@ $._loader = function(vis) {
/** Ask the CGI what APs are visible (async) */
function scanAPs() {
$.get('/wifi/scan', onScan);
$.get('http://'+_root+'/wifi/scan', onScan);
}
function rescan(time) {
@@ -1340,7 +1341,7 @@ $._loader = function(vis) {
var abortTmeo;
function getStatus() {
xhr.open("GET", "/wifi/connstatus");
xhr.open("GET", 'http://'+_root+"/wifi/connstatus");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) {
clearTimeout(abortTmeo);
+6
View File
@@ -24,6 +24,12 @@
</div>
</div>
<nav id="botnav">
<a href="/wifi">WiFi config</a><!--
--><a href="/help">Help</a><!--
--><a href="/about">About</a>
</nav>
<script>
_root = window.location.host;
termInit(%screenData%);
+6
View File
@@ -64,6 +64,12 @@
<div id="ap-list" style="display:none"></div>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/help">Help</a><!--
--><a href="/about">About</a>
</nav>
<div class="Modal hidden" id="psk-modal">
<div class="Dialog">
<form action="/wifi/connect" method="post" id="conn-form">
+80
View File
@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>About - ESP8266 Remote Terminal</title>
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
</head>
<body class="page-about">
<h1 onclick="location.href='/'">About</h1>
<div class="Box">
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min">
<h2>ESP8266 Remote Terminal</h2>
<img src="/img/cvut.svg" id="logo2" class="mq-phone">
<p>&copy; Ondřej Hruška, 2017 &lt;<a href="mailto:ondra@ondrovo.com">ondra@ondrovo.com</a>&gt;</p>
<p><a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br>Department of Measurement, FEE CTU</p>
</div>
<div class="Box">
<h2>Firmware</h2>
<table>
<tr>
<th>Firmware</th>
<td>v%vers_fw%, build <i>%date%</i> at <i>%time%</i></td>
</tr>
<tr>
<th>libesphttpd</th>
<td>v%vers_httpd%</td>
</tr>
<tr>
<th>ESP&nbsp;IoT&nbsp;SDK</th>
<td>v%vers_sdk%</td>
</tr>
</table>
</div>
<div class="Box">
<h2>Issues</h2>
<p>
Please report any issues to the <a href="%githubrepo%/issues">bugtracker</a> or send them by e-mail.
</p>
<p>
Firmware updates can be downloaded from the <a href="%githubrepo%/releases">releases page</a>.
Flash the images using <a href="https://github.com/espressif/esptool">esptool</a>.
</p>
</div>
<div class="Box">
<h2>Contributing</h2>
<p>
Submit your improvements and ideas to the project on <a href="%githubrepo%">GitHub</a>.<br>
</p>
</div>
<div class="Box">
<h2>Acknowledgements</h2>
<p>
The webserver is based on a <a href="https://github.com/MightyPork/libesphttpd">fork</a> of the
<a href="https://github.com/Spritetm/esphttpd">esphttpd</a> library by Jeroen Domburg (Sprite_tm).
</p>
<p>
Using (modified) JS library <a href="https://github.com/kylebarrow/chibi">chibi.js</a> by Kyle Barrow as a lightweight jQuery alternative.
</p>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/help">Help</a><!--
--><a href="/wifi">WiFi config</a>
</nav>
</body>
</html>
+167 -4
View File
@@ -461,8 +461,6 @@ h1 {
h2 {
font-size: 1.26563em;
margin-bottom: 0.61805rem; }
h2:first-child {
margin-top: 0; }
td, th {
padding: 0.38198rem;
@@ -498,6 +496,21 @@ tbody td input[type="text"], tbody td input[type="number"] {
#loader.show {
opacity: 1; }
ul > * {
padding-top: .1em;
padding-bottom: .1em; }
#botnav {
padding-top: 1.5em;
text-align: center; }
#botnav a {
padding: 0 0.38198rem;
text-decoration: underline; }
#botnav a, #botnav a:visited, #botnav a:link {
color: #2e4d6e; }
#botnav a:hover {
color: #5abfff; }
button, input[type=submit], .button {
text-align: center;
cursor: pointer;
@@ -668,6 +681,10 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
.page-term h1 {
font-size: 1.42383em; } }
.page-term #screen {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
font-family: monospace;
font-size: 16pt;
white-space: nowrap;
@@ -680,9 +697,11 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
cursor: pointer; }
.page-term #screen span:hover {
outline: 1px solid rgba(255, 255, 255, 0.4); }
@media screen and (max-width: 1000px) {
@media screen and (max-width: 544px) {
.page-term #screen span:hover {
outline: 0 none; } }
.page-term #screen .fg8, .page-term #screen .fg9, .page-term #screen .fg10, .page-term #screen .fg11, .page-term #screen .fg12, .page-term #screen .fg13, .page-term #screen .fg14, .page-term #screen .fg15 {
font-weight: bold; }
.page-term #buttons {
margin-top: 10px;
white-space: nowrap; }
@@ -698,4 +717,148 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
#termwrap {
text-align: center; }
/*# sourceMappingURL=app.css.map */
.page-about .Box {
padding-left: 1rem;
padding-right: 1rem; }
.page-about .Box a {
font-weight: bold; }
.page-about #logo {
float: right;
height: 130px; }
.page-about #logo2 {
max-width: 100%; }
.page-about td {
white-space: normal; }
.colorprev {
margin-top: 0.38198rem;
margin-bottom: 0.38198rem; }
.colorprev span {
display: inline-block;
width: 2em;
padding: 0.38198rem 0;
text-align: center; }
.ansiref, .ansiref td, .ansiref th {
border: 1px solid #666; }
.ansiref th, .ansiref td {
white-space: normal; }
.ansiref th {
background-color: rgba(255, 255, 255, 0.1); }
.ansiref td:nth-child(1) {
font-family: monospace; }
.ansiref.w100 {
width: 100%; }
.ansiref.w100 td:nth-child(1) {
width: 6em; }
.ansiref.w100 td:nth-child(2) {
width: 8em; }
.fg0 {
color: #111213; }
.bg0 {
background-color: #111213; }
.fg1 {
color: #CC0000; }
.bg1 {
background-color: #CC0000; }
.fg2 {
color: #4E9A06; }
.bg2 {
background-color: #4E9A06; }
.fg3 {
color: #C4A000; }
.bg3 {
background-color: #C4A000; }
.fg4 {
color: #3465A4; }
.bg4 {
background-color: #3465A4; }
.fg5 {
color: #75507B; }
.bg5 {
background-color: #75507B; }
.fg6 {
color: #06989A; }
.bg6 {
background-color: #06989A; }
.fg7 {
color: #D3D7CF; }
.bg7 {
background-color: #D3D7CF; }
.fg8 {
color: #555753; }
.bg8 {
background-color: #555753; }
.fg9 {
color: #EF2929; }
.bg9 {
background-color: #EF2929; }
.fg10 {
color: #8AE234; }
.bg10 {
background-color: #8AE234; }
.fg11 {
color: #FCE94F; }
.bg11 {
background-color: #FCE94F; }
.fg12 {
color: #729FCF; }
.bg12 {
background-color: #729FCF; }
.fg13 {
color: #AD7FA8; }
.bg13 {
background-color: #AD7FA8; }
.fg14 {
color: #34E2E2; }
.bg14 {
background-color: #34E2E2; }
.fg15 {
color: #EEEEEC; }
.bg15 {
background-color: #EEEEEC; }
@media screen and (min-width: 545px) {
.mq-phone {
display: none; } }
@media screen and (max-width: 544px) {
.mq-tablet-min {
display: none; } }
@media screen and (min-width: 1001px) {
.mq-tablet-max {
display: none; } }
@media screen and (max-width: 1000px) {
.mq-normal-min {
display: none; } }
+326
View File
@@ -0,0 +1,326 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Help - ESP8266 Remote Terminal</title>
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
</head>
<body class="page-help">
<h1 onclick="location.href='/'">Quick Reference</h1>
<div class="Box">
<h2>Wiring</h2>
<ul>
<li>Communication UART on pins <b>Rx, Tx</b> at 115200-8-1-N</li>
<li>Debug log on pin <b>GPIO2</b> at 115200-8-1-N</li>
<li>Use 3.3V logic, or 5V with protection resistors (10k)</li>
<li>Connect Rx and Tx with a piece of wire to test the terminal alone, you should see what you type in the browser.
NOTE: This won't work if your ESP8266 board has a built-in USB-serial.</li>
</ul>
</div>
<div class="Box">
<h2>Screen</h2>
<ul>
<li>Most ANSI escape sequences are supported.</li>
<li>The max screen size is 2000 characters (eg. <b>25x80</b>), default is <b>10x26</b>. Set using <code>\e]W?;?\a</code> with rows;cols.</li>
<li>The screen will automatically scroll, can be used for log output.</li>
<li>Display update is sent <b>after 20 ms of inactivity</b>.</li>
<li>The browser display needs WebSockets for the real-time updating. It may not work on really old phones / browsers.</li>
</ul>
</div>
<div class="Box">
<h2>Color Reference</h2>
<p>Color is set using <code>\e[?m</code> or <code>\e[?;?m</code> where "?" are numbers from the following tables:</p>
<b>Foreground</b>
<div class="colorprev">
<span class="fg0" style="background:#333">30</span>
<span class="bg0 fg1">31</span>
<span class="bg0 fg2">32</span>
<span class="bg0 fg3">33</span>
<span class="bg0 fg4">34</span>
<span class="bg0 fg5">35</span>
<span class="bg0 fg6">36</span>
<span class="bg0 fg7">37</span>
</div>
<div class="colorprev">
<span class="bg0 fg8">90</span>
<span class="bg0 fg9">91</span>
<span class="bg0 fg10">92</span>
<span class="bg0 fg11">93</span>
<span class="bg0 fg12">94</span>
<span class="bg0 fg13">95</span>
<span class="bg0 fg14">96</span>
<span class="bg0 fg15">97</span>
</div>
<b>Background</b>
<div class="colorprev">
<span class="bg0 fg15">30</span>
<span class="bg1 fg15">31</span>
<span class="bg2 fg15">32</span>
<span class="bg3 fg0">33</span>
<span class="bg4 fg15">34</span>
<span class="bg5 fg15">35</span>
<span class="bg6 fg15">36</span>
<span class="bg7 fg0">37</span>
</div>
<div class="colorprev">
<span class="bg8 fg15">100</span>
<span class="bg9 fg0">101</span>
<span class="bg10 fg0">102</span>
<span class="bg11 fg0">103</span>
<span class="bg12 fg0">104</span>
<span class="bg13 fg0">105</span>
<span class="bg14 fg0">106</span>
<span class="bg15 fg0">107</span>
</div>
<p>Bright foreground can also be set using the "bold" attribute 1 (eg. <code>\e[31;1m</code>). For more details, see the ANSI code reference below.</p>
</div>
<div class="Box">
<h2>User Input</h2>
<p>
All the user types on their keyboard is sent as-is to the UART, including ESC, ANSI sequences for arrow keys and CR-LF for Enter.
The input is limited to ASCII codes 32-126, backspace 8 and tab 9.
</p>
<p>The buttons under the screen send ASCII codes 1, 2, 3, 4, 5.</p>
<p>
<b>Mouse input</b> (click/tap) is sent as <code>\e?;?M</code> with row;column. You can use this for on-screen buttons, menu navigation etc.
<i>Please note this is a custom sequence, not supported by PuTTY or other terminals.</i>
</p>
</div>
<div class="Box">
<h2>Supported ANSI Escape Sequences</h2>
<p>Sequences are started by ASCII code 27 (ESC, <code>\e</code>, <code>\x1b</code>, <code>\033</code>)</p>
<p>Instead of <code>\a</code> (BELL, ASCII 7) in the commands, you can use `\e\` (ESC + backslash). It's the Text Separator code.</p>
<h3>Text Attributes</h3>
<p>
All text attributes are set using <code>\e[...m</code> where the dots are numbers separated by semicolons.
You can combine up to 3 attributes in a single command.
</p>
<table class="ansiref">
<thead>
<tr>
<th>Attribute</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>Reset text attributes to default</td>
</tr>
<tr>
<td>7</td>
<td>Inverse (fg/bg swap when printing)</td>
</tr>
<tr>
<td>27</td>
<td>Inverse OFF</td>
</tr>
<tr>
<td>30-37, 90-97</td>
<td>Foreground color, normal and bright</td>
</tr>
<tr>
<td>40-47, 100-107</td>
<td>Background color, normal and bright</td>
</tr>
</tbody>
</table>
<h3>Cursor</h3>
<p>Movement commands scroll the screen if needed. The coordinates are 1-based, origin top left.</p>
<table class="ansiref w100">
<thead>
<tr>
<th>Code</th>
<th>Params</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>\e[?A</td>
<td>[count]</td>
<td>Move cursor up</td>
</tr>
<tr>
<td>\e[?B</td>
<td>[count]</td>
<td>Move cursor down</td>
</tr>
<tr>
<td>\e[?C</td>
<td>[count]</td>
<td>Move cursor forward (right)</td>
</tr>
<tr>
<td>\e[?D</td>
<td>[count]</td>
<td>Move cursor backward (left)</td>
</tr>
<tr>
<td>\e[?E</td>
<td>[count]</td>
<td>Go N line down, start of line</td>
</tr>
<tr>
<td>\e[?F</td>
<td>[count]</td>
<td>Go N lines up, start of line</td>
</tr>
<tr>
<td>\e[?G</td>
<td>column</td>
<td>Go to column</td>
</tr>
<tr>
<td>\e[?;?G</td>
<td>[row=1];[col=1]</td>
<td>Go to row and column</td>
</tr>
<tr>
<td>\e[6n</td>
<td>--</td>
<td>Query cursor position. Position is sent back as <code>\e[?;?R</code> with row;column.</td>
</tr>
<tr>
<td>\e[s</td>
<td>--</td>
<td>Store position</td>
</tr>
<tr>
<td>\e[u</td>
<td>--</td>
<td>Restore position</td>
</tr>
<tr>
<td>\e7</td>
<td>--</td>
<td>Store position & attributes</td>
</tr>
<tr>
<td>\e8</td>
<td>--</td>
<td>Restore position & attributes</td>
</tr>
<tr>
<td>\e[?25l</td>
<td>--</td>
<td>Hide cursor (literal question mark, lowercase L!)</td>
</tr>
<tr>
<td>\e[?25h</td>
<td>--</td>
<td>Show cursor (literal question mark!)</td>
</tr>
</tbody>
</table>
<h3>Screen</h3>
<table class="ansiref w100">
<thead>
<tr>
<th>Code</th>
<th>Params</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>\e[?J</td>
<td>[mode=0]</td>
<td>Clear screen. Mode: 0 - from cursor, 1 - to cursor, 2 - all</td>
</tr>
<tr>
<td>\e[?K</td>
<td>[mode=0]</td>
<td>Erase line. Mode: 0 - from cursor, 1 - to cursor, 2 - all</td>
</tr>
<tr>
<td>\e[?S</td>
<td>[lines]</td>
<td>Scroll screen content up, add empty line at the bottom</td>
</tr>
<tr>
<td>\e[?T</td>
<td>[lines]</td>
<td>Scroll screen content down, add empty line at the top</td>
</tr>
<tr>
<td>\e]W?;?\a</td>
<td>rows;cols</td>
<td>Set screen size, maximum 25x80 (resp. total 2000 characters). This also clears the screen.</td>
</tr>
</tbody>
</table>
<h3>System Commands</h3>
<table class="ansiref w100">
<thead>
<tr>
<th>Code</th>
<th>Params</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>\ec</td>
<td>--</td>
<td>
"Device Reset" - clear screen, reset attributes, show cursor & move it to 1,1.
The screen size and WiFi settings stay unchanged.
</td>
</tr>
<tr>
<td>\e]FR\a</td>
<td>--</td>
<td>"Factory Reset", emergency code when you mess up the WiFi, restores SSID to unique default, clears stored credentials & enters Client+AP mode.</td>
</tr>
<tr>
<td>\e[5n</td>
<td>--</td>
<td>Query device status, replies with <code>\e[0n</code> "device is OK". Can be used to check if the UART works.</td>
</tr>
</tbody>
</table>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/wifi">WiFi config</a><!--
--><a href="/about">About</a>
</nav>
</body>
</html>
+5 -3
View File
@@ -894,6 +894,7 @@ $._loader = function(vis) {
var W, H;
var cursor = {a: false, x: 0, y: 0, suppress: false, hidden: false};
var screen = [];
var blinkIval;
/** Colors table */
var CLR = [// dark gray #2E3436
@@ -1071,7 +1072,8 @@ $._loader = function(vis) {
}
/* Cursor blinking */
setInterval(function() {
clearInterval(blinkIval);
blinkIval = setInterval(function() {
cursor.a = !cursor.a;
if (cursor.hidden) {
cursor.a = false;
@@ -1284,7 +1286,7 @@ $._loader = function(vis) {
/** Ask the CGI what APs are visible (async) */
function scanAPs() {
$.get('/wifi/scan', onScan);
$.get('http://'+_root+'/wifi/scan', onScan);
}
function rescan(time) {
@@ -1338,7 +1340,7 @@ $._loader = function(vis) {
var abortTmeo;
function getStatus() {
xhr.open("GET", "/wifi/connstatus");
xhr.open("GET", 'http://'+_root+"/wifi/connstatus");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) {
clearTimeout(abortTmeo);
+4 -3
View File
@@ -70,9 +70,10 @@
bg = inv ? cell.fg : cell.bg;
// Update
e.innerText = (cell.t+' ')[0];
e.style.color = colorHex(fg);
e.style.backgroundColor = colorHex(bg);
e.style.fontWeight = fg > 7 ? 'bold' : 'normal';
e.classList.className = 'fg'+fg+' bg'+bg;
// e.style.color = colorHex(fg);
// e.style.backgroundColor = colorHex(bg);
// e.style.fontWeight = fg > 7 ? 'bold' : 'normal';
}
/** Show entire screen */
+2 -2
View File
@@ -75,7 +75,7 @@
/** Ask the CGI what APs are visible (async) */
function scanAPs() {
$.get('/wifi/scan', onScan);
$.get('http://'+_root+'/wifi/scan', onScan);
}
function rescan(time) {
@@ -129,7 +129,7 @@
var abortTmeo;
function getStatus() {
xhr.open("GET", "/wifi/connstatus");
xhr.open("GET", 'http://'+_root+"/wifi/connstatus");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) {
clearTimeout(abortTmeo);
+24 -1
View File
@@ -103,7 +103,7 @@ h1 {
h2 {
font-size: fsize(2);
margin-bottom: dist(-1);
&:first-child{margin-top:0}
//&:first-child{margin-top:0}
}
td, th {
@@ -153,3 +153,26 @@ tbody td {
opacity:1;
}
}
ul > * {
padding-top: .1em;
padding-bottom: .1em;
}
#botnav {
padding-top: 1.5em;
text-align: center;
a {
padding: 0 dist(-2);
text-decoration: underline;
&, &:visited, &:link {
color: #2e4d6e;
}
&:hover {
color: #5abfff;
}
}
}
+16
View File
@@ -0,0 +1,16 @@
$term-colors:
// 0 black, 1 red, 2 green, 3 yellow
// 4 blue, 5 mag, 6 cyan, 7 white
#111213, #CC0000, #4E9A06, #C4A000,
#3465A4, #75507B, #06989A, #D3D7CF,
// BRIGHT
// 8 black, 9 red, 10 green, 11 yellow
// 12 blue, 13 mag, 14 cyan, 15 white
#555753, #EF2929, #8AE234, #FCE94F,
#729FCF, #AD7FA8, #34E2E2, #EEEEEC;
@for $i from 1 through length($term-colors) {
$c: nth($term-colors, $i);
.fg#{$i - 1} { color: $c; }
.bg#{$i - 1} { background-color: $c; }
}
+20
View File
@@ -39,3 +39,23 @@ $c-form-highlight-a: #2972ba;
// import all our pages
@import "pages/wifi";
@import "pages/term";
@import "pages/about";
@import "term-colors";
// media queries
@include media($tablet-min) {
.mq-phone { display: none; }
}
@include media($phone) {
.mq-tablet-min { display: none; }
}
@include media($normal-min) {
.mq-tablet-max { display: none; }
}
@include media($tablet-max) {
.mq-normal-min { display: none; }
}
+63
View File
@@ -0,0 +1,63 @@
.page-about {
.Box {
padding-left:dist(0);
padding-right:dist(0);
a {font-weight: bold;}
}
#logo {
float:right;
height: 130px;
}
// mobile friendly
#logo2 {
max-width: 100%;
}
td {
white-space: normal;
}
}
.colorprev {
margin-top: dist(-2);
margin-bottom: dist(-2);
span {
display: inline-block;
width: 2em;
padding: dist(-2) 0;
text-align: center;
}
}
.ansiref {
&,td,th{
border: 1px solid #666;
}
th,td {
white-space: normal;
}
th {
background-color: rgba(255,255,255,.1);
}
td:nth-child(1) {
font-family: monospace;
}
&.w100 {
width: 100%;
td:nth-child(1) {
width: 6em;
}
td:nth-child(2) {
width: 8em;
}
}
}
+8 -4
View File
@@ -7,25 +7,29 @@
}
#screen {
@include noselect();
font-family: monospace;
font-size: 16pt;
white-space: nowrap;
background: #111213;
padding: 6px;
display: inline-block;
border: 2px solid #3983CD;//#1bc249;
border: 2px solid #3983CD; //#1bc249;
span {
white-space: pre;
cursor: pointer;
&:hover {
outline: 1px solid rgba(#ffffff,0.4);
@include media($tablet-max) {
outline: 1px solid rgba(#ffffff, 0.4);
@include media($phone) {
outline: 0 none;
}
}
}
.fg8, .fg9, .fg10, .fg11, .fg12, .fg13, .fg14, .fg15 {
font-weight: bold;
}
}
#buttons {
+6
View File
@@ -24,6 +24,12 @@
</div>
</div>
<nav id="botnav">
<a href="/wifi">WiFi config</a><!--
--><a href="/help">Help</a><!--
--><a href="/about">About</a>
</nav>
<script>
_root = window.location.host;
termInit(%screenData%);
+15
View File
@@ -0,0 +1,15 @@
<?php
$f = file_get_contents('term.html');
$f = str_replace('%screenData%',
'{
"w": 26, "h": 10,
"x": 0, "y": 0,
"cv": 1,
"screen": "70 t259"
}', $f);
$f = str_replace('window.location.host', '"192.168.0.15"', $f);
echo $f;
+6
View File
@@ -64,6 +64,12 @@
<div id="ap-list" style="display:none"></div>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/help">Help</a><!--
--><a href="/about">About</a>
</nav>
<div class="Modal hidden" id="psk-modal">
<div class="Dialog">
<form action="/wifi/connect" method="post" id="conn-form">
+13
View File
@@ -0,0 +1,13 @@
<?php
$f = file_get_contents('wifi.html');
$f = str_replace('%StaSSID%', 'Chlivek', $f);
$f = str_replace('%StaIP%', '192.168.0.15', $f);
$f = str_replace('%WiFiModeNum%', '1', $f);
$f = str_replace('%WiFiMode%', 'Client', $f);
$f = str_replace('%WiFiChannel%', '1', $f);
$f = str_replace('window.location.host', '"192.168.0.15"', $f);
echo $f;
+98 -274
View File
@@ -1,212 +1,16 @@
/* #line 1 "user/ansi_parser.rl" */
#include <esp8266.h>
#include "screen.h"
#include "ansi_parser.h"
// Max nr of CSI parameters
#define CSI_N_MAX 3
/**
* \brief Handle fully received CSI ANSI sequence
* \param leadchar - private range leading character, 0 if none
* \param params - array of CSI_N_MAX ints holding the numeric arguments
* \param keychar - the char terminating the sequence
*/
void ICACHE_FLASH_ATTR
handle_CSI(char leadchar, int *params, char keychar)
{
/*
Implemented codes (from Wikipedia)
CSI n A CUU Cursor Up
CSI n B CUD Cursor Down
CSI n C CUF Cursor Forward
CSI n D CUB Cursor Back
CSI n E CNL Cursor Next Line
CSI n F CPL Cursor Previous Line
CSI n G CHA Cursor Horizontal Absolute
CSI n ; m H CUP Cursor Position
CSI n J ED Erase Display
CSI n K EL Erase in Line
CSI n S SU Scroll Up
CSI n T SD Scroll Down
CSI n ; m f HVP Horizontal and Vertical Position
CSI n m SGR Select Graphic Rendition (Implemented only some)
CSI 6n DSR Device Status Report NOT IMPL
CSI s SCP Save Cursor Position
CSI u RCP Restore Cursor Position
CSI ?25l DECTCEM Hides the cursor
CSI ?25h DECTCEM Shows the cursor
*/
int n1 = params[0];
int n2 = params[1];
// int n3 = params[2];
// defaults
switch (keychar) {
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'S':
case 'T':
if (n1 == 0) n1 = 1;
break;
case 'H':
case 'f':
if (n1 == 0) n1 = 1;
if (n2 == 0) n2 = 1;
break;
case 'J':
case 'K':
if (n1 > 2) n1 = 0;
break;
}
switch (keychar) {
// CUU CUD CUF CUB
case 'A': screen_cursor_move(0, -n1); break;
case 'B': screen_cursor_move(0, n1); break;
case 'C': screen_cursor_move(n1, 0); break;
case 'D': screen_cursor_move(-n1, 0); break;
case 'E': // CNL
screen_cursor_move(0, n1);
screen_cursor_set_x(0);
break;
case 'F': // CPL
screen_cursor_move(0, -n1);
screen_cursor_set_x(0);
break;
// CHA
case 'G':
screen_cursor_set_x(n1 - 1); break; // 1-based
// SU, SD
case 'S': screen_scroll_up(n1); break;
case 'T': screen_scroll_down(n1); break;
// CUP,HVP
case 'H':
case 'f':
screen_cursor_set(n2-1, n1-1); break; // 1-based
case 'J': // ED
if (n1 == 0) {
screen_clear(CLEAR_TO_CURSOR);
} else if (n1 == 1) {
screen_clear(CLEAR_FROM_CURSOR);
} else {
screen_clear(CLEAR_ALL);
screen_cursor_set(0, 0);
}
break;
case 'K': // EL
if (n1 == 0) {
screen_clear_line(CLEAR_TO_CURSOR);
} else if (n1 == 1) {
screen_clear_line(CLEAR_FROM_CURSOR);
} else {
screen_clear_line(CLEAR_ALL);
screen_cursor_set_x(0);
}
break;
// SCP, RCP
case 's': screen_cursor_save(); break;
case 'u': screen_cursor_restore(); break;
// DECTCEM cursor show hide
case 'l':
if (leadchar == '?' && n1 == 25) {
screen_cursor_enable(1);
}
break;
case 'h':
if (leadchar == '?' && n1 == 25) {
screen_cursor_enable(0);
}
break;
case 'm': // SGR
// iterate arguments
for (int i = 0; i < CSI_N_MAX; i++) {
int n = params[i];
if (i == 0 && n == 0) { // reset SGR
screen_set_fg(7);
screen_set_bg(0);
break; // cannot combine reset with others
}
else if (n >= 30 && n <= 37) screen_set_fg(n-30); // ANSI normal fg
else if (n >= 40 && n <= 47) screen_set_bg(n-40); // ANSI normal bg
else if (n == 39) screen_set_fg(7); // default fg
else if (n == 49) screen_set_bg(0); // default bg
else if (n == 7) screen_inverse(1); // inverse
else if (n == 27) screen_inverse(0); // positive
else if (n == 1) screen_set_bright_fg(); // ANSI bold = bright fg
else if (n >= 90 && n <= 97) screen_set_fg(n-90+8); // AIX bright fg
else if (n >= 100 && n <= 107) screen_set_bg(n-100+8); // AIX bright bg
}
break;
}
}
/**
* \brief Handle a request to reset the display device
*/
void ICACHE_FLASH_ATTR
handle_RESET_cmd(void)
{
screen_reset();
}
/**
* \brief Handle a received plain character
* \param c - the character
*/
void ICACHE_FLASH_ATTR
handle_plainchar(char c)
{
screen_putchar(c);
}
void ICACHE_FLASH_ATTR
handle_OSC_FactoryReset(void)
{
info("OSC: Factory reset");
warn("NOT IMPLEMENTED");
// TODO
}
void ICACHE_FLASH_ATTR
handle_OSC_SetScreenSize(int rows, int cols)
{
info("OSC: Set screen size to %d x %d", rows, cols);
screen_resize(rows, cols);
}
/* Ragel constants block */
/* #line 206 "user/ansi_parser.c" */
/* #line 9 "user/ansi_parser.c" */
static const char _ansi_actions[] = {
0, 1, 0, 1, 1, 1, 2, 1,
3, 1, 4, 1, 5, 1, 6, 1,
7, 1, 8, 1, 9, 1, 10
7, 1, 8, 1, 9, 1, 10, 1,
11, 1, 12
};
static const char _ansi_eof_actions[] = {
@@ -224,7 +28,7 @@ static const int ansi_en_OSC_body = 5;
static const int ansi_en_main = 1;
/* #line 205 "user/ansi_parser.rl" */
/* #line 8 "user/ansi_parser.rl" */
/**
@@ -261,17 +65,17 @@ ansi_parser(const char *newdata, size_t len)
// Init Ragel on the first run
if (cs == -1) {
/* #line 265 "user/ansi_parser.c" */
/* #line 69 "user/ansi_parser.c" */
{
cs = ansi_start;
}
/* #line 241 "user/ansi_parser.rl" */
/* #line 44 "user/ansi_parser.rl" */
}
// The parser
/* #line 275 "user/ansi_parser.c" */
/* #line 79 "user/ansi_parser.c" */
{
const char *_acts;
unsigned int _nacts;
@@ -288,9 +92,11 @@ case 1:
goto tr0;
case 2:
switch( (*p) ) {
case 91: goto tr3;
case 93: goto tr4;
case 99: goto tr5;
case 55: goto tr3;
case 56: goto tr4;
case 91: goto tr5;
case 93: goto tr6;
case 99: goto tr7;
}
goto tr2;
case 0:
@@ -301,131 +107,135 @@ case 14:
goto tr0;
case 3:
if ( (*p) == 59 )
goto tr8;
goto tr10;
if ( (*p) < 60 ) {
if ( (*p) > 47 ) {
if ( 48 <= (*p) && (*p) <= 57 )
goto tr7;
goto tr9;
} else if ( (*p) >= 32 )
goto tr6;
goto tr8;
} else if ( (*p) > 64 ) {
if ( (*p) > 90 ) {
if ( 97 <= (*p) && (*p) <= 122 )
goto tr9;
goto tr11;
} else if ( (*p) >= 65 )
goto tr9;
goto tr11;
} else
goto tr6;
goto tr8;
goto tr2;
case 4:
if ( (*p) == 59 )
goto tr8;
goto tr10;
if ( (*p) < 65 ) {
if ( 48 <= (*p) && (*p) <= 57 )
goto tr7;
goto tr9;
} else if ( (*p) > 90 ) {
if ( 97 <= (*p) && (*p) <= 122 )
goto tr9;
goto tr11;
} else
goto tr9;
goto tr11;
goto tr2;
case 15:
goto tr2;
case 5:
switch( (*p) ) {
case 70: goto tr10;
case 87: goto tr11;
case 70: goto tr12;
case 87: goto tr13;
}
goto tr2;
case 6:
if ( (*p) == 82 )
goto tr12;
goto tr14;
goto tr2;
case 7:
switch( (*p) ) {
case 7: goto tr13;
case 27: goto tr14;
case 7: goto tr15;
case 27: goto tr16;
}
goto tr2;
case 16:
goto tr2;
case 8:
if ( (*p) == 92 )
goto tr13;
goto tr15;
goto tr2;
case 9:
if ( 48 <= (*p) && (*p) <= 57 )
goto tr15;
goto tr17;
goto tr2;
case 10:
if ( (*p) == 59 )
goto tr16;
goto tr18;
if ( 48 <= (*p) && (*p) <= 57 )
goto tr15;
goto tr17;
goto tr2;
case 11:
if ( 48 <= (*p) && (*p) <= 57 )
goto tr17;
goto tr19;
goto tr2;
case 12:
switch( (*p) ) {
case 7: goto tr18;
case 27: goto tr19;
case 7: goto tr20;
case 27: goto tr21;
}
if ( 48 <= (*p) && (*p) <= 57 )
goto tr17;
goto tr19;
goto tr2;
case 13:
if ( (*p) == 92 )
goto tr18;
goto tr20;
goto tr2;
}
tr2: cs = 0; goto f0;
tr0: cs = 1; goto f1;
tr1: cs = 2; goto _again;
tr6: cs = 4; goto f5;
tr7: cs = 4; goto f6;
tr8: cs = 4; goto f7;
tr10: cs = 6; goto _again;
tr12: cs = 7; goto _again;
tr14: cs = 8; goto _again;
tr11: cs = 9; goto _again;
tr15: cs = 10; goto f6;
tr16: cs = 11; goto f7;
tr17: cs = 12; goto f6;
tr19: cs = 13; goto _again;
tr9: cs = 4; goto f8;
tr10: cs = 4; goto f9;
tr12: cs = 6; goto _again;
tr14: cs = 7; goto _again;
tr16: cs = 8; goto _again;
tr13: cs = 9; goto _again;
tr17: cs = 10; goto f8;
tr18: cs = 11; goto f9;
tr19: cs = 12; goto f8;
tr21: cs = 13; goto _again;
tr3: cs = 14; goto f2;
tr4: cs = 14; goto f3;
tr5: cs = 14; goto f4;
tr9: cs = 15; goto f8;
tr13: cs = 16; goto f9;
tr18: cs = 16; goto f10;
tr6: cs = 14; goto f5;
tr7: cs = 14; goto f6;
tr11: cs = 15; goto f10;
tr15: cs = 16; goto f11;
tr20: cs = 16; goto f12;
f1: _acts = _ansi_actions + 1; goto execFuncs;
f2: _acts = _ansi_actions + 3; goto execFuncs;
f5: _acts = _ansi_actions + 5; goto execFuncs;
f6: _acts = _ansi_actions + 7; goto execFuncs;
f7: _acts = _ansi_actions + 9; goto execFuncs;
f8: _acts = _ansi_actions + 11; goto execFuncs;
f4: _acts = _ansi_actions + 3; goto execFuncs;
f7: _acts = _ansi_actions + 5; goto execFuncs;
f8: _acts = _ansi_actions + 7; goto execFuncs;
f9: _acts = _ansi_actions + 9; goto execFuncs;
f10: _acts = _ansi_actions + 11; goto execFuncs;
f0: _acts = _ansi_actions + 13; goto execFuncs;
f3: _acts = _ansi_actions + 15; goto execFuncs;
f9: _acts = _ansi_actions + 17; goto execFuncs;
f10: _acts = _ansi_actions + 19; goto execFuncs;
f4: _acts = _ansi_actions + 21; goto execFuncs;
f5: _acts = _ansi_actions + 15; goto execFuncs;
f11: _acts = _ansi_actions + 17; goto execFuncs;
f12: _acts = _ansi_actions + 19; goto execFuncs;
f6: _acts = _ansi_actions + 21; goto execFuncs;
f2: _acts = _ansi_actions + 23; goto execFuncs;
f3: _acts = _ansi_actions + 25; goto execFuncs;
execFuncs:
_nacts = *_acts++;
while ( _nacts-- > 0 ) {
switch ( *_acts++ ) {
case 0:
/* #line 251 "user/ansi_parser.rl" */
/* #line 54 "user/ansi_parser.rl" */
{
handle_plainchar((*p));
apars_handle_plainchar((*p));
}
break;
case 1:
/* #line 258 "user/ansi_parser.rl" */
/* #line 61 "user/ansi_parser.rl" */
{
/* Reset the CSI builder */
csi_leading = csi_char = 0;
@@ -440,13 +250,13 @@ execFuncs:
}
break;
case 2:
/* #line 271 "user/ansi_parser.rl" */
/* #line 74 "user/ansi_parser.rl" */
{
csi_leading = (*p);
}
break;
case 3:
/* #line 275 "user/ansi_parser.rl" */
/* #line 78 "user/ansi_parser.rl" */
{
/* x10 + digit */
if (csi_ni < CSI_N_MAX) {
@@ -455,30 +265,30 @@ execFuncs:
}
break;
case 4:
/* #line 282 "user/ansi_parser.rl" */
/* #line 85 "user/ansi_parser.rl" */
{
csi_ni++;
}
break;
case 5:
/* #line 286 "user/ansi_parser.rl" */
/* #line 89 "user/ansi_parser.rl" */
{
csi_char = (*p);
handle_CSI(csi_leading, csi_n, csi_char);
apars_handle_CSI(csi_leading, csi_n, csi_char);
{cs = 1; goto _again;}
}
break;
case 6:
/* #line 294 "user/ansi_parser.rl" */
/* #line 97 "user/ansi_parser.rl" */
{
warn("Invalid escape sequence discarded.");
apars_handle_badseq();
{cs = 1; goto _again;}
}
break;
case 7:
/* #line 311 "user/ansi_parser.rl" */
/* #line 114 "user/ansi_parser.rl" */
{
csi_ni = 0;
@@ -491,29 +301,43 @@ execFuncs:
}
break;
case 8:
/* #line 322 "user/ansi_parser.rl" */
/* #line 125 "user/ansi_parser.rl" */
{
handle_OSC_FactoryReset();
apars_handle_OSC_FactoryReset();
{cs = 1; goto _again;}
}
break;
case 9:
/* #line 327 "user/ansi_parser.rl" */
/* #line 130 "user/ansi_parser.rl" */
{
handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
{cs = 1; goto _again;}
}
break;
case 10:
/* #line 338 "user/ansi_parser.rl" */
/* #line 141 "user/ansi_parser.rl" */
{
// Reset screen
handle_RESET_cmd();
apars_handle_RESET_cmd();
{cs = 1; goto _again;}
}
break;
/* #line 517 "user/ansi_parser.c" */
case 11:
/* #line 147 "user/ansi_parser.rl" */
{
apars_handle_saveCursorAttrs();
{cs = 1; goto _again;}
}
break;
case 12:
/* #line 152 "user/ansi_parser.rl" */
{
apars_handle_restoreCursorAttrs();
{cs = 1; goto _again;}
}
break;
/* #line 341 "user/ansi_parser.c" */
}
}
goto _again;
@@ -531,13 +355,13 @@ _again:
while ( __nacts-- > 0 ) {
switch ( *__acts++ ) {
case 6:
/* #line 294 "user/ansi_parser.rl" */
/* #line 97 "user/ansi_parser.rl" */
{
warn("Invalid escape sequence discarded.");
apars_handle_badseq();
{cs = 1; goto _again;}
}
break;
/* #line 541 "user/ansi_parser.c" */
/* #line 365 "user/ansi_parser.c" */
}
}
}
@@ -545,6 +369,6 @@ _again:
_out: {}
}
/* #line 356 "user/ansi_parser.rl" */
/* #line 171 "user/ansi_parser.rl" */
}
+9
View File
@@ -6,6 +6,15 @@
// Max nr of CSI parameters
#define CSI_N_MAX 3
extern void apars_handle_badseq(void);
extern void apars_handle_CSI(char leadchar, int *params, char keychar);
extern void apars_handle_RESET_cmd(void);
extern void apars_handle_plainchar(char c);
extern void apars_handle_OSC_FactoryReset(void);
extern void apars_handle_OSC_SetScreenSize(int rows, int cols);
extern void apars_handle_saveCursorAttrs(void);
extern void apars_handle_restoreCursorAttrs(void);
/**
* \brief Linear ANSI chars stream parser
*
+19 -204
View File
@@ -1,203 +1,6 @@
#include <esp8266.h>
#include "screen.h"
#include "ansi_parser.h"
// Max nr of CSI parameters
#define CSI_N_MAX 3
/**
* \brief Handle fully received CSI ANSI sequence
* \param leadchar - private range leading character, 0 if none
* \param params - array of CSI_N_MAX ints holding the numeric arguments
* \param keychar - the char terminating the sequence
*/
void ICACHE_FLASH_ATTR
handle_CSI(char leadchar, int *params, char keychar)
{
/*
Implemented codes (from Wikipedia)
CSI n A CUU Cursor Up
CSI n B CUD Cursor Down
CSI n C CUF Cursor Forward
CSI n D CUB Cursor Back
CSI n E CNL Cursor Next Line
CSI n F CPL Cursor Previous Line
CSI n G CHA Cursor Horizontal Absolute
CSI n ; m H CUP Cursor Position
CSI n J ED Erase Display
CSI n K EL Erase in Line
CSI n S SU Scroll Up
CSI n T SD Scroll Down
CSI n ; m f HVP Horizontal and Vertical Position
CSI n m SGR Select Graphic Rendition (Implemented only some)
CSI 6n DSR Device Status Report NOT IMPL
CSI s SCP Save Cursor Position
CSI u RCP Restore Cursor Position
CSI ?25l DECTCEM Hides the cursor
CSI ?25h DECTCEM Shows the cursor
*/
int n1 = params[0];
int n2 = params[1];
// int n3 = params[2];
// defaults
switch (keychar) {
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'S':
case 'T':
if (n1 == 0) n1 = 1;
break;
case 'H':
case 'f':
if (n1 == 0) n1 = 1;
if (n2 == 0) n2 = 1;
break;
case 'J':
case 'K':
if (n1 > 2) n1 = 0;
break;
}
switch (keychar) {
// CUU CUD CUF CUB
case 'A': screen_cursor_move(0, -n1); break;
case 'B': screen_cursor_move(0, n1); break;
case 'C': screen_cursor_move(n1, 0); break;
case 'D': screen_cursor_move(-n1, 0); break;
case 'E': // CNL
screen_cursor_move(0, n1);
screen_cursor_set_x(0);
break;
case 'F': // CPL
screen_cursor_move(0, -n1);
screen_cursor_set_x(0);
break;
// CHA
case 'G':
screen_cursor_set_x(n1 - 1); break; // 1-based
// SU, SD
case 'S': screen_scroll_up(n1); break;
case 'T': screen_scroll_down(n1); break;
// CUP,HVP
case 'H':
case 'f':
screen_cursor_set(n2-1, n1-1); break; // 1-based
case 'J': // ED
if (n1 == 0) {
screen_clear(CLEAR_TO_CURSOR);
} else if (n1 == 1) {
screen_clear(CLEAR_FROM_CURSOR);
} else {
screen_clear(CLEAR_ALL);
screen_cursor_set(0, 0);
}
break;
case 'K': // EL
if (n1 == 0) {
screen_clear_line(CLEAR_TO_CURSOR);
} else if (n1 == 1) {
screen_clear_line(CLEAR_FROM_CURSOR);
} else {
screen_clear_line(CLEAR_ALL);
screen_cursor_set_x(0);
}
break;
// SCP, RCP
case 's': screen_cursor_save(); break;
case 'u': screen_cursor_restore(); break;
// DECTCEM cursor show hide
case 'l':
if (leadchar == '?' && n1 == 25) {
screen_cursor_enable(1);
}
break;
case 'h':
if (leadchar == '?' && n1 == 25) {
screen_cursor_enable(0);
}
break;
case 'm': // SGR
// iterate arguments
for (int i = 0; i < CSI_N_MAX; i++) {
int n = params[i];
if (i == 0 && n == 0) { // reset SGR
screen_set_fg(7);
screen_set_bg(0);
break; // cannot combine reset with others
}
else if (n >= 30 && n <= 37) screen_set_fg(n-30); // ANSI normal fg
else if (n >= 40 && n <= 47) screen_set_bg(n-40); // ANSI normal bg
else if (n == 39) screen_set_fg(7); // default fg
else if (n == 49) screen_set_bg(0); // default bg
else if (n == 7) screen_inverse(1); // inverse
else if (n == 27) screen_inverse(0); // positive
else if (n == 1) screen_set_bright_fg(); // ANSI bold = bright fg
else if (n >= 90 && n <= 97) screen_set_fg(n-90+8); // AIX bright fg
else if (n >= 100 && n <= 107) screen_set_bg(n-100+8); // AIX bright bg
}
break;
}
}
/**
* \brief Handle a request to reset the display device
*/
void ICACHE_FLASH_ATTR
handle_RESET_cmd(void)
{
screen_reset();
}
/**
* \brief Handle a received plain character
* \param c - the character
*/
void ICACHE_FLASH_ATTR
handle_plainchar(char c)
{
screen_putchar(c);
}
void ICACHE_FLASH_ATTR
handle_OSC_FactoryReset(void)
{
info("OSC: Factory reset");
warn("NOT IMPLEMENTED");
// TODO
}
void ICACHE_FLASH_ATTR
handle_OSC_SetScreenSize(int rows, int cols)
{
info("OSC: Set screen size to %d x %d", rows, cols);
screen_resize(rows, cols);
}
/* Ragel constants block */
%%{
machine ansi;
@@ -249,7 +52,7 @@ ansi_parser(const char *newdata, size_t len)
# --- Regular characters to be printed ---
action plain_char {
handle_plainchar(fc);
apars_handle_plainchar(fc);
}
# --- CSI CSI commands (Select Graphic Rendition) ---
@@ -286,13 +89,13 @@ ansi_parser(const char *newdata, size_t len)
action CSI_end {
csi_char = fc;
handle_CSI(csi_leading, csi_n, csi_char);
apars_handle_CSI(csi_leading, csi_n, csi_char);
fgoto main;
}
action errBadSeq {
warn("Invalid escape sequence discarded.");
apars_handle_badseq();
fgoto main;
}
@@ -320,12 +123,12 @@ ansi_parser(const char *newdata, size_t len)
}
action OSC_fr {
handle_OSC_FactoryReset();
apars_handle_OSC_FactoryReset();
fgoto main;
}
action OSC_resize {
handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
fgoto main;
}
@@ -337,7 +140,17 @@ ansi_parser(const char *newdata, size_t len)
action RESET_cmd {
// Reset screen
handle_RESET_cmd();
apars_handle_RESET_cmd();
fgoto main;
}
action CSI_SaveCursorAttrs {
apars_handle_saveCursorAttrs();
fgoto main;
}
action CSI_RestoreCursorAttrs {
apars_handle_restoreCursorAttrs();
fgoto main;
}
@@ -348,7 +161,9 @@ ansi_parser(const char *newdata, size_t len)
(NOESC @plain_char)* ESC (
'[' @CSI_start |
']' @OSC_start |
'c' @RESET_cmd
'c' @RESET_cmd |
'7' @CSI_SaveCursorAttrs |
'8' @CSI_RestoreCursorAttrs
)
)+ $!errBadSeq;
+281
View File
@@ -0,0 +1,281 @@
/**
* ANSI code parser callbacks that do the actual work.
* Split from the parser itself for easier editing without
* having to deal with Ragel.
*/
#include <esp8266.h>
#include "ansi_parser_callbacks.h"
#include "screen.h"
#include "ansi_parser.h"
#include "uart_driver.h"
/**
* Handle a received plain character
*/
void ICACHE_FLASH_ATTR
apars_handle_plainchar(char c)
{
screen_putchar(c);
}
/**
* Bad sequence received, show warning
*/
void apars_handle_badseq(void)
{
warn("Invalid escape sequence discarded.");
}
/**
* \brief Handle fully received CSI ANSI sequence
* \param leadchar - private range leading character, 0 if none
* \param params - array of CSI_N_MAX ints holding the numeric arguments
* \param keychar - the char terminating the sequence
*/
void ICACHE_FLASH_ATTR
apars_handle_CSI(char leadchar, int *params, char keychar)
{
/*
Implemented codes (from Wikipedia)
CSI n A CUU Cursor Up
CSI n B CUD Cursor Down
CSI n C CUF Cursor Forward
CSI n D CUB Cursor Back
CSI n E CNL Cursor Next Line
CSI n F CPL Cursor Previous Line
CSI n G CHA Cursor Horizontal Absolute
CSI n ; m H CUP Cursor Position
CSI n J ED Erase Display
CSI n K EL Erase in Line
CSI n S SU Scroll Up
CSI n T SD Scroll Down
CSI n ; m f HVP Horizontal and Vertical Position
CSI n m SGR Select Graphic Rendition (Implemented only some)
CSI 6n DSR Device Status Report
CSI s SCP Save Cursor Position
CSI u RCP Restore Cursor Position
CSI ?25l DECTCEM Hides the cursor
CSI ?25h DECTCEM Shows the cursor
*/
int n1 = params[0];
int n2 = params[1];
// int n3 = params[2];
// defaults
switch (keychar) {
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'S':
case 'T':
if (n1 == 0) n1 = 1;
break;
case 'H':
case 'f':
if (n1 == 0) n1 = 1;
if (n2 == 0) n2 = 1;
break;
case 'J':
case 'K':
if (n1 > 2) n1 = 0;
break;
}
switch (keychar) {
// CUU CUD CUF CUB
case 'A': screen_cursor_move(-n1, 0); break;
case 'B': screen_cursor_move(n1, 0); break;
case 'C': screen_cursor_move(0, n1); break;
case 'D': screen_cursor_move(0, -n1); break;
case 'E': // CNL
screen_cursor_move(n1, 0);
screen_cursor_set_x(0);
break;
case 'F': // CPL
screen_cursor_move(-n1, 0);
screen_cursor_set_x(0);
break;
// CHA
case 'G':
screen_cursor_set_x(n1 - 1); break; // 1-based
// SU, SD
case 'S': screen_scroll_up(n1); break;
case 'T': screen_scroll_down(n1); break;
// CUP,HVP
case 'H':
case 'f':
screen_cursor_set(n1-1, n2-1); break; // 1-based
case 'J': // ED
if (n1 == 0) {
screen_clear(CLEAR_TO_CURSOR);
} else if (n1 == 1) {
screen_clear(CLEAR_FROM_CURSOR);
} else {
screen_clear(CLEAR_ALL);
screen_cursor_set(0, 0);
}
break;
case 'K': // EL
if (n1 == 0) {
screen_clear_line(CLEAR_TO_CURSOR);
} else if (n1 == 1) {
screen_clear_line(CLEAR_FROM_CURSOR);
} else {
screen_clear_line(CLEAR_ALL);
screen_cursor_set_x(0);
}
break;
// SCP, RCP
case 's': screen_cursor_save(0); break;
case 'u': screen_cursor_restore(0); break;
case 'n':
if (n1 == 6) {
// Query cursor position
char buf[20];
int x, y;
screen_cursor_get(&y, &x);
sprintf(buf, "\033[%d;%dR", y+1, x+1);
UART_WriteString(UART0, buf, UART_TIMEOUT_US);
}
else if (n1 == 5) {
// Query device status - reply "Device is OK"
UART_WriteString(UART0, "\0330n", UART_TIMEOUT_US);
}
break;
// DECTCEM cursor show hide
case 'l':
if (leadchar == '?' && n1 == 25) {
screen_cursor_enable(1);
}
break;
case 'h':
if (leadchar == '?' && n1 == 25) {
screen_cursor_enable(0);
}
break;
case 'm': // SGR
// iterate arguments
for (int i = 0; i < CSI_N_MAX; i++) {
int n = params[i];
if (i == 0 && n == 0) { // reset SGR
screen_set_fg(7);
screen_set_bg(0);
break; // cannot combine reset with others
}
else if (n >= 30 && n <= 37) screen_set_fg(n-30); // ANSI normal fg
else if (n >= 40 && n <= 47) screen_set_bg(n-40); // ANSI normal bg
else if (n == 39) screen_set_fg(7); // default fg
else if (n == 49) screen_set_bg(0); // default bg
else if (n == 7) screen_inverse(1); // inverse
else if (n == 27) screen_inverse(0); // positive
else if (n == 1) screen_set_bright_fg(); // ANSI bold = bright fg
else if (n >= 90 && n <= 97) screen_set_fg(n-90+8); // AIX bright fg
else if (n >= 100 && n <= 107) screen_set_bg(n-100+8); // AIX bright bg
}
break;
}
}
void ICACHE_FLASH_ATTR apars_handle_saveCursorAttrs(void)
{
screen_cursor_save(1);
}
void ICACHE_FLASH_ATTR apars_handle_restoreCursorAttrs(void)
{
screen_cursor_restore(1);
}
/**
* \brief Handle a request to reset the display device
*/
void ICACHE_FLASH_ATTR
apars_handle_RESET_cmd(void)
{
// XXX maybe user wanted to reset the module instead?
screen_reset();
}
/**
* Handle a factory reset request
*/
void ICACHE_FLASH_ATTR
apars_handle_OSC_FactoryReset(void)
{
info("OSC: Factory reset");
// Send acknowledgement message to UART0
// User is performing this manually, so we can just print it as string
UART_WriteString(UART0, "\r\nFACTORY RESET\r\n", UART_TIMEOUT_US);
// Disconnect from AP if connected
int opmode = wifi_get_opmode();
if (opmode != SOFTAP_MODE) {
wifi_station_disconnect();
}
// Both must be enabled so we can manipulate their settings
wifi_set_opmode(STATIONAP_MODE);
// --- AP config ---
struct softap_config apconf;
wifi_softap_get_config(&apconf);
apconf.authmode=AUTH_OPEN; // Disable access protection
apconf.channel=1; // Reset channel; user may have set bad channel in the UI
// generate unique AP name
u8 mac[6];
wifi_get_macaddr(SOFTAP_IF, mac);
sprintf((char*)apconf.ssid, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]);
apconf.ssid_len = (u8)strlen((char*)apconf.ssid);
// --- Station ---
struct station_config staconf;
wifi_station_get_config(&staconf);
// clear info about SSID
staconf.ssid[0]=0;
staconf.bssid_set=0;
staconf.password[0]=0;
wifi_softap_set_config(&apconf);
wifi_station_set_config(&staconf);
UART_WriteString(UART0, "Factory Reset complete, device reset.\r\n\r\n", UART_TIMEOUT_US);
// Reboot to clean STA+AP mode with Channel 1 & reset AP SSID.
system_restart();
}
/**
* Handle a screen resize request
*/
void ICACHE_FLASH_ATTR
apars_handle_OSC_SetScreenSize(int rows, int cols)
{
info("OSC: Set screen size to %d x %d", rows, cols);
screen_resize(rows, cols);
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef ANSI_PARSER_CALLBACKS_H
#define ANSI_PARSER_CALLBACKS_H
void apars_handle_badseq(void);
void apars_handle_CSI(char leadchar, int *params, char keychar);
void apars_handle_RESET_cmd(void);
void apars_handle_plainchar(char c);
void apars_handle_OSC_FactoryReset(void);
void apars_handle_OSC_SetScreenSize(int rows, int cols);
void apars_handle_saveCursorAttrs(void);
void apars_handle_restoreCursorAttrs(void);
#endif //ESP_VT100_FIRMWARE_ANSI_PARSER_CALLBACKS_H
+31
View File
@@ -1,8 +1,13 @@
#include <esp8266.h>
#include <httpd.h>
#include <esp_sdk_ver.h>
#include "cgi_main.h"
#include "screen.h"
#include "user_main.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
/**
* Main page template substitution
@@ -32,3 +37,29 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token
return HTTPD_CGI_DONE;
}
/** "About" page */
httpd_cgi_state ICACHE_FLASH_ATTR tplAbout(HttpdConnData *connData, char *token, void **arg)
{
if (token == NULL) return HTTPD_CGI_DONE;
if (streq(token, "vers_fw")) {
httpdSend(connData, FIRMWARE_VERSION, -1);
}
else if (streq(token, "date")) {
httpdSend(connData, __DATE__, -1);
}
else if (streq(token, "time")) {
httpdSend(connData, __TIME__, -1);
}
else if (streq(token, "vers_httpd")) {
httpdSend(connData, HTTPDVER, -1);
}
else if (streq(token, "vers_sdk")) {
httpdSend(connData, STR(ESP_SDK_VERSION), -1);
}
else if (streq(token, "githubrepo")) {
httpdSend(connData, TERMINAL_GITHUB_REPO, -1);
}
return HTTPD_CGI_DONE;
}
+1
View File
@@ -2,5 +2,6 @@
#define CGI_MAIN_H
httpd_cgi_state tplScreen(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state tplAbout(HttpdConnData *connData, char *token, void **arg);
#endif // CGI_MAIN_H
+56 -18
View File
@@ -6,38 +6,47 @@
#include "uart_driver.h"
#include "screen.h"
#define SOCK_BUF_LEN 2048
static char sock_buff[SOCK_BUF_LEN];
static void notifyTimCb(void *arg) {
void *data = NULL;
for (int i = 0; i < 20; i++) {
httpd_cgi_state cont = screenSerializeToBuffer(sock_buff, SOCK_BUF_LEN, &data);
int flg = 0;
if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE;
if (i > 0) flg |= WEBSOCK_FLAG_CONT;
cgiWebsockBroadcast(URL_WS_UPDATE, sock_buff, (int) strlen(sock_buff), flg);
if (cont == HTTPD_CGI_DONE) break;
}
screenSerializeToBuffer(NULL, SOCK_BUF_LEN, &data);
}
static ETSTimer notifyTim;
/**
* Broadcast screen state to sockets.
* This is a callback for the Screen module,
* called after each visible screen modification.
*/
void ICACHE_FLASH_ATTR screen_notifyChange()
void ICACHE_FLASH_ATTR screen_notifyChange(void)
{
// TODO cooldown / buffering to reduce nr of such events
void *data = NULL;
const int bufsiz = 512;
char buff[bufsiz];
for (int i = 0; i < 20; i++) {
httpd_cgi_state cont = screenSerializeToBuffer(buff, bufsiz, &data);
int flg = 0;
if (cont == HTTPD_CGI_MORE) flg |= WEBSOCK_FLAG_MORE;
if (i > 0) flg |= WEBSOCK_FLAG_CONT;
cgiWebsockBroadcast(URL_WS_UPDATE, buff, (int) strlen(buff), flg);
if (cont == HTTPD_CGI_DONE) break;
}
os_timer_disarm(&notifyTim);
os_timer_setfn(&notifyTim, notifyTimCb, NULL);
os_timer_arm(&notifyTim, 20, 0);
}
/** Socket received a message */
void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
{
char buf[20];
// Add terminator if missing (seems to randomly happen)
data[len] = 0;
dbg("Sock RX str: %s, len %d", data, len);
if (strstarts(data, "STR:")) {
// pass string verbatim
UART_WriteString(UART0, data+4, UART_TIMEOUT_US);
@@ -50,8 +59,37 @@ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags)
}
}
else if (strstarts(data, "TAP:")) {
// TODO
warn("TODO mouse input handling not implemented!");
// this comes in as 0-based
int y=0, x=0;
char *pc=data+4;
char c;
int phase=0;
while((c=*pc++) != '\0') {
if (c==','||c==';') {
phase++;
}
else if (c>='0' && c<='9') {
if (phase==0) {
y=y*10+(c-'0');
} else {
x=x*10+(c-'0');
}
}
}
if (!screen_isCoordValid(y, x)) {
warn("Mouse input at invalid coordinates");
return;
}
dbg("Screen clicked at row %d, col %d", y+1, x+1);
// Send as 1-based to user
sprintf(buf, "\033[%d;%dM", y+1, x+1);
UART_WriteString(UART0, buf, UART_TIMEOUT_US);
}
else {
warn("Bad command.");
+2
View File
@@ -27,6 +27,8 @@ HttpdBuiltInUrl routes[] = {
// --- Web pages ---
ROUTE_TPL_FILE("/", tplScreen, "/term.tpl"),
ROUTE_TPL_FILE("/about", tplAbout, "/about.tpl"),
ROUTE_FILE("/help", "/help.tpl"),
// --- Sockets ---
ROUTE_WS(URL_WS_UPDATE, updateSockConnect),
+81 -26
View File
@@ -10,12 +10,12 @@
#define COLOR_MAX 15
/**
* Screen cell data type
* Screen cell data type (16 bits)
*/
typedef struct {
char c;
Color fg;
Color bg;
typedef struct __attribute__((packed)){
char c : 8;
Color fg : 4;
Color bg : 4;
} Cell;
/**
@@ -27,8 +27,8 @@ static Cell screen[MAX_SCREEN_SIZE];
* Cursor position and attributes
*/
static struct {
Coordinate x; //!< X coordinate
Coordinate y; //!< Y coordinate
int x; //!< X coordinate
int y; //!< Y coordinate
bool visible; //!< Visible
bool inverse; //!< Inverse colors
Color fg; //!< Foreground color for writing
@@ -39,19 +39,25 @@ static struct {
* Saved cursor position, used with the SCP RCP commands
*/
static struct {
Coordinate x;
Coordinate y;
int x;
int y;
// optionally saved attrs
bool withAttrs;
bool inverse;
Color fg;
Color bg;
} cursor_sav;
/**
* Active screen width
*/
static Coordinate W = SCREEN_DEF_W;
static int W = SCREEN_DEF_W;
/**
* Active screen height
*/
static Coordinate H = SCREEN_DEF_H;
static int H = SCREEN_DEF_H;
// XXX volatile is probably not needed
static volatile int notifyLock = 0;
@@ -193,7 +199,7 @@ screen_clear_line(ClearMode mode)
* @param rows - new height
*/
void ICACHE_FLASH_ATTR
screen_resize(Coordinate rows, Coordinate cols)
screen_resize(int rows, int cols)
{
NOTIFY_LOCK();
// sanitize
@@ -277,7 +283,7 @@ done:
* Set cursor position
*/
void ICACHE_FLASH_ATTR
screen_cursor_set(Coordinate x, Coordinate y)
screen_cursor_set(int y, int x)
{
NOTIFY_LOCK();
if (x >= W) x = W - 1;
@@ -287,11 +293,21 @@ screen_cursor_set(Coordinate x, Coordinate y)
NOTIFY_DONE();
}
/**
* Set cursor position
*/
void ICACHE_FLASH_ATTR
screen_cursor_get(int *y, int *x)
{
*x = cursor.x;
*y = cursor.y;
}
/**
* Set cursor X position
*/
void ICACHE_FLASH_ATTR
screen_cursor_set_x(Coordinate x)
screen_cursor_set_x(int x)
{
NOTIFY_LOCK();
if (x >= W) x = W - 1;
@@ -303,7 +319,7 @@ screen_cursor_set_x(Coordinate x)
* Set cursor Y position
*/
void ICACHE_FLASH_ATTR
screen_cursor_set_y(Coordinate y)
screen_cursor_set_y(int y)
{
NOTIFY_LOCK();
if (y >= H) y = H - 1;
@@ -315,7 +331,7 @@ screen_cursor_set_y(Coordinate y)
* Relative cursor move
*/
void ICACHE_FLASH_ATTR
screen_cursor_move(int dx, int dy)
screen_cursor_move(int dy, int dx)
{
NOTIFY_LOCK();
int move;
@@ -344,21 +360,40 @@ screen_cursor_move(int dx, int dy)
* Save the cursor pos
*/
void ICACHE_FLASH_ATTR
screen_cursor_save(void)
screen_cursor_save(bool withAttrs)
{
cursor_sav.x = cursor.x;
cursor_sav.y = cursor.y;
cursor_sav.withAttrs = withAttrs;
if (withAttrs) {
cursor_sav.fg = cursor.fg;
cursor_sav.bg = cursor.bg;
cursor_sav.inverse = cursor.inverse;
} else {
cursor_sav.fg = SCREEN_DEF_FG;
cursor_sav.bg = SCREEN_DEF_BG;
cursor_sav.inverse = 0;
}
}
/**
* Restore the cursor pos
*/
void ICACHE_FLASH_ATTR
screen_cursor_restore(void)
screen_cursor_restore(bool withAttrs)
{
NOTIFY_LOCK();
cursor.x = cursor_sav.x;
cursor.y = cursor_sav.y;
if (withAttrs) {
cursor.fg = cursor_sav.fg;
cursor.bg = cursor_sav.bg;
cursor.inverse = cursor_sav.inverse;
}
NOTIFY_DONE();
}
@@ -431,6 +466,18 @@ screen_set_bright_fg(void)
//endregion
/**
* Check if coords are in range
*
* @param y
* @param x
* @return OK
*/
bool ICACHE_FLASH_ATTR screen_isCoordValid(int y, int x)
{
return x >= 0 && y >= 0 && x < W && y < H;
}
/**
* Set a character in the cursor color, move to right with wrap.
*/
@@ -448,23 +495,31 @@ screen_putchar(char ch)
goto done;
case '\n':
screen_cursor_move(0, 1);
screen_cursor_move(1, 0);
goto done;
case 8: // BS
if (cursor.x > 0) cursor.x--;
if (cursor.x > 0) {
cursor.x--;
} else {
// wrap around start of line
if (cursor.y>0) {
cursor.x=W-1;
cursor.y--;
}
}
// erase target cell
c = &screen[cursor.x + cursor.y * W];
c->c = ' ';
goto done;
case 9: // TAB
c->c = ' ';
// nested recurs >:( but it's ok
screen_putchar(' ');
screen_putchar(' ');
screen_putchar(' ');
screen_putchar(' ');
if (cursor.x<((W-1)-(W-1)%4)) {
c->c = ' ';
do {
screen_putchar(' ');
} while(cursor.x%4!=0);
}
goto done;
default:
+14 -9
View File
@@ -24,7 +24,7 @@
* 8 black, 9 red, 10 green, 11 yellow
* 12 blue, 13 mag, 14 cyan, 15 white
*
* Coordinates are 0-based, left-top is the origin.
* ints are 0-based, left-top is the origin.
* X grows to the right, Y to the bottom.
*
* +---->
@@ -53,7 +53,6 @@ typedef enum {
} ClearMode;
typedef uint8_t Color;
typedef int Coordinate;
httpd_cgi_state ICACHE_FLASH_ATTR
screenSerializeToBuffer(char *buffer, size_t buf_len, void **data);
@@ -62,7 +61,10 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data);
void screen_init(void);
/** Change the screen size */
void screen_resize(Coordinate rows, Coordinate cols);
void screen_resize(int rows, int cols);
/** Check if coord is valid */
bool screen_isCoordValid(int y, int x);
// --- Clearing ---
@@ -84,22 +86,25 @@ void screen_scroll_down(unsigned int lines);
// --- Cursor control ---
/** Set cursor position */
void screen_cursor_set(Coordinate x, Coordinate y);
void screen_cursor_set(int y, int x);
/** Read cursor pos to given vars */
void screen_cursor_get(int *y, int *x);
/** Set cursor X position */
void screen_cursor_set_x(Coordinate x);
void screen_cursor_set_x(int x);
/** Set cursor Y position */
void screen_cursor_set_y(Coordinate y);
void screen_cursor_set_y(int y);
/** Relative cursor move */
void screen_cursor_move(int dx, int dy);
void screen_cursor_move(int dy, int dx);
/** Save the cursor pos */
void screen_cursor_save(void);
void screen_cursor_save(bool withAttrs);
/** Restore the cursor pos */
void screen_cursor_restore(void);
void screen_cursor_restore(bool withAttrs);
/** Enable cursor display */
void screen_cursor_enable(bool enable);
+19 -10
View File
@@ -7,9 +7,9 @@
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
@@ -18,12 +18,12 @@
#include <captdns.h>
#include <espfs.h>
#include <webpages-espfs.h>
#include <cgiflash.h>
#include "serial.h"
#include "io.h"
#include "screen.h"
#include "routes.h"
#define FIRMWARE_VERSION "0.2"
#include "user_main.h"
#ifdef ESPFS_POS
CgiUploadFlashDef uploadParams={
@@ -50,20 +50,29 @@ static ETSTimer prHeapTimer;
/** Periodically show heap usage */
static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg)
{
static uint32_t last = 0;
static int last = 0;
static int cnt = 0;
int heap = system_get_free_heap_size();
int diff = (heap-last);
uint32_t heap = system_get_free_heap_size();
int32_t diff = (heap-last);
const char *cc = "+";
if (diff<0) cc = "";
if (diff == 0) {
dbg("Free heap: %d bytes", heap);
if (cnt == 5) {
// only every 5 secs if no change
dbg("Free heap: %d bytes", heap);
cnt = 0;
}
} else {
// report change
dbg("Free heap: %d bytes (%s%d)", heap, cc, diff);
cnt = 0;
}
last = heap;
cnt++;
}
//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done.
@@ -100,7 +109,7 @@ void ICACHE_FLASH_ATTR user_init(void)
// Heap use timer & blink
os_timer_disarm(&prHeapTimer);
os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL);
os_timer_arm(&prHeapTimer, 5000, 1);
os_timer_arm(&prHeapTimer, 1000, 1);
// The terminal screen
screen_init();
+7
View File
@@ -0,0 +1,7 @@
#ifndef USER_MAIN_H_H
#define USER_MAIN_H_H
#define FIRMWARE_VERSION "0.4"
#define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/esp-vt100-firmware"
#endif //USER_MAIN_H_H