This application will buffer and send lines from up to three serial devices and USB host when connected to via TCP, with telnet or netcat. Written, 02/10/2011-2/14/2011 by Graham Cantin & Special Guest Appearance from Sasha Jevtic (mostly Sasha)
Dependencies: EthernetNetIf MODDMA MODGPS MODSERIAL NTPClient mbed
main.cpp@1:29f6c660d174, 2014-11-06 (annotated)
- Committer:
- kamilion
- Date:
- Thu Nov 06 01:57:54 2014 +0000
- Revision:
- 1:29f6c660d174
- Parent:
- 0:5d5265391846
trying to make telnetserver repo public
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
kamilion | 0:5d5265391846 | 1 | // Telnet UART MUX server |
kamilion | 0:5d5265391846 | 2 | // This application will buffer and send lines from up to |
kamilion | 0:5d5265391846 | 3 | // three serial devices and USB host when connected to via TCP. |
kamilion | 0:5d5265391846 | 4 | // Written, 02/10/2011-2/14/2011 by Graham Cantin and |
kamilion | 0:5d5265391846 | 5 | // Special Guest Appearance from Sasha Jevtic (Patron Diety of Queues) |
kamilion | 0:5d5265391846 | 6 | |
kamilion | 0:5d5265391846 | 7 | /* |
kamilion | 0:5d5265391846 | 8 | "Have you looked on the underside of the mBed?" |
kamilion | 0:5d5265391846 | 9 | There's a chip marked "mBed Interface" which is an 8-bit |
kamilion | 0:5d5265391846 | 10 | microcontroller in it's own right and an AT45DB161 16Mbit flash device. |
kamilion | 0:5d5265391846 | 11 | It's the "backend" of the mBed system you are seeing, |
kamilion | 0:5d5265391846 | 12 | not the target LPC1768 device on the top side of the mBed. |
kamilion | 0:5d5265391846 | 13 | This interface exposes the flash device as a FAT partition over USB, |
kamilion | 0:5d5265391846 | 14 | and also handles resetting the LPC1768 when a serial break occurs. |
kamilion | 0:5d5265391846 | 15 | */ |
kamilion | 0:5d5265391846 | 16 | |
kamilion | 0:5d5265391846 | 17 | // 02/14/2011: Added a hard fault handler. Please improve if you've got a bit of time. |
kamilion | 0:5d5265391846 | 18 | |
kamilion | 0:5d5265391846 | 19 | // ------ Defines ------ |
kamilion | 0:5d5265391846 | 20 | |
kamilion | 0:5d5265391846 | 21 | // DEBUG LOGGING |
kamilion | 0:5d5265391846 | 22 | #define DEBUG |
kamilion | 0:5d5265391846 | 23 | |
kamilion | 0:5d5265391846 | 24 | // DHCP OR STATIC IP? |
kamilion | 0:5d5265391846 | 25 | #define DHCP |
kamilion | 0:5d5265391846 | 26 | |
kamilion | 0:5d5265391846 | 27 | // What port do we listen on? |
kamilion | 0:5d5265391846 | 28 | #define TCP_LISTENING_PORT 3001 |
kamilion | 0:5d5265391846 | 29 | |
kamilion | 0:5d5265391846 | 30 | // 9600, 14400, 19200, 38400, 57600, 115200, 230400, 460800 (too fast), 921600 (way too fast) |
kamilion | 0:5d5265391846 | 31 | #define HOST_BAUD 115200 |
kamilion | 0:5d5265391846 | 32 | #define MODSERIAL_DEFAULT_RX_BUFFER_SIZE 2048 |
kamilion | 0:5d5265391846 | 33 | #define MODSERIAL_DEFAULT_TX_BUFFER_SIZE 384 |
kamilion | 0:5d5265391846 | 34 | #define MESSAGE_BUFFER_SIZE 256 |
kamilion | 0:5d5265391846 | 35 | #define TS_PAD 10 |
kamilion | 0:5d5265391846 | 36 | #define TX_CHUNK 30 |
kamilion | 0:5d5265391846 | 37 | #define MAX_LINES_PER_ITER 10 |
kamilion | 0:5d5265391846 | 38 | #define Q_ALERT_THRESH 70 |
kamilion | 0:5d5265391846 | 39 | |
kamilion | 0:5d5265391846 | 40 | // ------ Includes ------ |
kamilion | 0:5d5265391846 | 41 | |
kamilion | 0:5d5265391846 | 42 | #include "mbed.h" |
kamilion | 0:5d5265391846 | 43 | #include "line_util.h" |
kamilion | 0:5d5265391846 | 44 | #include "EthernetNetIf.h" |
kamilion | 0:5d5265391846 | 45 | #include "TCPSocket.h" |
kamilion | 0:5d5265391846 | 46 | #include "NTPClient.h" |
kamilion | 0:5d5265391846 | 47 | #include "MODDMA.h" |
kamilion | 0:5d5265391846 | 48 | #include "MODSERIAL.h" |
kamilion | 0:5d5265391846 | 49 | #include "GPS.h" |
kamilion | 0:5d5265391846 | 50 | |
kamilion | 0:5d5265391846 | 51 | // ------ Constants ------ |
kamilion | 0:5d5265391846 | 52 | |
kamilion | 0:5d5265391846 | 53 | const char BannerText[] = "**** Software Build Date : "__DATE__" "__TIME__" ****\r\n"; |
kamilion | 0:5d5265391846 | 54 | const char REC_SEP[] = "\r\n"; |
kamilion | 0:5d5265391846 | 55 | |
kamilion | 0:5d5265391846 | 56 | // ------ Reset & Crash ------ |
kamilion | 0:5d5265391846 | 57 | |
kamilion | 0:5d5265391846 | 58 | extern "C" void mbed_reset(); // Ask for soft-reset (like pressing the button) |
kamilion | 0:5d5265391846 | 59 | extern "C" void NVIC_SystemReset(); // Force a hard reset by calling the low level raw function. |
kamilion | 0:5d5265391846 | 60 | |
kamilion | 0:5d5265391846 | 61 | // Handles a hard fault with a reset. SEE: http://mbed.org/forum/mbed/post/2843/ |
kamilion | 0:5d5265391846 | 62 | extern "C" void HardFault_Handler() { printf("HARD FAULT!\r\n"); NVIC_SystemReset(); } |
kamilion | 0:5d5265391846 | 63 | // SEE: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337g/Babcefea.html |
kamilion | 0:5d5265391846 | 64 | // Could be handled better. |
kamilion | 0:5d5265391846 | 65 | // MBED WATCHDOGS: http://mbed.org/forum/mbed/post/3276/ |
kamilion | 0:5d5265391846 | 66 | |
kamilion | 0:5d5265391846 | 67 | |
kamilion | 0:5d5265391846 | 68 | // ------ Timers ----- |
kamilion | 0:5d5265391846 | 69 | // Note that timers are based on 32-bit int microsecond (us) counters, |
kamilion | 0:5d5265391846 | 70 | // so can only time up to a maximum of 2^31-1 microseconds i.e. 30 minutes. |
kamilion | 0:5d5265391846 | 71 | Timer highres; // for reading |
kamilion | 0:5d5265391846 | 72 | |
kamilion | 0:5d5265391846 | 73 | // ------ LEDS ----- |
kamilion | 0:5d5265391846 | 74 | |
kamilion | 0:5d5265391846 | 75 | PwmOut led1(LED1, "led1"); |
kamilion | 0:5d5265391846 | 76 | PwmOut led2(LED2, "led2"); |
kamilion | 0:5d5265391846 | 77 | PwmOut led3(LED3, "led3"); |
kamilion | 0:5d5265391846 | 78 | PwmOut led4(LED4, "led4"); |
kamilion | 0:5d5265391846 | 79 | |
kamilion | 0:5d5265391846 | 80 | // ------ Buttons ----- |
kamilion | 0:5d5265391846 | 81 | |
kamilion | 0:5d5265391846 | 82 | // Define some interrupts for buttons. |
kamilion | 0:5d5265391846 | 83 | InterruptIn P21(p21); |
kamilion | 0:5d5265391846 | 84 | InterruptIn P22(p22); |
kamilion | 0:5d5265391846 | 85 | InterruptIn P23(p23); |
kamilion | 0:5d5265391846 | 86 | InterruptIn P24(p24); |
kamilion | 0:5d5265391846 | 87 | |
kamilion | 0:5d5265391846 | 88 | #define BUTTON_MESSAGE_SEND(x,y) \ |
kamilion | 0:5d5265391846 | 89 | printf("$MBEDE,%d,BUTTON,%02d,%d*FF\r\n",time(NULL),x,y); |
kamilion | 0:5d5265391846 | 90 | |
kamilion | 0:5d5265391846 | 91 | void pin21Rise(void) { BUTTON_MESSAGE_SEND(21, 1); } |
kamilion | 0:5d5265391846 | 92 | void pin21Fall(void) { BUTTON_MESSAGE_SEND(21, 0); } |
kamilion | 0:5d5265391846 | 93 | void pin22Rise(void) { BUTTON_MESSAGE_SEND(22, 1); } |
kamilion | 0:5d5265391846 | 94 | void pin22Fall(void) { BUTTON_MESSAGE_SEND(22, 0); } |
kamilion | 0:5d5265391846 | 95 | void pin23Rise(void) { BUTTON_MESSAGE_SEND(23, 1); } |
kamilion | 0:5d5265391846 | 96 | void pin23Fall(void) { BUTTON_MESSAGE_SEND(23, 0); } |
kamilion | 0:5d5265391846 | 97 | void pin24Rise(void) { BUTTON_MESSAGE_SEND(24, 1); } |
kamilion | 0:5d5265391846 | 98 | void pin24Fall(void) { BUTTON_MESSAGE_SEND(24, 0); } |
kamilion | 0:5d5265391846 | 99 | |
kamilion | 0:5d5265391846 | 100 | |
kamilion | 0:5d5265391846 | 101 | void buttonSetup() { |
kamilion | 0:5d5265391846 | 102 | // Enable pullup resistors on pins. |
kamilion | 0:5d5265391846 | 103 | P21.mode(PullUp); P22.mode(PullUp); P23.mode(PullUp); P24.mode(PullUp); |
kamilion | 0:5d5265391846 | 104 | |
kamilion | 0:5d5265391846 | 105 | // Fix Mbed library bug, see http://mbed.org/forum/bugs-suggestions/topic/1498 |
kamilion | 0:5d5265391846 | 106 | // Prevents pullup interrupts from firing once on below attachment during startup. |
kamilion | 0:5d5265391846 | 107 | LPC_GPIOINT->IO2IntClr = (1UL << 5) | (1UL << 4) | (1UL << 3) | (1UL << 2); |
kamilion | 0:5d5265391846 | 108 | |
kamilion | 0:5d5265391846 | 109 | // Attach InterruptIn pin callbacks. |
kamilion | 0:5d5265391846 | 110 | P21.rise(&pin21Rise); P21.fall(&pin21Fall); |
kamilion | 0:5d5265391846 | 111 | P22.rise(&pin22Rise); P22.fall(&pin22Fall); |
kamilion | 0:5d5265391846 | 112 | P23.rise(&pin23Rise); P23.fall(&pin23Fall); |
kamilion | 0:5d5265391846 | 113 | P24.rise(&pin24Rise); P24.fall(&pin24Fall); |
kamilion | 0:5d5265391846 | 114 | } |
kamilion | 0:5d5265391846 | 115 | |
kamilion | 0:5d5265391846 | 116 | // ------ Ethernet ----- |
kamilion | 0:5d5265391846 | 117 | |
kamilion | 0:5d5265391846 | 118 | #ifdef DHCP |
kamilion | 0:5d5265391846 | 119 | EthernetNetIf eth; // Use DHCP from router |
kamilion | 0:5d5265391846 | 120 | #else |
kamilion | 0:5d5265391846 | 121 | EthernetNetIf eth( |
kamilion | 0:5d5265391846 | 122 | IpAddr(10,4,0,212), //IP Address |
kamilion | 0:5d5265391846 | 123 | IpAddr(255,255,255,0), //Network Mask |
kamilion | 0:5d5265391846 | 124 | IpAddr(10,4,0,1), //Gateway |
kamilion | 0:5d5265391846 | 125 | IpAddr(10,0,0,7) //DNS |
kamilion | 0:5d5265391846 | 126 | ); // Define a static IP to run with for early crossover cable debugging. |
kamilion | 0:5d5265391846 | 127 | #endif |
kamilion | 0:5d5265391846 | 128 | |
kamilion | 0:5d5265391846 | 129 | NTPClient ntp; // Object for the ntpclient |
kamilion | 0:5d5265391846 | 130 | |
kamilion | 0:5d5265391846 | 131 | void rtcSetup() { |
kamilion | 0:5d5265391846 | 132 | Host server(IpAddr(), 123, "0.us.pool.ntp.org"); |
kamilion | 0:5d5265391846 | 133 | ntp.setTime(server); |
kamilion | 0:5d5265391846 | 134 | time_t ctTime; |
kamilion | 0:5d5265391846 | 135 | ctTime = time(NULL); |
kamilion | 0:5d5265391846 | 136 | printf("$MBEDE,%d,TIME_SET_NTP,%s",time(NULL), ctime(&ctTime)); |
kamilion | 0:5d5265391846 | 137 | } |
kamilion | 0:5d5265391846 | 138 | |
kamilion | 0:5d5265391846 | 139 | Host client; // a Host (IP address) object named client; the one connecting to us. |
kamilion | 0:5d5265391846 | 140 | TCPSocket ListeningSock; // Actual listening socket |
kamilion | 0:5d5265391846 | 141 | TCPSocket* pConnectedSock; // pointer for ConnectedSock |
kamilion | 0:5d5265391846 | 142 | TCPSocketErr err; // Error slot for TCP Socket |
kamilion | 0:5d5265391846 | 143 | |
kamilion | 0:5d5265391846 | 144 | bool clientExists = 0; |
kamilion | 0:5d5265391846 | 145 | |
kamilion | 0:5d5265391846 | 146 | void onConnectedTCPSocketEvent(TCPSocketEvent e) { |
kamilion | 0:5d5265391846 | 147 | switch(e) { |
kamilion | 0:5d5265391846 | 148 | case TCPSOCKET_WRITEABLE: |
kamilion | 0:5d5265391846 | 149 | // printf("$MBEDE,%d,IP,CONNECTED,TCP_SOCKET_SENDING_DATA*FF\r\n",time(NULL)); |
kamilion | 0:5d5265391846 | 150 | led1 = 1.0; // Blink an xmit |
kamilion | 0:5d5265391846 | 151 | led1 = 0.10; |
kamilion | 0:5d5265391846 | 152 | break; |
kamilion | 0:5d5265391846 | 153 | case TCPSOCKET_READABLE: |
kamilion | 0:5d5265391846 | 154 | printf("$MBEDE,%d,IP,CONNECTED,TCP_SOCKET_READABLE*FF\r\n",time(NULL)); |
kamilion | 0:5d5265391846 | 155 | led2 = 1.0; // Blink a recv |
kamilion | 0:5d5265391846 | 156 | led2 = 0.10; |
kamilion | 0:5d5265391846 | 157 | break; |
kamilion | 0:5d5265391846 | 158 | // The following cases generally should not happen often, but are explicitly covered anyway |
kamilion | 0:5d5265391846 | 159 | case TCPSOCKET_CONTIMEOUT: |
kamilion | 0:5d5265391846 | 160 | case TCPSOCKET_CONRST: |
kamilion | 0:5d5265391846 | 161 | case TCPSOCKET_CONABRT: |
kamilion | 0:5d5265391846 | 162 | case TCPSOCKET_ERROR: |
kamilion | 0:5d5265391846 | 163 | case TCPSOCKET_DISCONNECTED: |
kamilion | 0:5d5265391846 | 164 | printf("$MBEDE,%d,IP,CONNECTED,TCP_SOCKET_DISCONNECTED*FF\r\n",time(NULL)); |
kamilion | 0:5d5265391846 | 165 | pConnectedSock->close(); |
kamilion | 0:5d5265391846 | 166 | clientExists = 0; |
kamilion | 0:5d5265391846 | 167 | SLine_put_control(false); |
kamilion | 0:5d5265391846 | 168 | SLine_clear(); |
kamilion | 0:5d5265391846 | 169 | led4 = 0.0; // Shut off LED4 (Connected) |
kamilion | 0:5d5265391846 | 170 | led1 = 0.0; // Shut off LED1 (Xmit) |
kamilion | 0:5d5265391846 | 171 | break; |
kamilion | 0:5d5265391846 | 172 | default: |
kamilion | 0:5d5265391846 | 173 | break; |
kamilion | 0:5d5265391846 | 174 | } |
kamilion | 0:5d5265391846 | 175 | } |
kamilion | 0:5d5265391846 | 176 | |
kamilion | 0:5d5265391846 | 177 | |
kamilion | 0:5d5265391846 | 178 | void onListeningTCPSocketEvent(TCPSocketEvent e) { |
kamilion | 0:5d5265391846 | 179 | switch(e) { |
kamilion | 0:5d5265391846 | 180 | case TCPSOCKET_ACCEPT: |
kamilion | 0:5d5265391846 | 181 | printf("$MBEDE,%d,IP,LISTENING,TCP_SOCKET_ACCEPTED*FF\r\n",time(NULL)); |
kamilion | 0:5d5265391846 | 182 | // Accepts connection from client and gets connected socket. |
kamilion | 0:5d5265391846 | 183 | err=ListeningSock.accept(&client, &pConnectedSock); |
kamilion | 0:5d5265391846 | 184 | if (err) { |
kamilion | 0:5d5265391846 | 185 | printf("$MBEDE,%d,IP,onListeningTcpSocketEvent,Could not accept connection*FF\r\n",time(NULL)); |
kamilion | 0:5d5265391846 | 186 | return; //Error in accept, discard connection (nmap/netcat -z portscanning us?) |
kamilion | 0:5d5265391846 | 187 | } |
kamilion | 0:5d5265391846 | 188 | // Setup the new socket events |
kamilion | 0:5d5265391846 | 189 | pConnectedSock->setOnEvent(&onConnectedTCPSocketEvent); |
kamilion | 0:5d5265391846 | 190 | // We can find out where the connection is coming by looking at the |
kamilion | 0:5d5265391846 | 191 | // Host parameter of the accept() method |
kamilion | 0:5d5265391846 | 192 | IpAddr clientIp = client.getIp(); |
kamilion | 0:5d5265391846 | 193 | printf("$MBEDE,%d,IP,LISTENING,TCP_CONNECT_FROM,%d.%d.%d.%d*FF\r\n",time(NULL), |
kamilion | 0:5d5265391846 | 194 | clientIp[0], clientIp[1], clientIp[2], clientIp[3]); |
kamilion | 0:5d5265391846 | 195 | clientExists = 1; // Mark ourselves connected. |
kamilion | 0:5d5265391846 | 196 | SLine_clear(); |
kamilion | 0:5d5265391846 | 197 | SLine_put_control(true); |
kamilion | 0:5d5265391846 | 198 | led4 = 0.10; // Turn on LED4 (Connected) |
kamilion | 0:5d5265391846 | 199 | break; |
kamilion | 0:5d5265391846 | 200 | // The following cases generally should not happen often, but are explicitly covered anyway |
kamilion | 0:5d5265391846 | 201 | case TCPSOCKET_CONRST: |
kamilion | 0:5d5265391846 | 202 | case TCPSOCKET_CONTIMEOUT: |
kamilion | 0:5d5265391846 | 203 | case TCPSOCKET_CONABRT: |
kamilion | 0:5d5265391846 | 204 | case TCPSOCKET_ERROR: |
kamilion | 0:5d5265391846 | 205 | case TCPSOCKET_DISCONNECTED: |
kamilion | 0:5d5265391846 | 206 | // Close the socket (nmap/netcat -z portscanning us?) |
kamilion | 0:5d5265391846 | 207 | printf("$MBEDE,%d,IP,LISTENING,TCP_SOCKET_DISCONNECTED*FF\r\n",time(NULL)); |
kamilion | 0:5d5265391846 | 208 | ListeningSock.close(); |
kamilion | 0:5d5265391846 | 209 | clientExists = 0; |
kamilion | 0:5d5265391846 | 210 | SLine_put_control(false); |
kamilion | 0:5d5265391846 | 211 | SLine_clear(); |
kamilion | 0:5d5265391846 | 212 | led4 = 0.0; // Shut off LED4 (Connected) |
kamilion | 0:5d5265391846 | 213 | break; |
kamilion | 0:5d5265391846 | 214 | default: |
kamilion | 0:5d5265391846 | 215 | break; |
kamilion | 0:5d5265391846 | 216 | }; |
kamilion | 0:5d5265391846 | 217 | } |
kamilion | 0:5d5265391846 | 218 | |
kamilion | 0:5d5265391846 | 219 | // ------ Serial ----- |
kamilion | 0:5d5265391846 | 220 | |
kamilion | 0:5d5265391846 | 221 | // Set up the UARTs with MODSERIAL buffering |
kamilion | 0:5d5265391846 | 222 | MODSERIAL hostPort(USBTX, USBRX); |
kamilion | 0:5d5265391846 | 223 | MODSERIAL gpsPort(p13, p14); |
kamilion | 0:5d5265391846 | 224 | MODSERIAL imuPort(p9, p10); |
kamilion | 0:5d5265391846 | 225 | MODSERIAL wheelPort(p28, p27); |
kamilion | 0:5d5265391846 | 226 | |
kamilion | 0:5d5265391846 | 227 | void captureSerial(MODSERIAL* serial, LINE_T* line, LINE_SRC_T src) { |
kamilion | 0:5d5265391846 | 228 | memset(line->line, 0, LINE_MAX_LEN + 1); // Empty out buffer first |
kamilion | 0:5d5265391846 | 229 | line->source = src; // Enumerate source from line_util.h |
kamilion | 0:5d5265391846 | 230 | line->usec = highres.read_us(); // Read highres timer |
kamilion | 0:5d5265391846 | 231 | serial->move(line->line, LINE_MAX_LEN); // Move the buffer into the struct member |
kamilion | 0:5d5265391846 | 232 | line->len = strip_crlf(line->line); // Strip the line of endings of the struct member |
kamilion | 0:5d5265391846 | 233 | line->timestamp = time(NULL); // Read RTC time |
kamilion | 0:5d5265391846 | 234 | } |
kamilion | 0:5d5265391846 | 235 | |
kamilion | 0:5d5265391846 | 236 | void generateOutput(TCPSocket* socket, LINE_T* line) { |
kamilion | 0:5d5265391846 | 237 | char tx_buf[MESSAGE_BUFFER_SIZE]; |
kamilion | 0:5d5265391846 | 238 | unsigned int tx_length; // total bytes to be sent. |
kamilion | 0:5d5265391846 | 239 | int bytes_sent; // total bytes sent so far in function. |
kamilion | 0:5d5265391846 | 240 | int send_rc; // result of last send operation. |
kamilion | 0:5d5265391846 | 241 | unsigned int send_chunk; // amount attempted for tx operation. |
kamilion | 0:5d5265391846 | 242 | unsigned int consec_zeros; // consecutive socket send ops returning 0. |
kamilion | 0:5d5265391846 | 243 | |
kamilion | 0:5d5265391846 | 244 | if (line->len > 0) { |
kamilion | 0:5d5265391846 | 245 | |
kamilion | 0:5d5265391846 | 246 | // Though not strictly necessary, life is just easier if we preformat |
kamilion | 0:5d5265391846 | 247 | // the data we want to send by socket. |
kamilion | 0:5d5265391846 | 248 | |
kamilion | 0:5d5265391846 | 249 | tx_length = snprintf(tx_buf, MESSAGE_BUFFER_SIZE, |
kamilion | 0:5d5265391846 | 250 | "%s,%010u,%010u,%s%s", |
kamilion | 0:5d5265391846 | 251 | LINE_SRC_NAMES[line->source], |
kamilion | 0:5d5265391846 | 252 | line->timestamp, |
kamilion | 0:5d5265391846 | 253 | line->usec, |
kamilion | 0:5d5265391846 | 254 | line->line, |
kamilion | 0:5d5265391846 | 255 | REC_SEP); |
kamilion | 0:5d5265391846 | 256 | bytes_sent = 0; |
kamilion | 0:5d5265391846 | 257 | send_rc = 0; |
kamilion | 0:5d5265391846 | 258 | consec_zeros = 0; |
kamilion | 0:5d5265391846 | 259 | printf(tx_buf); |
kamilion | 0:5d5265391846 | 260 | |
kamilion | 0:5d5265391846 | 261 | // Traditional socket transmission APIs don't guarantee that |
kamilion | 0:5d5265391846 | 262 | // everything we ask to send will get sent. Details on the mbed |
kamilion | 0:5d5265391846 | 263 | // implementation are light, but it is wise to take this likelihood |
kamilion | 0:5d5265391846 | 264 | // into consideration. Socket buffer capacity is certainly not |
kamilion | 0:5d5265391846 | 265 | // infinite, and can fill for a variety of reasons. |
kamilion | 0:5d5265391846 | 266 | |
kamilion | 0:5d5265391846 | 267 | while ((send_rc >= 0) && ((tx_length - bytes_sent) > 0)) { |
kamilion | 0:5d5265391846 | 268 | |
kamilion | 0:5d5265391846 | 269 | // We'll go at it until we're out of stuff to send or we get an |
kamilion | 0:5d5265391846 | 270 | // error when we send (traditional socket transmission APIs |
kamilion | 0:5d5265391846 | 271 | // return -1 when encountering an error. |
kamilion | 0:5d5265391846 | 272 | |
kamilion | 0:5d5265391846 | 273 | send_chunk = ((tx_length - bytes_sent) > TX_CHUNK ? |
kamilion | 0:5d5265391846 | 274 | TX_CHUNK : tx_length - bytes_sent); |
kamilion | 0:5d5265391846 | 275 | |
kamilion | 0:5d5265391846 | 276 | // We use this notion of chunking to go easy on the network |
kamilion | 0:5d5265391846 | 277 | // stack, never trying to transmit a large amount of data |
kamilion | 0:5d5265391846 | 278 | // in a single call. |
kamilion | 0:5d5265391846 | 279 | |
kamilion | 0:5d5265391846 | 280 | send_rc = pConnectedSock->send(&(tx_buf[bytes_sent]), |
kamilion | 0:5d5265391846 | 281 | send_chunk); |
kamilion | 0:5d5265391846 | 282 | bytes_sent += (send_rc > 0 ? send_rc : 0); |
kamilion | 0:5d5265391846 | 283 | |
kamilion | 0:5d5265391846 | 284 | // If we try to send too much and not poll often enough, the |
kamilion | 0:5d5265391846 | 285 | // network stack will stop being able to accept data. So, we'll |
kamilion | 0:5d5265391846 | 286 | // poll here since this is a really intense area of transmission. |
kamilion | 0:5d5265391846 | 287 | |
kamilion | 0:5d5265391846 | 288 | Net::poll(); |
kamilion | 0:5d5265391846 | 289 | |
kamilion | 0:5d5265391846 | 290 | if (consec_zeros && (send_rc > 0)) { |
kamilion | 0:5d5265391846 | 291 | printf("### Recovery: First non-zero send after %06u tries " |
kamilion | 0:5d5265391846 | 292 | "(%04d/%04u). ###\r\n", |
kamilion | 0:5d5265391846 | 293 | consec_zeros, send_rc, send_chunk); |
kamilion | 0:5d5265391846 | 294 | printf("Serial data line queue utilization: %u/%u.\r\n", |
kamilion | 0:5d5265391846 | 295 | SLines_get_fill(), SLines_get_capacity()); |
kamilion | 0:5d5265391846 | 296 | consec_zeros = 0; |
kamilion | 0:5d5265391846 | 297 | } |
kamilion | 0:5d5265391846 | 298 | |
kamilion | 0:5d5265391846 | 299 | if (send_rc < send_chunk) { |
kamilion | 0:5d5265391846 | 300 | |
kamilion | 0:5d5265391846 | 301 | if (!consec_zeros) { |
kamilion | 0:5d5265391846 | 302 | printf("### Send result: %04d/%04u. ###\r\n", |
kamilion | 0:5d5265391846 | 303 | send_rc, send_chunk); |
kamilion | 0:5d5265391846 | 304 | printf("Serial data line queue utilization: %u/%u.\r\n", |
kamilion | 0:5d5265391846 | 305 | SLines_get_fill(), SLines_get_capacity()); |
kamilion | 0:5d5265391846 | 306 | } |
kamilion | 0:5d5265391846 | 307 | |
kamilion | 0:5d5265391846 | 308 | if (send_rc <= 0) { |
kamilion | 0:5d5265391846 | 309 | consec_zeros++; |
kamilion | 0:5d5265391846 | 310 | } |
kamilion | 0:5d5265391846 | 311 | |
kamilion | 0:5d5265391846 | 312 | } |
kamilion | 0:5d5265391846 | 313 | |
kamilion | 0:5d5265391846 | 314 | } |
kamilion | 0:5d5265391846 | 315 | |
kamilion | 0:5d5265391846 | 316 | } |
kamilion | 0:5d5265391846 | 317 | |
kamilion | 0:5d5265391846 | 318 | } |
kamilion | 0:5d5265391846 | 319 | |
kamilion | 0:5d5265391846 | 320 | // ------ Serial ISRs ----- |
kamilion | 0:5d5265391846 | 321 | |
kamilion | 0:5d5265391846 | 322 | void gpsMessageReceive(void) { |
kamilion | 0:5d5265391846 | 323 | LINE_T new_line; |
kamilion | 0:5d5265391846 | 324 | |
kamilion | 0:5d5265391846 | 325 | captureSerial(&gpsPort, &new_line, LINE_SRC_GPS); |
kamilion | 0:5d5265391846 | 326 | SLine_put(&new_line); |
kamilion | 0:5d5265391846 | 327 | } |
kamilion | 0:5d5265391846 | 328 | |
kamilion | 0:5d5265391846 | 329 | void imuMessageReceive(void) { |
kamilion | 0:5d5265391846 | 330 | LINE_T new_line; |
kamilion | 0:5d5265391846 | 331 | |
kamilion | 0:5d5265391846 | 332 | captureSerial(&imuPort, &new_line, LINE_SRC_IMU); |
kamilion | 0:5d5265391846 | 333 | SLine_put(&new_line); |
kamilion | 0:5d5265391846 | 334 | } |
kamilion | 0:5d5265391846 | 335 | |
kamilion | 0:5d5265391846 | 336 | void wheelMessageReceive(void) { |
kamilion | 0:5d5265391846 | 337 | LINE_T new_line; |
kamilion | 0:5d5265391846 | 338 | |
kamilion | 0:5d5265391846 | 339 | captureSerial(&wheelPort, &new_line, LINE_SRC_WHEEL); |
kamilion | 0:5d5265391846 | 340 | SLine_put(&new_line); |
kamilion | 0:5d5265391846 | 341 | } |
kamilion | 0:5d5265391846 | 342 | |
kamilion | 0:5d5265391846 | 343 | void hostMessageReceive(void) { |
kamilion | 0:5d5265391846 | 344 | LINE_T new_line; |
kamilion | 0:5d5265391846 | 345 | |
kamilion | 0:5d5265391846 | 346 | captureSerial(&hostPort, &new_line, LINE_SRC_HOST); |
kamilion | 0:5d5265391846 | 347 | SLine_put(&new_line); |
kamilion | 0:5d5265391846 | 348 | } |
kamilion | 0:5d5265391846 | 349 | |
kamilion | 0:5d5265391846 | 350 | void processSerialQueues() { |
kamilion | 0:5d5265391846 | 351 | unsigned int cur_fill; |
kamilion | 0:5d5265391846 | 352 | unsigned int lines_handled; |
kamilion | 0:5d5265391846 | 353 | |
kamilion | 0:5d5265391846 | 354 | cur_fill = SLines_get_fill(); |
kamilion | 0:5d5265391846 | 355 | lines_handled = 0; |
kamilion | 0:5d5265391846 | 356 | |
kamilion | 0:5d5265391846 | 357 | while ((cur_fill > 0) && (lines_handled < MAX_LINES_PER_ITER)) { |
kamilion | 0:5d5265391846 | 358 | |
kamilion | 0:5d5265391846 | 359 | // It's tempting to just process every line we have while we're in |
kamilion | 0:5d5265391846 | 360 | // here. However, since we're in a super-looped environment, network |
kamilion | 0:5d5265391846 | 361 | // I/O is completed via polling, and it seems prudent to not jam too |
kamilion | 0:5d5265391846 | 362 | // much down the stack between polling operations. |
kamilion | 0:5d5265391846 | 363 | |
kamilion | 0:5d5265391846 | 364 | if (((cur_fill * 100) / SLines_get_capacity()) >= Q_ALERT_THRESH) { |
kamilion | 0:5d5265391846 | 365 | printf("Serial data line queue utilization: %u/%u.\r\n", |
kamilion | 0:5d5265391846 | 366 | cur_fill, SLines_get_capacity()); |
kamilion | 0:5d5265391846 | 367 | } |
kamilion | 0:5d5265391846 | 368 | |
kamilion | 0:5d5265391846 | 369 | if (clientExists) { |
kamilion | 0:5d5265391846 | 370 | generateOutput(pConnectedSock, SLine_get()); |
kamilion | 0:5d5265391846 | 371 | } |
kamilion | 0:5d5265391846 | 372 | |
kamilion | 0:5d5265391846 | 373 | SLine_remove(); |
kamilion | 0:5d5265391846 | 374 | cur_fill = SLines_get_fill(); |
kamilion | 0:5d5265391846 | 375 | lines_handled++; |
kamilion | 0:5d5265391846 | 376 | } |
kamilion | 0:5d5265391846 | 377 | |
kamilion | 0:5d5265391846 | 378 | } |
kamilion | 0:5d5265391846 | 379 | |
kamilion | 0:5d5265391846 | 380 | void serialSetup() { |
kamilion | 0:5d5265391846 | 381 | // Set up the USB UART for host debugging |
kamilion | 0:5d5265391846 | 382 | hostPort.baud(HOST_BAUD); // Need a baud rate. We can go faster, but this is good. |
kamilion | 0:5d5265391846 | 383 | hostPort.attach(&hostMessageReceive, MODSERIAL::RxAutoDetect); // Attach a handler on receive. |
kamilion | 0:5d5265391846 | 384 | hostPort.autoDetectChar('\r'); // Tell the handler to keep an eye for carrage returns. |
kamilion | 0:5d5265391846 | 385 | |
kamilion | 0:5d5265391846 | 386 | // Set up the GPS UART for incoming sentences |
kamilion | 0:5d5265391846 | 387 | gpsPort.baud(38400); // Need a baud rate. We can go faster, but this is good. |
kamilion | 0:5d5265391846 | 388 | gpsPort.attach(&gpsMessageReceive, MODSERIAL::RxAutoDetect); // Attach a handler on receive. |
kamilion | 0:5d5265391846 | 389 | gpsPort.autoDetectChar('\n'); // Tell the handler to keep an eye for newlines. |
kamilion | 0:5d5265391846 | 390 | |
kamilion | 0:5d5265391846 | 391 | // Set up the IMU UART for incoming sentences |
kamilion | 0:5d5265391846 | 392 | imuPort.baud(57600); // Need a baud rate. We can go faster, but this is good. |
kamilion | 0:5d5265391846 | 393 | imuPort.attach(&imuMessageReceive, MODSERIAL::RxAutoDetect); // Attach a handler on receive. |
kamilion | 0:5d5265391846 | 394 | imuPort.autoDetectChar('\n'); // Tell the handler to keep an eye for newlines. |
kamilion | 0:5d5265391846 | 395 | |
kamilion | 0:5d5265391846 | 396 | // Set up the Wheel UART for incoming sentences |
kamilion | 0:5d5265391846 | 397 | wheelPort.baud(57600); // Need a baud rate. We can go faster, but this is good. |
kamilion | 0:5d5265391846 | 398 | wheelPort.attach(&wheelMessageReceive, MODSERIAL::RxAutoDetect); // Attach a handler on receive. |
kamilion | 0:5d5265391846 | 399 | wheelPort.autoDetectChar('\r'); // Tell the handler to keep an eye for carrage returns. |
kamilion | 0:5d5265391846 | 400 | } |
kamilion | 0:5d5265391846 | 401 | |
kamilion | 0:5d5265391846 | 402 | int ethernetSetup() { // Setup ethernet hardware |
kamilion | 0:5d5265391846 | 403 | printf("\r\n$MBEDE,%d,IP,ETHERNET_DHCP_SLEEP_20*FF\r\n",time(NULL)); // Send some status |
kamilion | 0:5d5265391846 | 404 | wait(20); |
kamilion | 0:5d5265391846 | 405 | printf("\r\n$MBEDE,%d,IP,ETHERNET_DHCP*FF\r\n",time(NULL)); // Send some status |
kamilion | 0:5d5265391846 | 406 | EthernetErr ethErr = eth.setup(60000); // Initiate ethernet setup (DHCP or Static IP) |
kamilion | 0:5d5265391846 | 407 | if(ethErr) { |
kamilion | 0:5d5265391846 | 408 | printf("$MBEDE,%d,IP,ETHERNET_ERROR,%d*FF\r\n",time(NULL), ethErr); |
kamilion | 0:5d5265391846 | 409 | return -1; |
kamilion | 0:5d5265391846 | 410 | } |
kamilion | 0:5d5265391846 | 411 | IpAddr ip = eth.getIp(); // Say our IP address |
kamilion | 0:5d5265391846 | 412 | printf("$MBEDE,%d,IP,ADDRESS,%d.%d.%d.%d*FF\r\n",time(NULL), ip[0], ip[1], ip[2], ip[3]); |
kamilion | 0:5d5265391846 | 413 | led3 = 0.10; // Turn on LED3 (DHCP Success) |
kamilion | 0:5d5265391846 | 414 | led1 = 0.00; // Turn off LED1 (Xmit) |
kamilion | 0:5d5265391846 | 415 | led2 = 0.00; // Turn off LED2 (Recv) |
kamilion | 0:5d5265391846 | 416 | return 0; |
kamilion | 0:5d5265391846 | 417 | } |
kamilion | 0:5d5265391846 | 418 | |
kamilion | 0:5d5265391846 | 419 | void portSetup() { // Set the callbacks for Listening |
kamilion | 0:5d5265391846 | 420 | ListeningSock.setOnEvent(&onListeningTCPSocketEvent); // bind and listen on TCP |
kamilion | 0:5d5265391846 | 421 | err=ListeningSock.bind(Host(IpAddr(), TCP_LISTENING_PORT)); // Bind the port |
kamilion | 0:5d5265391846 | 422 | printf("$MBEDE,%d,IP,BINDING_PORT,%i*FF\r\n",time(NULL), TCP_LISTENING_PORT); |
kamilion | 0:5d5265391846 | 423 | if(err) printf("$MBEDE,%d,IP,BINDING_ERROR,%i*FF\r\n",time(NULL), TCP_LISTENING_PORT); |
kamilion | 0:5d5265391846 | 424 | err=ListeningSock.listen(); // Starts listening |
kamilion | 0:5d5265391846 | 425 | printf("$MBEDE,%d,IP,LISTENING_PORT,%i*FF\r\n",time(NULL), TCP_LISTENING_PORT); |
kamilion | 0:5d5265391846 | 426 | if(err) printf("$MBEDE,%d,IP,LISTENING_ERROR,%i*FF\r\n",time(NULL), TCP_LISTENING_PORT); |
kamilion | 0:5d5265391846 | 427 | } |
kamilion | 0:5d5265391846 | 428 | |
kamilion | 0:5d5265391846 | 429 | // ------ MAINLOOP ----- |
kamilion | 0:5d5265391846 | 430 | |
kamilion | 0:5d5265391846 | 431 | int main() { |
kamilion | 0:5d5265391846 | 432 | led1 = 1.0; |
kamilion | 0:5d5265391846 | 433 | highres.start(); // Initiate highres timer ticking. |
kamilion | 0:5d5265391846 | 434 | led1 = 0.75; |
kamilion | 0:5d5265391846 | 435 | buttonSetup(); // Activate button ISRs |
kamilion | 0:5d5265391846 | 436 | led1 = 0.50; |
kamilion | 0:5d5265391846 | 437 | serialSetup(); // Activate Serial ISRs |
kamilion | 0:5d5265391846 | 438 | led1 = 0.10; |
kamilion | 0:5d5265391846 | 439 | printf(BannerText); |
kamilion | 0:5d5265391846 | 440 | led2 = 1.0; |
kamilion | 0:5d5265391846 | 441 | if(ethernetSetup()==-1) return -1; // 'quit' if we cannot setup ethernet. (return to bootloader/'reboot') |
kamilion | 0:5d5265391846 | 442 | led2 = 0.75; |
kamilion | 0:5d5265391846 | 443 | rtcSetup(); // Get time from network |
kamilion | 0:5d5265391846 | 444 | led2 = 0.50; |
kamilion | 0:5d5265391846 | 445 | portSetup(); // Open listens |
kamilion | 0:5d5265391846 | 446 | led2 = 0.10; |
kamilion | 0:5d5265391846 | 447 | |
kamilion | 0:5d5265391846 | 448 | while(true) { // infinite network poll loop |
kamilion | 0:5d5265391846 | 449 | Net::poll(); // Poll the network stack. |
kamilion | 0:5d5265391846 | 450 | processSerialQueues(); // Process queued messages (Seems fastest here) |
kamilion | 0:5d5265391846 | 451 | } |
kamilion | 0:5d5265391846 | 452 | } |