ESP8266-based WiFi serial modem emulator ROM
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove MailStation code, rename WiFiStation -> WiFiPPP

+17 -1050
+3
.gitmodules
··· 1 + [submodule "lib/Syslog"] 2 + path = lib/Syslog 3 + url = https://github.com/arcao/Syslog.git
-3
GNUmakefile
··· 1 1 # pkg_add makeesparduino 2 2 3 - # targeting the adafruit feather huzzah esp8266 4 - BOARD = huzzah 5 - 6 3 # default of -w supresses all warnings 7 4 COMP_WARNINGS = -Wall -Wextra 8 5
-189
MCP23S18.cpp
··· 1 - /* 2 - * MCP23S18 16-bit I/O Expander 3 - * Copyright (c) 2021 joshua stein <jcs@jcs.org> 4 - * 5 - * Permission to use, copy, modify, and distribute this software for any 6 - * purpose with or without fee is hereby granted, provided that the above 7 - * copyright notice and this permission notice appear in all copies. 8 - * 9 - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 - */ 17 - 18 - #include "MCP23S18.h" 19 - #include <SPI.h> 20 - 21 - /* 22 - .---._.---. 23 - VSS | 1 28 | NC 24 - NC | 2 27 | GPA7 25 - GPB0 | 3 26 | GPA6 26 - GPB1 | 4 25 | GPA5 27 - GPB2 | 5 24 | GPA4 28 - GPB3 | 6 23 | GPA3 29 - GPB4 | 7 22 | GPA2 30 - GPB5 | 8 21 | GPA1 31 - GPB6 | 9 20 | GPA0 32 - GPB7 | 10 19 | INTA 33 - VDD | 11 18 | INTB 34 - CS | 12 17 | NC 35 - SCK | 13 16 | RESET 36 - MOSI | 14 15 | MISO 37 - `---------' 38 - */ 39 - 40 - #define WRITE 0 41 - #define READ 1 42 - 43 - void 44 - MCP23S18::begin(uint8_t _cs_pin) 45 - { 46 - cs_pin = _cs_pin; 47 - ::pinMode(cs_pin, OUTPUT); 48 - ::digitalWrite(cs_pin, HIGH); 49 - 50 - SPI.begin(); 51 - 52 - /* 53 - * Disable Sequential Operation Mode and set BANK=1, but use the IOCON 54 - * register at the address it's at when in BANK=0 as it is at power-on 55 - */ 56 - writeRegister(MCP23S18_IOCON_INIT, 57 - MCP23S18_IOCON_SEQOP | MCP23S18_IOCON_BANK); 58 - } 59 - 60 - uint8_t 61 - MCP23S18::readGPIO(uint8_t bank_b) 62 - { 63 - return readRegister(MCP23S18_GPIO + (bank_b ? MCP23S18_BANKB : 0)); 64 - } 65 - 66 - void 67 - MCP23S18::writeGPIO(uint8_t bank_b, uint8_t val) 68 - { 69 - writeRegister(MCP23S18_GPIO + (bank_b ? MCP23S18_BANKB : 0), val); 70 - } 71 - 72 - uint16_t 73 - MCP23S18::readGPIOAB(void) 74 - { 75 - return (readRegister(MCP23S18_BANKB + MCP23S18_GPIO) << 8) | 76 - readRegister(MCP23S18_GPIO); 77 - } 78 - 79 - void 80 - MCP23S18::updateRegisterBit(uint8_t reg, uint8_t bit, bool set) 81 - { 82 - uint8_t val = readRegister(reg); 83 - 84 - if (set) 85 - val |= (1 << bit); 86 - else 87 - val &= ~(1 << bit); 88 - 89 - writeRegister(reg, val); 90 - } 91 - 92 - void 93 - MCP23S18::pinMode(uint8_t pin, uint8_t dir) 94 - { 95 - updateRegisterBit(regForPin(MCP23S18_IODIR, pin), bitForPin(pin), 96 - dir == INPUT ? MCP23S18_IODIR_INPUT : MCP23S18_IODIR_OUTPUT); 97 - } 98 - 99 - void 100 - MCP23S18::bankPinMode(uint8_t bank, uint8_t dir) 101 - { 102 - writeRegister(MCP23S18_IODIR + (bank ? MCP23S18_BANKB : 0), 103 - dir == OUTPUT ? 0 : 0xff); 104 - } 105 - 106 - uint8_t 107 - MCP23S18::digitalRead(uint8_t pin) 108 - { 109 - uint8_t val = readRegister(regForPin(MCP23S18_GPIO, pin)); 110 - return (val & (1 << bitForPin(pin))) ? HIGH : LOW; 111 - } 112 - 113 - void 114 - MCP23S18::digitalWrite(uint8_t pin, uint8_t level) 115 - { 116 - uint8_t val = readRegister(regForPin(MCP23S18_OLAT, pin)); 117 - 118 - if (level == HIGH) 119 - val |= (1 << bitForPin(pin)); 120 - else 121 - val &= ~(1 << bitForPin(pin)); 122 - 123 - writeRegister(regForPin(MCP23S18_OLAT, pin), val); 124 - } 125 - 126 - void 127 - MCP23S18::pullUp(uint8_t pin, uint8_t level) 128 - { 129 - updateRegisterBit(regForPin(MCP23S18_GPPU, pin), bitForPin(pin), 130 - level == HIGH ? 1 : 0); 131 - } 132 - 133 - void 134 - MCP23S18::bankPullUp(uint8_t bank, uint8_t level) 135 - { 136 - writeRegister(MCP23S18_GPPU + (bank ? MCP23S18_BANKB : 0), 137 - level == HIGH ? 0xff : 0); 138 - } 139 - 140 - uint8_t 141 - MCP23S18::readRegister(uint8_t reg) 142 - { 143 - uint8_t val; 144 - 145 - beginSend(READ); 146 - SPI.transfer(reg); 147 - val = SPI.transfer(0x0); 148 - endSend(); 149 - return val; 150 - } 151 - 152 - void 153 - MCP23S18::writeRegister(uint8_t reg, uint8_t b) 154 - { 155 - beginSend(WRITE); 156 - SPI.transfer(reg); 157 - SPI.transfer(b); 158 - endSend(); 159 - } 160 - 161 - void 162 - MCP23S18::beginSend(uint8_t mode) 163 - { 164 - /* 5Mhz */ 165 - SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE0)); 166 - ::digitalWrite(cs_pin, LOW); 167 - 168 - /* 7-bit address is 0x20, shift in the read/write bit */ 169 - SPI.transfer((0x20 << 1) | mode); 170 - } 171 - 172 - void 173 - MCP23S18::endSend(void) 174 - { 175 - ::digitalWrite(cs_pin, HIGH); 176 - SPI.endTransaction(); 177 - } 178 - 179 - inline uint8_t 180 - MCP23S18::regForPin(uint8_t reg, uint8_t pin) 181 - { 182 - return reg + (pin > MCP23S18_BANKA_MAX_PIN ? MCP23S18_BANKB : 0); 183 - } 184 - 185 - inline uint8_t 186 - MCP23S18::bitForPin(uint8_t pin) 187 - { 188 - return pin % 8; 189 - }
-81
MCP23S18.h
··· 1 - /* 2 - * MCP23S18 16-bit I/O Expander 3 - * Copyright (c) 2021 joshua stein <jcs@jcs.org> 4 - * 5 - * Permission to use, copy, modify, and distribute this software for any 6 - * purpose with or without fee is hereby granted, provided that the above 7 - * copyright notice and this permission notice appear in all copies. 8 - * 9 - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 - */ 17 - 18 - #ifndef _MCP23S018_H_ 19 - #define _MCP23S018_H_ 20 - 21 - #include <Arduino.h> 22 - #include <Wire.h> 23 - 24 - class MCP23S18 { 25 - public: 26 - uint8_t cs_pin; 27 - uint16_t pin_states; 28 - 29 - void begin(uint8_t _cs_pin); 30 - 31 - uint8_t readGPIO(uint8_t bank_b); 32 - uint16_t readGPIOAB(void); 33 - void writeGPIO(uint8_t bank_b, uint8_t val); 34 - 35 - uint8_t readRegister(uint8_t reg); 36 - void writeRegister(uint8_t addr, uint8_t b); 37 - void updateRegisterBit(uint8_t reg, uint8_t bit, bool set); 38 - void pinMode(uint8_t pin, uint8_t dir); 39 - void bankPinMode(uint8_t bank, uint8_t dir); 40 - 41 - uint8_t digitalRead(uint8_t b); 42 - void digitalWrite(uint8_t pin, uint8_t level); 43 - void pullUp(uint8_t pin, uint8_t level); 44 - void bankPullUp(uint8_t bank, uint8_t level); 45 - 46 - private: 47 - inline uint8_t regForPin(uint8_t reg, uint8_t pin); 48 - inline uint8_t bitForPin(uint8_t pin); 49 - void beginSend(uint8_t mode); 50 - void endSend(void); 51 - }; 52 - 53 - /* IOCON.BANK = 0 */ 54 - #define MCP23S18_IOCON_INIT 0x0A 55 - 56 - /* IOCON.BANK = 1 */ 57 - #define MCP23S18_IODIR 0x00 /* I/O DIRECTION */ 58 - #define MCP23S18_IODIR_OUTPUT 0x0 59 - #define MCP23S18_IODIR_INPUT 0x1 60 - #define MCP23S18_IPOL 0x01 /* INPUT POLARITY */ 61 - #define MCP23S18_GPINTEN 0x02 /* INTERRUPT-ON-CHANGE CONTROL */ 62 - #define MCP23S18_DEFVAL 0x03 /* DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE */ 63 - #define MCP23S18_INTCON 0x04 /* INTERRUPT CONTROL */ 64 - #define MCP23S18_IOCON 0x05 /* CONFIGURATION */ 65 - #define MCP23S18_IOCON_INTCC (1 << 0) /* Interrupt Clearing Control */ 66 - #define MCP23S18_IOCON_INTPOL (1 << 1) /* polarity of the INT output */ 67 - #define MCP23S18_IOCON_ODR (1 << 2) /* INT pin as an open-drain output */ 68 - #define MCP23S18_IOCON_SEQOP (1 << 5) /* Sequential Operation mode */ 69 - #define MCP23S18_IOCON_MIRROR (1 << 6) /* INT pins mirror */ 70 - #define MCP23S18_IOCON_BANK (1 << 7) /* how the registers are addressed */ 71 - #define MCP23S18_GPPU 0x06 /* PULL-UP RESISTOR CONFIGURATION */ 72 - #define MCP23S18_INTF 0x07 /* INTERRUPT FLAG */ 73 - #define MCP23S18_INTCAP 0x08 /* INTERRUPT CAPTURE */ 74 - #define MCP23S18_GPIO 0x09 /* PORT */ 75 - #define MCP23S18_OLAT 0x0A /* OUTPUT LATCH */ 76 - 77 - #define MCP23S18_BANKA 0x00 78 - #define MCP23S18_BANKA_MAX_PIN 7 79 - #define MCP23S18_BANKB 0x10 80 - 81 - #endif
-269
http.cpp
··· 1 - /* 2 - * WiFiStation 3 - * Copyright (c) 2021 joshua stein <jcs@jcs.org> 4 - * 5 - * Permission to use, copy, modify, and distribute this software for any 6 - * purpose with or without fee is hereby granted, provided that the above 7 - * copyright notice and this permission notice appear in all copies. 8 - * 9 - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 - */ 17 - 18 - #include <ESP8266WebServer.h> 19 - #include "wifistation.h" 20 - 21 - std::unique_ptr<ESP8266WebServer> http = NULL; 22 - 23 - static const char html_wrapper[] PROGMEM = R"END(<!doctype html> 24 - <html> 25 - <head> 26 - <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 27 - <title>WiFiStation at %s</title> 28 - <style type="text/css"> 29 - body { 30 - background-color: #fffff8; 31 - font-family: sans-serif; 32 - font-size: 12pt; 33 - padding: 1em; 34 - } 35 - h3 { 36 - margin: 0; 37 - } 38 - </style> 39 - </head> 40 - <body> 41 - <h3>WiFiStation %s</h3> 42 - <p> 43 - %s 44 - </body> 45 - </html> 46 - )END"; 47 - 48 - static unsigned int upload_size = 0; 49 - static unsigned int delivered_bytes = 0; 50 - 51 - void 52 - http_process(void) 53 - { 54 - if (!settings->http_server) 55 - return; 56 - 57 - http->handleClient(); 58 - } 59 - 60 - void 61 - http_send_result(int status, bool include_home, const char *body, ...) 62 - { 63 - va_list arg; 64 - const char *accept = http->header("Accept").c_str(); 65 - int len; 66 - char *doc = NULL; 67 - 68 - /* expand body format */ 69 - va_start(arg, body); 70 - len = vasprintf(&doc, body, arg); 71 - va_end(arg); 72 - 73 - if (len == -1) 74 - goto fail; 75 - 76 - /* if Accept header starts with text/html, client prefers html */ 77 - if (accept && strncmp(accept, "text/html", 9) == 0) { 78 - char *tmp; 79 - 80 - /* append home link to body */ 81 - if (include_home) { 82 - len = asprintf(&tmp, "%s" 83 - "<p>" 84 - "<a href=\"/\">Home</a>", doc); 85 - if (len == -1) 86 - goto fail; 87 - free(doc); 88 - doc = tmp; 89 - } 90 - 91 - /* insert body into html template */ 92 - len = asprintf(&tmp, html_wrapper, 93 - WiFi.localIP().toString().c_str(), WIFISTATION_VERSION, 94 - doc); 95 - if (len == -1) 96 - goto fail; 97 - free(doc); 98 - doc = tmp; 99 - 100 - http->send(status, "text/html", doc); 101 - } else { 102 - /* append newline since this is probably going to a terminal */ 103 - doc = (char *)realloc(doc, len + 2); 104 - doc[len] = '\n'; 105 - doc[len + 1] = '\0'; 106 - http->send(status, "text/plain", doc); 107 - } 108 - 109 - free(doc); 110 - return; 111 - 112 - fail: 113 - if (doc != NULL) 114 - free(doc); 115 - http->send(500, "text/plain", "out of memory :("); 116 - return; 117 - } 118 - 119 - void 120 - http_setup(void) 121 - { 122 - if (settings->http_server) { 123 - if (http) 124 - return; 125 - 126 - http.reset(new ESP8266WebServer(80)); 127 - } else { 128 - if (http) 129 - http = NULL; 130 - 131 - return; 132 - } 133 - 134 - http->collectHeaders(F("Accept")); 135 - 136 - http->on("/", HTTP_GET, []() { 137 - http_send_result(200, false, R"END( 138 - <form action="/upload" method="POST" enctype="multipart/form-data"> 139 - <p> 140 - Binary to execute on MailStation (<em>maximum size is %d bytes</em>): 141 - <p> 142 - <input type="file" name="file" maxlength="%d" size="%d"> 143 - <br> 144 - <p> 145 - Ensure the WSLoader application is running on the MailStation ready to accept 146 - the upload. 147 - <p> 148 - <input type="submit" value="Upload"> 149 - </form> 150 - )END", 151 - MAX_UPLOAD_SIZE, MAX_UPLOAD_SIZE, MAX_UPLOAD_SIZE); 152 - }); 153 - 154 - /* 155 - * This measure step is because we don't get the total size of the 156 - * upload until we read the whole thing, but we need to send the file 157 - * size to the MailStation before sending any data. So to avoid 158 - * caching the entire upload, we process the upload and just count the 159 - * bytes being passed through, then send a 307 redirect to the actual 160 - * upload endpoint with the total size. Browsers follow a 307 with a 161 - * POST, so at /upload we'll have the actual size before it sends us 162 - * the same data. 163 - */ 164 - http->on("/upload", HTTP_POST, []() { 165 - http_send_result(400, true, "Failed receiving file."); 166 - }, []() { 167 - HTTPUpload& upload = http->upload(); 168 - char tmp[32]; 169 - 170 - switch (upload.status) { 171 - case UPLOAD_FILE_START: 172 - upload_size = 0; 173 - break; 174 - case UPLOAD_FILE_WRITE: 175 - upload_size += upload.currentSize; 176 - break; 177 - case UPLOAD_FILE_ABORTED: 178 - upload_size = 0; 179 - /* FALLTHROUGH */ 180 - case UPLOAD_FILE_END: 181 - if (upload_size == 0) { 182 - http_send_result(400, true, 183 - "Failed receiving file."); 184 - return; 185 - } 186 - 187 - if (upload_size > MAX_UPLOAD_SIZE) { 188 - http_send_result(400, true, 189 - "File upload cannot be larger than %d " 190 - "bytes.", MAX_UPLOAD_SIZE); 191 - return; 192 - } 193 - 194 - snprintf(tmp, sizeof(tmp), "/upload_measured?size=%d", 195 - upload_size); 196 - http->sendHeader("Location", tmp); 197 - http->send(307); 198 - break; 199 - } 200 - }); 201 - 202 - http->on("/upload_measured", HTTP_POST, []() { 203 - http_send_result(400, true, "Failed receiving file."); 204 - }, []() { 205 - HTTPUpload& upload = http->upload(); 206 - 207 - switch (upload.status) { 208 - case UPLOAD_FILE_START: 209 - delivered_bytes = 0; 210 - 211 - if (!(upload_size = atoi(http->arg("size").c_str()))) { 212 - http_send_result(400, true, "No size " 213 - "parameter passed. Perhaps your browser " 214 - "failed to follow the 307 redirect " 215 - "properly."); 216 - return; 217 - } 218 - 219 - if (upload_size > MAX_UPLOAD_SIZE) { 220 - http_send_result(400, true, 221 - "File upload cannot be larger than %d " 222 - "bytes.", MAX_UPLOAD_SIZE); 223 - return; 224 - } 225 - 226 - if (ms_write(upload_size & 0xff) != 0 || 227 - ms_write((upload_size >> 8) & 0xff) != 0) { 228 - http_send_result(400, true, 229 - "Failed sending size bytes " 230 - "(<tt>0x%x</tt>, <tt>0x%x</tt>) to " 231 - "MailStation. Is the WSLoader program " 232 - "running?", 233 - upload_size & 0xff, 234 - (upload_size >> 8) & 0xff); 235 - return; 236 - } 237 - break; 238 - case UPLOAD_FILE_WRITE: 239 - for (int i = 0; i < (int)upload.currentSize; i++) { 240 - delivered_bytes++; 241 - if (ms_write(upload.buf[i]) == -1) { 242 - http_send_result(400, true, 243 - "Failed uploading to MailStation " 244 - "at byte %d/%d.", 245 - delivered_bytes, upload_size); 246 - return; 247 - } 248 - yield(); 249 - delayMicroseconds(500); 250 - } 251 - break; 252 - case UPLOAD_FILE_END: 253 - http_send_result(200, true, 254 - "Successfully uploaded %d byte%s to MailStation.", 255 - delivered_bytes, delivered_bytes == 1 ? "" : "s"); 256 - return; 257 - case UPLOAD_FILE_ABORTED: 258 - http_send_result(400, true, 259 - "Aborted upload to MailStation."); 260 - return; 261 - } 262 - }); 263 - 264 - http->onNotFound([]() { 265 - http_send_result(404, true, "404"); 266 - }); 267 - 268 - http->begin(); 269 - }
-246
mailstation.cpp
··· 1 - /* 2 - * WiFiStation 3 - * Copyright (c) 2021 joshua stein <jcs@jcs.org> 4 - * 5 - * Permission to use, copy, modify, and distribute this software for any 6 - * purpose with or without fee is hereby granted, provided that the above 7 - * copyright notice and this permission notice appear in all copies. 8 - * 9 - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 - */ 17 - 18 - #include "MCP23S18.h" 19 - #include "wifistation.h" 20 - 21 - /* millis to consider a read/write timed out */ 22 - #define MAILSTATION_TIMEOUT 2500 23 - 24 - /* 25 - * ESP8266 default SPI pins: 26 - * 27 - * GPIO 12: MISO 28 - * GPIO 13: MOSI 29 - * GPIO 14: SCK 30 - * 31 - * Custom pins will be: 32 - * GPIO 4: CS 33 - * GPIO 16: Reset 34 - */ 35 - 36 - #define GPIO_CS 4 37 - #define GPIO_RESET 16 38 - 39 - /* these are pins on the MCP23S18 */ 40 - const int pData0 = 0; 41 - const int pData1 = 1; 42 - const int pData2 = 2; 43 - const int pData3 = 3; 44 - const int pData4 = 4; 45 - const int pData5 = 5; 46 - const int pData6 = 6; 47 - const int pData7 = 7; 48 - 49 - const int pBusy = 8; /* input from mailstation pin 1 (strobe) */ 50 - const int pAck = 9; /* input from mailstation pin 14 (linefeed) */ 51 - 52 - const int pLineFeed = 10; /* output to mailstation pin 10 (ack) */ 53 - const int pStrobe = 11; /* output to mailstation pin 11 (busy) */ 54 - 55 - MCP23S18 mcp; 56 - 57 - /* cache data pin direction */ 58 - int data_mode = -1; 59 - 60 - void 61 - ms_init(void) 62 - { 63 - /* reset the MCP23S18 */ 64 - pinMode(GPIO_RESET, OUTPUT); 65 - digitalWrite(GPIO_RESET, LOW); 66 - delay(100); 67 - digitalWrite(GPIO_RESET, HIGH); 68 - delay(100); 69 - 70 - mcp.begin(GPIO_CS); 71 - 72 - /* data lines will flip between input/output, start in output mode */ 73 - ms_datadir(OUTPUT); 74 - 75 - /* strobe (control) */ 76 - mcp.pinMode(pStrobe, OUTPUT); 77 - mcp.pullUp(pStrobe, HIGH); 78 - mcp.digitalWrite(pStrobe, LOW); 79 - 80 - /* linefeed (control) */ 81 - mcp.pinMode(pLineFeed, OUTPUT); 82 - mcp.pullUp(pLineFeed, HIGH); 83 - mcp.digitalWrite(pLineFeed, LOW); 84 - 85 - /* ack (status) */ 86 - mcp.pinMode(pAck, INPUT); 87 - mcp.pullUp(pAck, LOW); 88 - 89 - /* busy (status) */ 90 - mcp.pinMode(pBusy, INPUT); 91 - mcp.pullUp(pBusy, LOW); 92 - } 93 - 94 - void 95 - ms_setup(void) 96 - { 97 - uint8_t iocon; 98 - 99 - for (int i = 0; i < 10; i++) { 100 - ms_init(); 101 - iocon = mcp.readRegister(MCP23S18_IOCON); 102 - 103 - if (iocon == 0xff || iocon == 0x0) { 104 - error_flash(); 105 - ms_init(); 106 - } else 107 - break; 108 - } 109 - } 110 - 111 - void 112 - ms_datadir(uint8_t which) 113 - { 114 - if (data_mode == which) 115 - return; 116 - 117 - mcp.bankPinMode(0, which); 118 - mcp.bankPullUp(0, which == OUTPUT ? HIGH : LOW); 119 - data_mode = which; 120 - } 121 - 122 - int 123 - ms_read(void) 124 - { 125 - unsigned long t; 126 - char c; 127 - 128 - /* sender raises strobe (busy) to signal a write */ 129 - if (mcp.digitalRead(pBusy) == LOW) 130 - return -1; 131 - 132 - /* but when both lines are high, something's not right */ 133 - if (mcp.digitalRead(pAck) == HIGH) { 134 - /* 135 - * Both pins will be raised during a reboot, so in that case 136 - * stop talking. 137 - */ 138 - mailstation_alive = false; 139 - return -1; 140 - } 141 - 142 - ms_datadir(INPUT); 143 - 144 - /* raise linefeed (ack) to signal ready to receive */ 145 - mcp.digitalWrite(pLineFeed, HIGH); 146 - 147 - /* sender sees raised ack and writes data */ 148 - 149 - /* sender lowers strobe (busy) once data is ready */ 150 - t = millis(); 151 - while (mcp.digitalRead(pBusy) == HIGH) { 152 - if (millis() - t > MAILSTATION_TIMEOUT) { 153 - mcp.digitalWrite(pLineFeed, LOW); 154 - error_flash(); 155 - return -1; 156 - } 157 - ESP.wdtFeed(); 158 - } 159 - 160 - c = mcp.readGPIO(0); 161 - 162 - /* lower linefeed (ack) when we're done reading */ 163 - mcp.digitalWrite(pLineFeed, LOW); 164 - 165 - return c; 166 - } 167 - 168 - uint16_t 169 - ms_status(void) 170 - { 171 - return mcp.readGPIOAB(); 172 - } 173 - 174 - int 175 - ms_write(char c) 176 - { 177 - unsigned long t; 178 - 179 - ms_datadir(OUTPUT); 180 - 181 - /* raise strobe (busy on receiver) to signal write */ 182 - mcp.digitalWrite(pStrobe, HIGH); 183 - 184 - /* wait for receiver to raise ack (linefeed on receiver) */ 185 - t = millis(); 186 - while (mcp.digitalRead(pAck) == LOW && 187 - mcp.digitalRead(pLineFeed) == LOW) { 188 - if (millis() - t > MAILSTATION_TIMEOUT) { 189 - mcp.digitalWrite(pStrobe, LOW); 190 - error_flash(); 191 - mailstation_alive = false; 192 - return -1; 193 - } 194 - ESP.wdtFeed(); 195 - } 196 - 197 - /* ack is high, write data */ 198 - ms_writedata(c); 199 - 200 - /* lower strobe (busy on receiver) to indicate we're done writing */ 201 - mcp.digitalWrite(pStrobe, LOW); 202 - 203 - /* wait for receiver to read and lower ack (busy on their end) */ 204 - t = millis(); 205 - while (mcp.digitalRead(pAck) == HIGH) { 206 - if (millis() - t > MAILSTATION_TIMEOUT) { 207 - error_flash(); 208 - return -1; 209 - } 210 - ESP.wdtFeed(); 211 - } 212 - 213 - return 0; 214 - } 215 - 216 - int 217 - ms_print(String str) 218 - { 219 - size_t len = str.length(); 220 - int ret; 221 - 222 - for (size_t i = 0; i < len; i++) 223 - if ((ret = ms_write(str.charAt(i))) != 0) 224 - return ret; 225 - 226 - return 0; 227 - } 228 - 229 - int 230 - ms_print(char *string) 231 - { 232 - size_t len = strlen(string); 233 - int ret; 234 - 235 - for (size_t i = 0; i < len; i++) 236 - if ((ret = ms_write(string[i])) != 0) 237 - return ret; 238 - 239 - return 0; 240 - } 241 - 242 - void 243 - ms_writedata(char c) 244 - { 245 - mcp.writeGPIO(0, c); 246 - }
+1 -1
telnet.cpp
··· 15 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 16 */ 17 17 18 - #include "wifistation.h" 18 + #include "wifippp.h" 19 19 20 20 WiFiClient telnet; 21 21
+1 -1
update.cpp
··· 16 16 */ 17 17 18 18 #include <WiFiClientSecure.h> 19 - #include "wifistation.h" 19 + #include "wifippp.h" 20 20 21 21 #define OTA_VERSION_URL "https://raw.githubusercontent.com/jcs/WiFiStation/main/release/version.txt" 22 22
+3 -18
util.cpp
··· 15 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 16 */ 17 17 18 - #include "wifistation.h" 18 + #include "wifippp.h" 19 19 20 20 struct eeprom_data *settings; 21 21 22 22 bool serial_alive = true; 23 - bool mailstation_alive = false; 24 23 25 24 void 26 25 setup(void) ··· 33 32 if (memcmp(settings->magic, EEPROM_MAGIC_BYTES, 34 33 sizeof(settings->magic)) == 0) { 35 34 /* do migrations if needed based on current revision */ 36 - switch (settings->revision) { 37 - case 1: 38 - settings->http_server = 0; 39 - /* FALLTHROUGH */ 40 - case 2: 41 - memset(settings->bookmarks, 0, 42 - BOOKMARK_SIZE * NUM_BOOKMARKS); 43 - strcpy(settings->bookmarks[0], "klud.ge"); 44 - } 45 - 46 35 if (settings->revision != EEPROM_REVISION) { 47 36 settings->revision = EEPROM_REVISION; 48 37 EEPROM.commit(); ··· 63 52 settings->telnet_tts_w = 64; 64 53 settings->telnet_tts_h = 15; 65 54 66 - settings->http_server = 0; 55 + memset(settings->bookmarks, 0, BOOKMARK_SIZE * NUM_BOOKMARKS); 56 + strcpy(settings->bookmarks[0], "klud.ge"); 67 57 68 58 EEPROM.commit(); 69 59 } ··· 72 62 delay(1000); 73 63 74 64 led_setup(); 75 - ms_setup(); 76 65 led_reset(); 77 66 78 67 WiFi.mode(WIFI_STA); ··· 82 71 WiFi.disconnect(); 83 72 else 84 73 WiFi.begin(settings->wifi_ssid, settings->wifi_pass); 85 - 86 - http_setup(); 87 74 } 88 75 89 76 void ··· 153 140 if (c == '\n') 154 141 Serial.flush(); 155 142 } 156 - if (mailstation_alive) 157 - ms_write(c); 158 143 159 144 return 0; 160 145 }
+5 -33
wifistation.h wifippp.h
··· 15 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 16 */ 17 17 18 - #ifndef __WIFISTATION_H__ 19 - #define __WIFISTATION_H__ 18 + #ifndef __WIFIPPP_H__ 19 + #define __WIFIPPP_H__ 20 20 21 21 #include <Arduino.h> 22 22 #include <EEPROM.h> ··· 27 27 #define EEPROM_SIZE 512 28 28 struct __attribute((__packed__)) eeprom_data { 29 29 char magic[3]; 30 - #define EEPROM_MAGIC_BYTES "jcs" 30 + #define EEPROM_MAGIC_BYTES "ppp" 31 31 uint8_t revision; 32 - #define EEPROM_REVISION 3 32 + #define EEPROM_REVISION 0 33 33 char wifi_ssid[64]; 34 34 char wifi_pass[64]; 35 35 uint32_t baud; ··· 37 37 uint8_t telnet_tts_w; 38 38 uint8_t telnet_tts_h; 39 39 uint8_t telnet; 40 - uint8_t http_server; 41 40 #define BOOKMARK_SIZE 64 42 41 #define NUM_BOOKMARKS 3 43 42 char bookmarks[NUM_BOOKMARKS][BOOKMARK_SIZE]; ··· 51 50 const int pRedLED = 0; 52 51 const int pBlueLED = 2; 53 52 54 - /* wifistation.ino */ 53 + /* wifippp.ino */ 55 54 void exec_cmd(char *cmd, size_t len); 56 55 extern bool serial_alive; 57 - extern bool mailstation_alive; 58 56 59 57 /* util.cpp */ 60 58 void led_setup(void); ··· 65 63 int output(const char *str); 66 64 int output(String str); 67 65 68 - /* mailstation.cpp */ 69 - extern const int pData0; 70 - extern const int pData1; 71 - extern const int pData2; 72 - extern const int pData3; 73 - extern const int pData4; 74 - extern const int pData5; 75 - extern const int pData6; 76 - extern const int pData7; 77 - extern const int pBusy; 78 - extern const int pAck; 79 - extern const int pLineFeed; 80 - extern const int pStrobe; 81 - void ms_setup(void); 82 - void ms_datadir(uint8_t which); 83 - int ms_read(void); 84 - uint16_t ms_status(void); 85 - int ms_write(char c); 86 - int ms_print(char *string); 87 - int ms_print(String); 88 - void ms_writedata(char c); 89 - 90 66 /* telnet.cpp */ 91 67 int telnet_connect(char *host, uint16_t port); 92 68 bool telnet_connected(void); ··· 94 70 int telnet_read(void); 95 71 int telnet_write(char b); 96 72 int telnet_write(String s); 97 - 98 - /* http.cpp */ 99 - void http_setup(void); 100 - void http_process(void); 101 73 102 74 /* update.cpp */ 103 75 void update_process(bool do_update, bool force);
+4 -209
wifistation.ino wifippp.ino
··· 15 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 16 */ 17 17 18 - #include "wifistation.h" 19 - #include "MCP23S18.h" 18 + #include "wifippp.h" 20 19 21 20 enum { 22 21 STATE_AT, ··· 37 36 int b = -1, i; 38 37 long now = millis(); 39 38 40 - http_process(); 41 - 42 39 switch (state) { 43 40 case STATE_AT: 44 - if ((b = ms_read()) != -1) { 45 - if (!mailstation_alive) { 46 - /* mailstation can only come alive sending 'a' */ 47 - if (b != 'A' && b != 'a') 48 - return; 49 - mailstation_alive = true; 50 - } 51 - } else if (Serial.available() && (b = Serial.read())) 41 + if (Serial.available() && (b = Serial.read())) 52 42 serial_alive = true; 53 43 else 54 44 return; ··· 109 99 case STATE_TELNET: 110 100 b = -1; 111 101 112 - if (mailstation_alive && (b = ms_read()) != -1) { 113 - if (b == '\e') { 114 - /* probably a multi-character command */ 115 - String seq = String((char)b); 116 - unsigned long t = millis(); 117 - 118 - while (millis() - t < 50) { 119 - if ((b = ms_read()) != -1) 120 - seq += (char)b; 121 - } 122 - telnet_write(seq); 123 - plusses = 0; 124 - break; 125 - } 126 - } else if (Serial.available() && (b = Serial.read())) 102 + if (Serial.available() && (b = Serial.read())) 127 103 serial_alive = true; 128 104 129 105 if (b == -1 && plus_wait > 0 && (millis() - plus_wait) >= 500) { ··· 156 132 } 157 133 158 134 if ((b = telnet_read()) != -1) { 159 - if (mailstation_alive) 160 - ms_write(b); 161 135 if (serial_alive) 162 136 Serial.write(b); 163 137 return; ··· 173 147 void 174 148 exec_cmd(char *cmd, size_t len) 175 149 { 176 - unsigned long t; 177 150 char *errstr = NULL; 178 151 179 152 char *lcmd = (char *)malloc(len + 1); ··· 315 288 outputf("DNS server IP: %s\r\n", 316 289 WiFi.dnsIP().toString().c_str()); 317 290 } 318 - outputf("MailStation alive: %s\r\n", 319 - mailstation_alive ? "yes" : "no"); 320 - outputf("HTTP server: %s\r\n", 321 - settings->http_server ? "yes" : "no"); 322 291 for (int i = 0; i < NUM_BOOKMARKS; i++) { 323 292 if (settings->bookmarks[i][0] != '\0') 324 293 outputf("ATDS bookmark %d: %s\r\n", ··· 384 353 case '$': 385 354 /* wifi232 commands */ 386 355 387 - if (strcmp(lcmd, "at$http=0") == 0) { 388 - /* AT$HTTP=0: disable http server */ 389 - settings->http_server = 0; 390 - http_setup(); 391 - output("OK\r\n"); 392 - } else if (strcmp(lcmd, "at$http=1") == 0) { 393 - /* AT$HTTP=1: enable http server */ 394 - settings->http_server = 1; 395 - http_setup(); 396 - output("OK\r\n"); 397 - } else if (strcmp(lcmd, "at$http?") == 0) { 398 - /* AT$HTTP?: show http server setting */ 399 - outputf("%d\r\nOK\r\n", settings->http_server); 400 - } else if (strcmp(lcmd, "at$net=0") == 0) { 356 + if (strcmp(lcmd, "at$net=0") == 0) { 401 357 /* AT$NET=0: disable telnet setting */ 402 358 settings->telnet = 0; 403 359 output("OK\r\n"); ··· 423 379 } else if (strcmp(lcmd, "at$pass?") == 0) { 424 380 /* AT$PASS?: print wep/wpa passphrase */ 425 381 outputf("%s\r\nOK\r\n", settings->wifi_pass); 426 - } else if (strcmp(lcmd, "at$pins?") == 0) { 427 - /* AT$PINS?: watch MCP23S18 lines for debugging */ 428 - uint16_t prev = UINT16_MAX; 429 - int i, done = 0; 430 - unsigned char b, bit, n, data = 0; 431 - extern MCP23S18 mcp; 432 - 433 - ms_datadir(INPUT); 434 - 435 - while (!done) { 436 - ESP.wdtFeed(); 437 - 438 - /* watch for ^C */ 439 - if (Serial.available()) { 440 - switch (b = Serial.read()) { 441 - case 3: 442 - /* ^C */ 443 - done = 1; 444 - break; 445 - case 'd': 446 - Serial.printf("data input\r\n"); 447 - ms_datadir(INPUT); 448 - break; 449 - case 'D': 450 - Serial.printf("data output\r\n"); 451 - ms_datadir(OUTPUT); 452 - break; 453 - case 'L': 454 - Serial.printf("linefeed high\r\n"); 455 - mcp.digitalWrite(pLineFeed, HIGH); 456 - break; 457 - case 'l': 458 - Serial.printf("linefeed low\r\n"); 459 - mcp.digitalWrite(pLineFeed, LOW); 460 - break; 461 - case 'S': 462 - Serial.printf("strobe high\r\n"); 463 - mcp.digitalWrite(pStrobe, HIGH); 464 - break; 465 - case 's': 466 - Serial.printf("strobe low\r\n"); 467 - mcp.digitalWrite(pStrobe, LOW); 468 - break; 469 - case '0': 470 - case '1': 471 - case '2': 472 - case '3': 473 - case '4': 474 - case '5': 475 - case '6': 476 - case '7': 477 - n = (b - '0'); 478 - bit = (data & (1 << n)); 479 - if (bit) 480 - data &= ~(1 << n); 481 - else 482 - data |= (1 << n); 483 - Serial.printf("turning data%d " 484 - "%s (0x%x)\r\n", 485 - n, bit ? "off" : " on", 486 - data); 487 - ms_datadir(OUTPUT); 488 - ms_writedata(data); 489 - break; 490 - } 491 - } 492 - 493 - uint16_t all = ms_status(); 494 - if (all != prev) { 495 - Serial.print("data: "); 496 - for (i = 0; i < 8; i++) 497 - Serial.print((all & (1 << i)) ? 498 - '1' : '0'); 499 - 500 - Serial.print(" status: "); 501 - for (; i < 16; i++) 502 - Serial.print((all & (1 << i)) ? 503 - '1' : '0'); 504 - Serial.print("\r\n"); 505 - prev = all; 506 - } 507 - } 508 - ms_datadir(INPUT); 509 - Serial.print("OK\r\n"); 510 382 } else if (strncmp(lcmd, "at$sb=", 6) == 0) { 511 383 uint32_t baud = 0; 512 384 int chars = 0; ··· 601 473 } else if (strcmp(lcmd, "at$update") == 0) { 602 474 /* AT$UPDATE: do an OTA update */ 603 475 update_process(true, false); 604 - } else if (strncmp(lcmd, "at$upload", 9) == 0) { 605 - /* AT$UPLOAD: mailstation program loader */ 606 - unsigned int bytes = 0; 607 - unsigned char b; 608 - 609 - if (sscanf(lcmd, "at$upload%u", &bytes) != 1 || 610 - bytes < 1) 611 - goto error; 612 - 613 - if (bytes > (MAX_UPLOAD_SIZE - 1)) { 614 - outputf("ERROR size cannot be larger than " 615 - "%d\r\n", (MAX_UPLOAD_SIZE - 1)); 616 - break; 617 - } 618 - 619 - /* 620 - * Prevent output() from sending data to the 621 - * MailStation until we see it on the other side of the 622 - * upload. 623 - */ 624 - mailstation_alive = false; 625 - 626 - /* 627 - * Send low and high bytes of size. 628 - * 629 - * XXX: Tell the MailStation we're sending one more 630 - * byte than we're receiving from sendload so we can 631 - * include one trailing null byte because sometimes the 632 - * final ack of ms_write will fail to see ack line go 633 - * low before WSLoader jumps to the payload. 634 - * Figure out why that happens and remove this hack. 635 - */ 636 - if (ms_write((bytes + 1) & 0xff) != 0 || 637 - ms_write(((bytes + 1) >> 8) & 0xff) != 0) { 638 - output("ERROR MailStation failed to receive " 639 - "size\r\n"); 640 - break; 641 - } 642 - 643 - outputf("OK send your %d byte%s\r\n", bytes, 644 - bytes == 1 ? "" : "s"); 645 - 646 - t = millis(); 647 - int written = 0; 648 - char cksum = 0; 649 - while (bytes > 0) { 650 - if (!Serial.available()) { 651 - if (millis() - t > 5000) 652 - break; 653 - yield(); 654 - continue; 655 - } 656 - 657 - b = Serial.read(); 658 - t = millis(); 659 - 660 - if (ms_write(b) != 0) 661 - break; 662 - 663 - cksum ^= b; 664 - written++; 665 - bytes--; 666 - 667 - if (written % 32 == 0) 668 - output(cksum); 669 - } 670 - 671 - if (bytes == 0) { 672 - output(cksum); 673 - output("\r\nOK good luck\r\n"); 674 - /* XXX: trailing dummy byte, ignore response */ 675 - ms_write(0); 676 - } else 677 - outputf("\r\nERROR MailStation failed to " 678 - "receive byte with %d byte%s left\r\n", 679 - bytes, (bytes == 1 ? "" : "s")); 680 - break; 681 476 } else 682 477 goto error; 683 478 break;