├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── README.md ├── TinyXML.cpp ├── TinyXML.h ├── TinyXMLTable.hpp ├── examples ├── NextBus │ └── NextBus.ino └── routefinder.py └── library.properties /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for opening an issue on an Adafruit Arduino library repository. To 2 | improve the speed of resolution please review the following guidelines and 3 | common troubleshooting steps below before creating the issue: 4 | 5 | - **Do not use GitHub issues for troubleshooting projects and issues.** Instead use 6 | the forums at http://forums.adafruit.com to ask questions and troubleshoot why 7 | something isn't working as expected. In many cases the problem is a common issue 8 | that you will more quickly receive help from the forum community. GitHub issues 9 | are meant for known defects in the code. If you don't know if there is a defect 10 | in the code then start with troubleshooting on the forum first. 11 | 12 | - **If following a tutorial or guide be sure you didn't miss a step.** Carefully 13 | check all of the steps and commands to run have been followed. Consult the 14 | forum if you're unsure or have questions about steps in a guide/tutorial. 15 | 16 | - **For Arduino projects check these very common issues to ensure they don't apply**: 17 | 18 | - For uploading sketches or communicating with the board make sure you're using 19 | a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes 20 | very hard to tell the difference between a data and charge cable! Try using the 21 | cable with other devices or swapping to another cable to confirm it is not 22 | the problem. 23 | 24 | - **Be sure you are supplying adequate power to the board.** Check the specs of 25 | your board and plug in an external power supply. In many cases just 26 | plugging a board into your computer is not enough to power it and other 27 | peripherals. 28 | 29 | - **Double check all soldering joints and connections.** Flakey connections 30 | cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. 31 | 32 | - **Ensure you are using an official Arduino or Adafruit board.** We can't 33 | guarantee a clone board will have the same functionality and work as expected 34 | with this code and don't support them. 35 | 36 | If you're sure this issue is a defect in the code and checked the steps above 37 | please fill in the following fields to provide enough troubleshooting information. 38 | You may delete the guideline and text above to just leave the following details: 39 | 40 | - Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE** 41 | 42 | - Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO 43 | VERSION HERE** 44 | 45 | - List the steps to reproduce the problem below (if possible attach a sketch or 46 | copy the sketch code in too): **LIST REPRO STEPS BELOW** 47 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for creating a pull request to contribute to Adafruit's GitHub code! 2 | Before you open the request please review the following guidelines and tips to 3 | help it be more easily integrated: 4 | 5 | - **Describe the scope of your change--i.e. what the change does and what parts 6 | of the code were modified.** This will help us understand any risks of integrating 7 | the code. 8 | 9 | - **Describe any known limitations with your change.** For example if the change 10 | doesn't apply to a supported platform of the library please mention it. 11 | 12 | - **Please run any tests or examples that can exercise your modified code.** We 13 | strive to not break users of the code and running tests/examples helps with this 14 | process. 15 | 16 | Thank you again for contributing! We will try to test and integrate the change 17 | as soon as we can, but be aware we have many GitHub repositories to manage and 18 | can't immediately respond to every request. There is no need to bump or check in 19 | on a pull request (it will clutter the discussion of the request). 20 | 21 | Also don't be worried if the request is closed or not integrated--sometimes the 22 | priorities of Adafruit's GitHub code (education, ease of use) might not match the 23 | priorities of the pull request. Don't fret, the open source community thrives on 24 | forks and GitHub makes it easy to keep your changes in a forked repo. 25 | 26 | After reviewing the guidelines above you can delete this text from the pull request. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TinyXML Library for Arduino 2 | ---- 3 | 4 | Forked from Adam Rudd's (adamvr) 'arduino-sketches' repository: 5 | https://github.com/adamvr/arduino-sketches 6 | Just the TinyXML library is used here, updated for current Arduino IDE. 7 | -------------------------------------------------------------------------------- /TinyXML.cpp: -------------------------------------------------------------------------------- 1 | /* LCD library for noka 3110 display 2 | * Date: January 2010 3 | * Author: J Crouchley 4 | * 5 | * This is a table driven parser for simple XML, 6 | * Pass the XML into the library one character at a time 7 | * the callback function will be called at 'interesting' 8 | * point (got a tag, got an attribute value, tag text, end of tag) 9 | * with information about the tags and any values. 10 | * 11 | * See the examples for usage 12 | */ 13 | #include 14 | #include 15 | 16 | #include "TinyXMLTable.hpp" 17 | 18 | #include "TinyXML.h" 19 | 20 | 21 | #define DEBUG 2 22 | 23 | parseTable *pTable = (parseTable *)&stateTable; 24 | 25 | 26 | TinyXML::TinyXML() 27 | { 28 | } 29 | 30 | void TinyXML::init(uint8_t* buffer, uint16_t maxbuflen, XMLcallback XMLcb) 31 | { 32 | Xcb = XMLcb; 33 | dataBuffer = buffer; 34 | maxDataLen = maxbuflen - 1; 35 | reset(); 36 | } 37 | 38 | void TinyXML::reset() 39 | { 40 | dataBufferPtr =0; 41 | tagBufferPtr = 0; 42 | LTCount = 0; 43 | tagCount = 0; 44 | currentState = Init; 45 | } 46 | 47 | void TinyXML::processChar(uint8_t ch) 48 | { 49 | uint16_t chToParse; 50 | boolean bMatch=false; 51 | while (!bMatch) 52 | { 53 | chToParse = pgm_read_word(&(pTable[currentState].charToParse)); 54 | switch ( chToParse ) 55 | { 56 | case whiteSpace: 57 | if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') bMatch=true; 58 | break; 59 | case alpha: 60 | if (isAlpha(ch)) bMatch=true; 61 | break; 62 | case alphanum: 63 | if (isAlpha(ch) || isNumeric(ch) || (ch == ':') || (ch == '_') || (ch == '-')) bMatch=true; 64 | break; 65 | case quote: 66 | if (ch == '"' || ch == '\'') 67 | { 68 | matchQuote = ch; 69 | bMatch=true; 70 | } 71 | break; 72 | case matchingquote: 73 | if (ch == matchQuote) bMatch=true; 74 | break; 75 | case anychar: 76 | bMatch=true; 77 | break; 78 | default: 79 | if (ch == chToParse) bMatch=true; 80 | break; 81 | } 82 | if (!bMatch) 83 | { 84 | #if DEBUG > 3 85 | Serial.print("Non-matching state:"); 86 | Serial.print(currentState,DEC); 87 | Serial.print(" ch:"); 88 | Serial.print(ch,HEX); 89 | Serial.print(" match criteria:"); 90 | Serial.print(chToParse,HEX); 91 | Serial.print(" new state:"); 92 | Serial.println(currentState+1,DEC); 93 | #endif 94 | currentState++; 95 | } 96 | } // as every table enry must end in anychar we must get out of here 97 | 98 | #if DEBUG > 2 99 | Serial.print("Matching state:"); 100 | Serial.print(currentState,DEC); 101 | Serial.print(" ch:"); 102 | Serial.print(ch,HEX); 103 | Serial.print(" match criteria:"); 104 | Serial.print(chToParse,HEX); 105 | Serial.print(" tagBufferPtr:"); 106 | Serial.print(tagBufferPtr,DEC); 107 | Serial.print(" new state:"); 108 | Serial.println(pgm_read_byte(&(pTable[currentState].nextState)),DEC); 109 | #endif 110 | action(ch, pgm_read_byte(&(pTable[currentState].actionNumber))); 111 | action(ch, pgm_read_byte(&(pTable[currentState].actionNumber2))); 112 | 113 | currentState=pgm_read_byte(&(pTable[currentState].nextState)); 114 | } 115 | 116 | 117 | 118 | void TinyXML::action(uint8_t ch, uint8_t actionType) 119 | { 120 | #if DEBUG > 5 121 | Serial.print("Action:"); 122 | Serial.println(actionType,DEC); 123 | #endif 124 | switch (actionType) 125 | { 126 | case donothing: 127 | break; 128 | case incLTcount: 129 | LTCount++; 130 | break; 131 | case decLTcount: 132 | if (--LTCount < 0 ) action(ch,error); 133 | break; 134 | case cleardata: 135 | dataBufferPtr = 0; 136 | break; 137 | case storeifneeded: 138 | if (dataBufferPtr < maxDataLen) dataBuffer[dataBufferPtr++] = ch; 139 | break; 140 | case starttagname: 141 | dataBuffer[dataBufferPtr] = 0; // terminate the text 142 | // call back if the previous tag text is required 143 | tagBuffer[tagBufferPtr] = 0; 144 | if (tagBufferPtr && dataBufferPtr) Xcb(STATUS_TAG_TEXT,(char*)tagBuffer,tagBufferPtr,(char*)dataBuffer,dataBufferPtr); 145 | dataBufferPtr = 0; // clear down for next time 146 | #if DEBUG > 2 147 | Serial.print("starttagname Tag:"); 148 | Serial.print((char*)tagBuffer); 149 | Serial.print(" text:"); 150 | Serial.println((char*)dataBuffer); 151 | #endif 152 | if (tagBufferPtr < TAGBUFFERMAX) tagBuffer[tagBufferPtr++] = '/'; 153 | else action(ch, error); 154 | break; 155 | case cleartagname: 156 | if (tagBufferPtr) 157 | tagBuffer[--tagBufferPtr]=0; // remove the slash 158 | break; 159 | case addtotagname: 160 | if (tagBufferPtr < TAGBUFFERMAX) tagBuffer[tagBufferPtr++] = ch; 161 | else action(ch, error); 162 | break; 163 | case inctagcount: 164 | tagCount++; 165 | tagBuffer[tagBufferPtr] = 0; 166 | Xcb(STATUS_START_TAG,(char*)tagBuffer,tagBufferPtr,0,0); 167 | #if DEBUG > 2 168 | Serial.print("incTagCount:"); 169 | Serial.print(tagCount,DEC); 170 | Serial.print(" Tags found:"); 171 | Serial.println((char*)tagBuffer); 172 | #endif 173 | break; 174 | case removelasttag: 175 | if (--tagCount < 0 ) 176 | { 177 | action(ch,error); 178 | break; 179 | } 180 | tagBuffer[tagBufferPtr] = 0; 181 | Xcb(STATUS_END_TAG,(char*)tagBuffer,tagBufferPtr,0,0); 182 | while (tagBufferPtr && tagBuffer[--tagBufferPtr] != '/'); // as we error if tagBuffer overflows then this will be safe 183 | #if DEBUG > 3 184 | tagBuffer[tagBufferPtr] = 0; 185 | Serial.print("removelasttag TagCount:"); 186 | Serial.print(tagCount,DEC); 187 | Serial.print(" tagBufferPtr:"); 188 | Serial.print(tagBufferPtr,DEC); 189 | Serial.print(" Tags found:"); 190 | Serial.println((char*)tagBuffer); 191 | #endif 192 | break; 193 | case cleartagendname: 194 | checkTagBufferPtr = 0; 195 | break; 196 | case addtochktagname: 197 | if (checkTagBufferPtr < CHECKTAGMAX) checkTagBuffer[checkTagBufferPtr++] = ch; 198 | else action(ch, error); 199 | break; 200 | case checkremovelasttag: 201 | // need to test here to see if the tag being removed is the correct one - error if not 202 | if (--tagCount < 0 ) 203 | { 204 | action(ch,error); 205 | break; 206 | } 207 | tagBufferPtr--; // we have had a start so back past the last '/' we placed when the tag started 208 | tagBuffer[tagBufferPtr] = 0; 209 | Xcb(STATUS_END_TAG,(char*)tagBuffer,tagBufferPtr,0,0); 210 | while (tagBufferPtr && tagBuffer[--tagBufferPtr] != '/'); // as we error if tagBuffer overflows then this will be safe 211 | #if DEBUG > 3 212 | tagBuffer[tagBufferPtr] = 0; 213 | Serial.print("checkremovelasttag TagCount:"); 214 | Serial.print(tagCount,DEC); 215 | Serial.print(" tagBufferPtr:"); 216 | Serial.print(tagBufferPtr,DEC); 217 | Serial.print(" Tags found:"); 218 | Serial.println((char*)tagBuffer); 219 | #endif 220 | break; 221 | case clearattrname: 222 | attrBufferPtr = 0; 223 | dataBufferPtr = 0; 224 | break; 225 | case addtoattrname: 226 | attrBuffer[attrBufferPtr++] = ch; 227 | break; 228 | case setquotechar: 229 | matchQuote = ch; 230 | break; 231 | case addtoattrvalue: 232 | if (dataBufferPtr < maxDataLen) dataBuffer[dataBufferPtr++] = ch; 233 | break; 234 | case gotattrvalue: 235 | attrBuffer[attrBufferPtr] = 0; 236 | dataBuffer[dataBufferPtr] = 0; 237 | Xcb(STATUS_ATTR_TEXT,(char*)attrBuffer,attrBufferPtr,(char*)dataBuffer,dataBufferPtr); 238 | dataBufferPtr = 0; 239 | break; 240 | case error: 241 | Xcb(STATUS_ERROR,(char*)tagBuffer,tagBufferPtr,(char*)dataBuffer,dataBufferPtr); 242 | reset(); 243 | break; 244 | case initialise: 245 | reset(); 246 | break; 247 | } 248 | } 249 | 250 | -------------------------------------------------------------------------------- /TinyXML.h: -------------------------------------------------------------------------------- 1 | #ifndef TinyXML_h 2 | #define TinyXML_h 3 | 4 | #include 5 | typedef void (*XMLcallback) (uint8_t errorflag, char* nameBuffer, uint16_t namebuflen, char* dataBuffer, uint16_t databuflen); 6 | 7 | #define isAlpha(ch) ((ch >= 'A' && ch <= 'Z') || (ch>='a' && ch<='z')) 8 | #define isNumeric(ch) (ch >= '0' && ch <= '9') 9 | 10 | // 11 | // Status flags 12 | // 13 | #define STATUS_START_TAG 0x01 14 | #define STATUS_TAG_TEXT 0x02 15 | #define STATUS_ATTR_TEXT 0x04 16 | #define STATUS_END_TAG 0x08 17 | #define STATUS_ERROR 0x10 18 | 19 | #define DEFAULT_BUFFER_SIZE 256 20 | #define TAGBUFFERMAX 128 21 | #define ATTRBUFFERMAX 64 22 | #define CHECKTAGMAX 64 23 | 24 | class TinyXML 25 | { 26 | private: 27 | XMLcallback Xcb; 28 | uint8_t tagBuffer[TAGBUFFERMAX+1]; // allow for terminating zero 29 | uint16_t tagBufferPtr; 30 | uint8_t attrBuffer[ATTRBUFFERMAX+1]; // allow for terminating zero 31 | uint16_t attrBufferPtr; 32 | uint8_t currentState; 33 | uint8_t matchQuote; 34 | uint8_t LTCount; 35 | int8_t tagCount; 36 | uint8_t* dataBuffer; 37 | uint16_t maxDataLen; 38 | uint16_t dataBufferPtr; 39 | uint8_t checkTagBuffer[CHECKTAGMAX+1]; // allow for terminating zero 40 | uint16_t checkTagBufferPtr; 41 | 42 | void action(uint8_t ch, uint8_t actionType); 43 | public: 44 | TinyXML(); // constructor 45 | void init (uint8_t* buffer, uint16_t maxbuflen, XMLcallback XMLcb); 46 | void reset(); 47 | void processChar(uint8_t ch); 48 | }; 49 | 50 | #endif 51 | 52 | 53 | -------------------------------------------------------------------------------- /TinyXMLTable.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // definitions for the parsing table structure 3 | // 4 | #include 5 | 6 | // 7 | // Char types 8 | // 9 | #define whiteSpace 0x100 10 | #define alpha 0x200 11 | #define alphanum 0x300 12 | #define quote 0x400 13 | #define matchingquote 0x500 14 | #define anychar 0x600 // must be one of these at the end of every state 15 | 16 | // 17 | // actions 18 | // 19 | #define donothing 0 20 | #define incLTcount 1 21 | #define decLTcount 2 22 | #define storeifneeded 3 23 | #define starttagname 4 24 | #define addtotagname 5 25 | #define removelasttag 6 26 | #define checkremovelasttag 7 27 | #define addtoattrname 8 28 | #define setquotechar 9 29 | #define gotattrvalue 10 30 | #define error 11 31 | #define initialise 12 32 | #define clearattrname 13 33 | #define addtoattrvalue 14 34 | #define inctagcount 15 35 | #define addtochktagname 16 36 | #define cleardata 17 37 | #define cleartagendname 18 38 | #define cleartagname 19 39 | 40 | // 41 | // State Defines 42 | // 43 | #define Init 0 44 | #define Init1 Init+3 45 | #define TagStart Init1+2 46 | #define IgnoreToGT TagStart+5 47 | #define IgnoreTagToGT IgnoreToGT+2 48 | #define IgnoreTagToGTEnd IgnoreTagToGT+3 49 | #define TagEnd IgnoreTagToGTEnd+2 50 | #define TagName TagEnd+3 51 | #define InTag TagName+5 52 | #define InAttr InTag+5 53 | #define InAttrGetValue InAttr+3 54 | #define InAttrGetValue1 InAttrGetValue+6 55 | #define InAttrGetValue2 InAttrGetValue1+2 56 | 57 | struct parseTable { 58 | uint16_t charToParse; 59 | uint8_t actionNumber; 60 | uint8_t actionNumber2; 61 | uint8_t nextState; 62 | }; 63 | 64 | const parseTable stateTable[] PROGMEM = { 65 | /* 00 Init */ {'<', incLTcount, starttagname, TagStart}, 66 | /* 01 2 */ {whiteSpace, donothing, donothing, Init}, 67 | /* 02 3 */ {anychar, cleardata, storeifneeded, Init1}, 68 | 69 | /* 03 Init1 */ {'<', incLTcount, starttagname, TagStart}, 70 | /* 04 2 */ {anychar, storeifneeded, donothing, Init1}, 71 | 72 | /* 05 TagStart */ {'?', cleartagname, donothing, IgnoreToGT}, // start of a tag name 73 | /* 06 2 */ {'!', cleartagname, donothing, IgnoreToGT}, // start of a tag name 74 | /* 07 3 */ {'/', cleartagendname, donothing, TagEnd}, 75 | /* 08 4 */ {alpha, addtotagname, donothing, TagName}, 76 | /* 09 5 */ {anychar, error, initialise, Init}, 77 | 78 | /* 10 IgnoreToGT */ {'>', decLTcount, donothing, Init}, // handle 79 | /* 11 2 */ {anychar, donothing, donothing, IgnoreToGT}, 80 | 81 | /* 12 IgnoreTagToGT */ {'>', decLTcount, donothing, Init}, // handle tag of form 82 | /* 13 2 */ {'/', donothing, donothing, IgnoreTagToGTEnd}, // handle tag of form 83 | /* 14 3 */ {anychar, donothing, donothing, IgnoreTagToGT}, 84 | 85 | /* 15 IgnoreTagToGTEnd */ {'>', removelasttag, decLTcount, Init}, // handle tag of form 86 | /* 16 2 */ {anychar, donothing, donothing, IgnoreTagToGT}, 87 | 88 | /* 17 TagEnd */ {alphanum, addtochktagname, donothing, TagEnd}, // cope with 89 | /* 18 2 */ {'>', checkremovelasttag,decLTcount, Init}, 90 | /* 19 3 */ {anychar, error, initialise, Init}, 91 | 92 | /* 20 TagName */ {alphanum, addtotagname, donothing, TagName}, // process 93 | /* 21 2 */ {whiteSpace, inctagcount, clearattrname, InTag}, 94 | /* 22 3 */ {'>', inctagcount, decLTcount, Init}, 95 | /* 23 4 */ {'/', inctagcount, donothing, IgnoreTagToGTEnd}, 96 | /* 24 5 */ {anychar, error, initialise, Init}, 97 | 98 | /* 25 InTag */ {alpha, addtoattrname, donothing, InAttr}, // cope with 99 | /* 26 2 */ {whiteSpace, clearattrname, donothing, InTag}, 100 | /* 27 3 */ {'>', decLTcount, donothing, Init}, 101 | /* 28 4 */ {'/', donothing, donothing, IgnoreTagToGTEnd}, 102 | /* 29 5 */ {anychar, error, initialise, Init}, 103 | 104 | /* 30 InAttr */ {alphanum, addtoattrname, donothing, InAttr}, // cope with 105 | /* 31 2 */ {'=', donothing, donothing, InAttrGetValue}, 106 | /* 32 3 */ {anychar, error, initialise, Init}, 107 | 108 | /* 33 InAttrGetValue */ {quote, setquotechar, donothing, InAttrGetValue1}, // process 109 | /* 34 2 */ {whiteSpace, gotattrvalue, clearattrname, InTag}, 110 | /* 35 3 */ {'/', gotattrvalue, donothing, IgnoreTagToGTEnd}, 111 | /* 36 4 */ {'>', gotattrvalue, decLTcount, Init}, 112 | /* 37 5 */ {alphanum, addtoattrvalue, donothing, InAttrGetValue}, 113 | /* 38 6 */ {anychar, error, initialise, Init}, 114 | 115 | /* 39 InAttrGetValue1 */ {matchingquote, gotattrvalue, donothing, InAttrGetValue2}, // process 116 | /* 40 2 */ {anychar, addtoattrvalue, donothing, InAttrGetValue1}, 117 | 118 | /* 41 InAttrGetValue2 */ {whiteSpace, clearattrname, donothing, InTag}, // process 119 | /* 42 2 */ {'/', donothing, donothing, IgnoreTagToGTEnd}, 120 | /* 43 3 */ {'>', decLTcount, donothing, Init}, 121 | /* 44 4 */ {anychar, error, initialise, Init} 122 | 123 | }; 124 | 125 | -------------------------------------------------------------------------------- /examples/NextBus/NextBus.ino: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------- 2 | // NextBus transit display using ESP8266 WiFi microcontroller and Adafruit 3 | // 7-segment displays. Uses the following parts: 4 | // - Adafruit Feather HUZZAH with ESP8266 WiFi (Adafruit #2821) -OR- 5 | // - Adafruit HUZZAH ESP8266 Breakout (#2471) (requires FTDI cable) 6 | // - Adafruit 0.56" 4-Digit 7-Segment Display (878, 879, 880, 881 or 1002) 7 | // 1 to 8 displays can be used, one per transit stop & line. 8 | // 9 | // Requires TinyXML library: https://github.com/adafruit/TinyXML 10 | // 11 | // This shows the next tracked & predicted arrival time for each transit 12 | // line and stop listed below, one per display (or next 2 arrivals if both 13 | // can fit on the 4-digit display with a space in-between). As written, 14 | // this discards arrivals below a certain time threshold (can't safely be 15 | // reached in time) and shows only whole minutes; it does not count down 16 | // seconds. Intended to be a convenience and not a stress-inducing thing. 17 | // ------------------------------------------------------------------------- 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | // CONFIGURABLE GLOBAL STUFF ----------------------------------------------- 25 | 26 | char ssid[] = "NETWORK_NAME", // WiFi network name 27 | pass[] = "NETWORK_PASSWORD", // WiFi network password 28 | host[] = "webservices.nextbus.com"; 29 | 30 | #define POLL_INTERVAL 2 // Time between searches (minutes) 31 | #define MIN_TIME 5 // Skip arrivals sooner than this (minutes) 32 | #define READ_TIMEOUT 15 // Cancel query if no data received (seconds) 33 | 34 | struct { 35 | const uint8_t addr; // I2C address of display 36 | const char *agency; // Get these using routefinder.py script 37 | const char *route; 38 | const char *stopID; 39 | uint32_t lastQueryTime; // Time of last query (millis()) 40 | uint32_t seconds[2]; // Most recent predictions from server 41 | Adafruit_7segment display; // 7segment initialized at runtime 42 | } stops[] = { 43 | { 0x70, "actransit", "210", "0702640" }, // Ohlone College 44 | { 0x71, "actransit", "210", "0702630" }, // Union Landing 45 | { 0x72, "actransit", "232", "0704440" }, // Fremont BART 46 | { 0x73, "actransit", "232", "0704430" } // NewPark Mall 47 | }; 48 | 49 | // For basic use as-is, shouldn't need to edit below this line ------------- 50 | 51 | #define NUM_STOPS (sizeof(stops) / sizeof(stops[0])) 52 | 53 | WiFiClient client; 54 | TinyXML xml; 55 | uint8_t buffer[150]; // For XML decoding 56 | uint8_t s=NUM_STOPS; // Stop # currently being searched 57 | uint32_t lastConnectTime = -(POLL_INTERVAL * 60000L); // neg on purpose! 58 | uint32_t seconds[2]; 59 | 60 | // UTILITY FUNCTIONS ------------------------------------------------------- 61 | 62 | // TinyXML invokes this callback as elements are parsed in XML, 63 | // allowing us to pick out just the tidbits of interest rather than 64 | // dynamically allocating and then traversing a whole brobdingnagian 65 | // tree structure; very Arduino-friendly! 66 | // As written here, this looks for XML tags named "seconds" and extracts 67 | // the accompanying value (a predicted bus arrival time, in seconds). 68 | // If it's longer than our minimum threshold, the least two values are 69 | // sorted and saved (sooner time first); one or both may be zero if 70 | // further predictions are not available. Results are stored in global 71 | // two-element int array named 'seconds' and moved to stop struct later. 72 | void XML_callback(uint8_t statusflags, char* tagName, 73 | uint16_t tagNameLen, char* data, uint16_t dataLen) { 74 | if((statusflags & STATUS_ATTR_TEXT) && !strcasecmp(tagName, "seconds")) { 75 | uint32_t t = atoi(data); // Prediction in seconds (0 if gibberish) 76 | Serial.print(t); Serial.println(" seconds"); 77 | if(t >= (MIN_TIME * 60)) { // Above our "too soon" threshold? 78 | if(!seconds[0]) { // No predictions yet? 79 | seconds[0] = t; // Save in slot 0, done 80 | } else { // Else 1-2 existing predictions... 81 | if(t <= seconds[0]) { // New time sooner than slot 0? 82 | seconds[1] = seconds[0]; // Move 0 up to 1 (old 1 discarded) 83 | seconds[0] = t; // Store new time in 0 84 | } else if(!seconds[1] || // Slot 1 empty? 85 | (t <= seconds[1])) { // Or new time sooner than 1? 86 | seconds[1] = t; // Store new time in slot 1 87 | } // Else discard 88 | if(seconds[0] == seconds[1]) seconds[1] = 0; // If equal, delete 1 89 | } 90 | } 91 | } 92 | } 93 | 94 | // Update 7-segment I2C display(s) with arrival predictions 95 | void refresh(void) { 96 | uint32_t t, dt; 97 | int16_t p[2], m; // Prediction times (in minutes) 98 | uint8_t i, j, k, idx[] = { 0, 1, 3, 4 }; // 7-segment digit indices 99 | 100 | yield(); // Handle WiFi events 101 | 102 | for(i=0; i= (MIN_TIME * 60)) { // Above min time threshold? 111 | m = s / 60; // Seconds -> minutes 112 | if(m > 99) m = 99; // Clip to 2 digits 113 | p[k++] = m; // Store 114 | } 115 | } 116 | } 117 | 118 | if(!k) { 119 | // No current predictions for this stop. Display '----' 120 | for(j=0; j<4; j++) { 121 | stops[i].display.writeDigitRaw(idx[j], 0b01000000); 122 | } 123 | } else { 124 | stops[i].display.clear(); 125 | // If two predictions for this stop, and if earlier prediction 126 | // is 10 minutes or more, don't show the second prediction (it 127 | // won't fit on display). Two predictions are shown ONLY if 128 | // both will fit with a blank digit between them. 129 | if(p[0] >= 10) p[1] = 0; 130 | 131 | if(p[1]) { 132 | stops[i].display.print(p[1]); // 2nd prediction on right 133 | stops[i].display.writeDigitNum(0, p[0], false); // 1st on left 134 | } else { 135 | // Single prediction sorta left-ish justified on digits 0 & 1 136 | if(p[0] >= 10) stops[i].display.writeDigitNum(0, p[0] / 10, false); 137 | stops[i].display.writeDigitNum(1, p[0] % 10, false); 138 | } 139 | } 140 | 141 | stops[i].display.writeDisplay(); 142 | 143 | // If this stop is currently being searched, pulse brightness 144 | if(i == s) { 145 | j = t >> 6; // ~1/16 sec 146 | if(j & 0x10) stops[i].display.setBrightness( j & 0xF); 147 | else stops[i].display.setBrightness(15 - (j & 0xF)); 148 | } else stops[i].display.setBrightness(k ? 15 : 1); 149 | } 150 | } 151 | 152 | // ONE-TIME INITIALIZATION ------------------------------------------------- 153 | 154 | void setup(void) { 155 | Serial.begin(115200); 156 | Serial.println("NextBus Tracker"); 157 | Wire.begin(); 158 | xml.init((uint8_t *)buffer, sizeof(buffer), &XML_callback); 159 | 160 | for(uint8_t i=0; i= 60) { // If it didn't connect within 1 min 191 | Serial.println("Failed. Will retry..."); 192 | return; 193 | } 194 | Serial.println("OK!"); 195 | delay(10000); // Pause before hitting it with queries & stuff 196 | } 197 | 198 | for(s=0; s= 0) { 221 | xml.processChar(c); 222 | t = millis(); // Reset timeout clock 223 | } else if((millis() - t) >= (READ_TIMEOUT * 1000)) { 224 | Serial.println("---Timeout---"); 225 | timedOut = true; 226 | break; 227 | } 228 | } 229 | if(!timedOut && seconds[0]) { // Successful transfer? 230 | // Copy newly-polled predictions to stops structure: 231 | memcpy(stops[s].seconds, seconds, sizeof(seconds)); 232 | stops[s].lastQueryTime = millis(); 233 | } 234 | } 235 | client.stop(); 236 | Serial.println(); 237 | } 238 | } 239 | 240 | -------------------------------------------------------------------------------- /examples/routefinder.py: -------------------------------------------------------------------------------- 1 | # NextBus configurator. Prompts user for transit agency, bus line, 2 | # direction and stop, issues a string which can then be copied & pasted 3 | # into the predictor program. Not fancy, just uses text prompts, 4 | # minimal error checking. 5 | 6 | import urllib 7 | from xml.dom.minidom import parseString 8 | 9 | # Open connection, issue request, read & parse XML response ------------------ 10 | def req(cmd): 11 | connection = urllib.urlopen( 12 | 'http://webservices.nextbus.com' + 13 | '/service/publicXMLFeed?command=' + cmd) 14 | raw = connection.read() 15 | connection.close() 16 | xml = parseString(raw) 17 | return xml 18 | 19 | # Prompt user for a number in a given range ---------------------------------- 20 | def getNum(prompt, n): 21 | while True: 22 | nb = raw_input('Enter ' + prompt + ' 0-' + str(n-1) + ': ') 23 | try: x = int(nb) 24 | except ValueError: continue # Ignore non-numbers 25 | if x >= 0 and x < n: return x # and out-of-range values 26 | 27 | # Main application ----------------------------------------------------------- 28 | 29 | # Get list of transit agencies, prompt user for selection, get agency tag. 30 | dom = req('agencyList') 31 | elements = dom.getElementsByTagName('agency') 32 | print 'TRANSIT AGENCIES:' 33 | for i, item in enumerate(elements): 34 | print str(i) + ') ' + item.getAttribute('title') 35 | n = getNum('transit agency', len(elements)) 36 | agencyTag = elements[n].getAttribute('tag') 37 | 38 | # Get list of routes for selected agency, prompt user, get route tag. 39 | dom = req('routeList&a=' + agencyTag) 40 | elements = dom.getElementsByTagName('route') 41 | print '\nROUTES:' 42 | for i, item in enumerate(elements): 43 | print str(i) + ') ' + item.getAttribute('title') 44 | n = getNum('route', len(elements)) 45 | routeTag = elements[n].getAttribute('tag') 46 | 47 | # Get list of directions for selected agency & route, prompt user... 48 | dom = req('routeConfig&a=' + agencyTag + '&r=' + routeTag) 49 | elements = dom.getElementsByTagName('direction') 50 | print 51 | print '\nDIRECTIONS:' 52 | for i, item in enumerate(elements): 53 | print str(i) + ') ' + item.getAttribute('title') 54 | n = getNum('direction', len(elements)) 55 | dirTitle = elements[n].getAttribute('title') # Save for later 56 | # ...then get list of stop numbers and descriptions -- these are 57 | # nested in different parts of the XML and must be cross-referenced 58 | stopNums = elements[n].getElementsByTagName('stop') 59 | stopDescs = dom.getElementsByTagName('stop') 60 | 61 | # Cross-reference stop numbers and descriptions to provide a readable 62 | # list of available stops for selected agency, route & direction. 63 | # Prompt user for stop number and get corresponding stop tag. 64 | print '\nSTOPS:' 65 | for i, item in enumerate(stopNums): 66 | stopNumTag = item.getAttribute('tag') 67 | for d in stopDescs: 68 | stopDescTag = d.getAttribute('tag') 69 | if stopNumTag == stopDescTag: 70 | print str(i) + ') ' + d.getAttribute('title') 71 | break 72 | n = getNum('stop', len(stopNums)) 73 | stopTag = stopNums[n].getAttribute('tag') 74 | 75 | # The prediction server wants the stop tag, NOT the stop ID, not sure 76 | # what's up with that. 77 | 78 | print '\nCOPY/PASTE INTO ARDUINO SKETCH:' 79 | print (' { 0x70, "' + agencyTag + '", "' + routeTag + '", "' + stopTag + 80 | '" }, // ' + dirTitle) 81 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=TinyXML 2 | version=1.0.3 3 | author=Adafruit 4 | maintainer=Adafruit 5 | sentence=Fork of Adam Rudd's (adamvr) TinyXML library. 6 | paragraph=Fork of Adam Rudd's (adamvr) TinyXML library. 7 | category=Data Processing 8 | url=https://github.com/adafruit/TinyXML 9 | architectures=* 10 | --------------------------------------------------------------------------------