Bringing WiFi to the Cidco Mailstation
at main 246 lines 5.0 kB view raw
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 */ 40const int pData0 = 0; 41const int pData1 = 1; 42const int pData2 = 2; 43const int pData3 = 3; 44const int pData4 = 4; 45const int pData5 = 5; 46const int pData6 = 6; 47const int pData7 = 7; 48 49const int pBusy = 8; /* input from mailstation pin 1 (strobe) */ 50const int pAck = 9; /* input from mailstation pin 14 (linefeed) */ 51 52const int pLineFeed = 10; /* output to mailstation pin 10 (ack) */ 53const int pStrobe = 11; /* output to mailstation pin 11 (busy) */ 54 55MCP23S18 mcp; 56 57/* cache data pin direction */ 58int data_mode = -1; 59 60void 61ms_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 94void 95ms_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 111void 112ms_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 122int 123ms_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 168uint16_t 169ms_status(void) 170{ 171 return mcp.readGPIOAB(); 172} 173 174int 175ms_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 216int 217ms_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 229int 230ms_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 242void 243ms_writedata(char c) 244{ 245 mcp.writeGPIO(0, c); 246}