Emic2 reading tweets
Hello,
I have a problem with a project I am working on with emic2 and Arduino Uno+Ethernet Shield. I want to make emic reading tweets. This project is similar with Social chatter (http://www.adafruit.com/blog/2012/08/16 ... ad-tweets/). The problem is that the twitter API has changed so now I have to combine the code form socialChatter and thermalPrinter. I have done this very carefully but emic2 doesn't read the first tweet that is appeared on serial monitor and everything stops there. And if I place emic2TtsModule.say(msgText); before (for example) Serial.print(F("OK\r\nAwaiting results (if any)...")); the whole process stops there...Any idea why emic2 stops the whole operation? It would be very helpful!!!
Thanks!!
ps. Here is the code I have done!
I have a problem with a project I am working on with emic2 and Arduino Uno+Ethernet Shield. I want to make emic reading tweets. This project is similar with Social chatter (http://www.adafruit.com/blog/2012/08/16 ... ad-tweets/). The problem is that the twitter API has changed so now I have to combine the code form socialChatter and thermalPrinter. I have done this very carefully but emic2 doesn't read the first tweet that is appeared on serial monitor and everything stops there. And if I place emic2TtsModule.say(msgText); before (for example) Serial.print(F("OK\r\nAwaiting results (if any)...")); the whole process stops there...Any idea why emic2 stops the whole operation? It would be very helpful!!!
Thanks!!
ps. Here is the code I have done!
[/FONT][/COLOR][COLOR=#2E8B57][FONT=Monaco]#include <SPI.h>[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]#include <Ethernet.h>[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]#include <EthernetUdp.h>[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]#include <Dns.h>[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]#include <sha1.h>[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]#include <SoftwareSerial.h>[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]#include <Emic2TtsModule.h>[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Stream parse states[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]enum ParseState {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_NORMAL,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_H,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_HT,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_HTT,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_HTTP,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_HTTPS,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_HTTP_COLON,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_HTTP_COLON_SLASH,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_FOUND,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_FALSE_POSITIVE,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] STATE_LINK_DONE[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]};[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Similar to F(), but for PROGMEM string pointers rather than literals[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]#define F2(progmem_ptr) (const __FlashStringHelper *)progmem_ptr[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Configurable globals. Edit to your needs. -------------------------------[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]const char PROGMEM[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Twitter application credentials -- see notes above -- DO NOT SHARE.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] consumer_key[] = "PUT_YOUR_CONSUMER_KEY_HERE",//PUT_YOUR_CONSUMER_KEY_HERE[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] access_token[] = "PUT_YOUR_ACCESS_TOKEN_HERE",//PUT_YOUR_ACCESS_TOKEN_HERE[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] signingKey[] = "Consumer secret" // Consumer secret[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] "&" "Access token secret", // Access token secret[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // The ampersand is intentional -- do not delete![/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // queryString can be any valid Twitter API search string, including[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // boolean operators. See http://dev.twitter.com/docs/using-search[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // for options and syntax. Funny characters do NOT need to be URL[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // encoded here -- the code takes care of that.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] queryString[] = "Christmas",[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Other globals. You probably won't need to change these. -----------------[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] endpoint[] = "/1.1/search/tweets.json",[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] agent[] = "Gutenbird v1.0";[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]const char[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] host[] = "api.twitter.com";[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]const int led_pin = 1; // To status LED (hardware PWM pin)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Emic 2 Globals[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]const int emic2RxPin = 11;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]const int emic2TxPin = 12;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]SoftwareSerial emic2Serial = SoftwareSerial(emic2RxPin, emic2TxPin);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]Emic2TtsModule emic2TtsModule = Emic2TtsModule(&emic2Serial);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]const unsigned long[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] pollingInterval = 60L * 1000L, // Note: Twitter server will allow 150/hr max[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] searchesPerDay = 86400000L / pollingInterval,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] connectTimeout = 15L * 1000L, // Max time to wait for server connection[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] responseTimeout = 15L * 1000L; // Max time to wait for data from server[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]//Adafruit_Thermal[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// printer(printer_RX_Pin, printer_TX_Pin);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]byte[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] maxTweets = 3, // One tweet on first run; avoid runaway output[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] sleepPos = 0, // Current "sleep throb" table position[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] resultsDepth, // Used in JSON parsing[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Ethernet MAC address is found on sticker on Ethernet shield or board:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] mac[] = { 0x90, 0xA2, 0xWA, 0x0E, 0xEB, 0x43 };[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]IPAddress[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] ip(192,168,0,118); // Fallback address -- code will try DHCP first[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]char[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] lastId[21], // 18446744073709551615\0 (64-bit maxint as string)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] timeStamp[32], // WWW, DD MMM YYYY HH:MM:SS +XXXX\0[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] fromUser[16], // Max username length (15) + \0[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] msgText[141], // Max tweet length (140) + \0[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] name[12], // Temp space for name:value parsing[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] value[141]; // Temp space for name:value parsing[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]int[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] searchCount = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]unsigned long[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] currentTime = 0L;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]EthernetClient[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Function prototypes -------------------------------------------------------[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]boolean jsonParse(int, byte);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]boolean readString(char *, int, char);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]int writeStringIfPossible(int, int, char *, char *);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]int unidecode(byte);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]int timedRead(void);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]PROGMEM byte[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] sleepTab[] = { // "Sleep throb" brightness table (reverse for second half)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] 1, 1, 2, 3, 4, 5, 6, 8, 10, 13,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] 15, 19, 22, 26, 31, 36, 41, 47, 54, 61,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] 68, 76, 84, 92, 101, 110, 120, 129, 139, 148,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] 158, 167, 177, 186, 194, 203, 211, 218, 225, 232,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] 237, 242, 246, 250, 252, 254, 255 };[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// --------------------------------------------------------------------------[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]void setup() {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Set up LED "sleep throb" ASAP, using Timer1 interrupt:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] TCCR1A = _BV(WGM11); // Mode 14 (fast PWM), 64:1 prescale, OC1A off[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] ICR1 = 8333; // ~30 Hz between sleep throb updates[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] TIMSK1 |= _BV(TOIE1); // Enable Timer1 interrupt[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] sei(); // Enable global interrupts[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] randomSeed(analogRead(0));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.begin(9600);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// pinMode(printer_Ground, OUTPUT);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// digitalWrite(printer_Ground, LOW); // Just a reference ground, not power[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// printer.begin();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// printer.sleep();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]Serial.print(F("Initializing Emic..."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] pinMode(emic2RxPin, INPUT);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] pinMode(emic2TxPin, OUTPUT);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] emic2Serial.begin(9600);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] emic2TtsModule.init();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] emic2TtsModule.setVolume(8);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] emic2TtsModule.setWordsPerMinute(120);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] emic2TtsModule.setVoice(PerfectPaul);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("OK"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Initialize Ethernet connection. Request dynamic[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // IP address, fall back on fixed IP if that fails:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("Initializing Ethernet..."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] emic2TtsModule.say(F("Hello, my name is Tweet o' Phone."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(Ethernet.begin(mac)) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("OK\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// emic2TtsModule.say(F("OK."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("\r\nno DHCP response, using static IP address."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// emic2TtsModule.say(F("No DHCP response, using static IP address."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Ethernet.begin(mac, ip);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Get initial time from time server (make a few tries if needed)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] for(uint8_t i=0; (i<5) && !(currentTime = getTime()); delay(15000L), i++);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Clear all string data[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] strcpy_P(lastId, PSTR("1"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] timeStamp[0] = fromUser[0] = msgText[0] = name[0] = value[0] = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Search occurs in loop. ---------------------------------------------------[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]void loop() {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] uint8_t *in, out, i;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] char nonce[9], // 8 random digits + NUL[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] searchTime[11], // 32-bit int + NUL[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] b64[29];[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] unsigned long startTime, t;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] static const char PROGMEM b64chars[] =[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] startTime = millis();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Disable Timer1 interrupt during network access, else there's trouble.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Just show LED at steady 100% while working. :T[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] TIMSK1 &= ~_BV(TOIE1);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] analogWrite(led_pin, 255);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Initialize unique values for query[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] sprintf(nonce, "%04x%04x", random() ^ currentTime, startTime ^ currentTime);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] sprintf(searchTime, "%ld", currentTime);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Some debugging/testing/status stuff[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F(" Current time: "));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.println(currentTime);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F(" Last ID: "));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.println(lastId);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.println(msgText[141]);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.initHmac_P((uint8_t *)signingKey, sizeof(signingKey) - 1);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // A dirty hack makes the Oauth song and dance MUCH simpler within the[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Arduino's limited RAM and CPU. A proper general-purpose implementation[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // would be expected to URL-encode keys and values (from the complete list[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // of GET parameters and authentication values), sort by encoded key,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // concatenate and URL-encode the combined result. Sorting is avoided[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // because every query this program performs has exactly the same set of[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // parameters, so we've pre-sorted the list as it appears here. Keys[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // (and many values) are pre-encoded because they never change; many are[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // passed through verbatim because the format is known to not require[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // encoding. Most reside in PROGMEM, not RAM. This is bending a LOT of[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // rules of Good and Proper Authentication and would land you an 'F' in[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Comp Sci class, but it handles the required task and is VERY compact.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F("GET&http%3A%2F%2F"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(host);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] urlEncode(Sha1, endpoint, true, false);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F("&count%3D"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(maxTweets);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F("%26include_entities%3D0%26oauth_consumer_key%3D"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F2(consumer_key));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F("%26oauth_nonce%3D"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(nonce);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F("%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(searchTime);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F("%26oauth_token%3D"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F2(access_token));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F("%26oauth_version%3D1.0%26q%3D"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] urlEncode(Sha1, queryString, true, true);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(F("%26since_id%3D"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Sha1.print(lastId);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // base64-encode SHA-1 hash output. This is NOT a general-purpose base64[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // encoder! It's stripped down for the fixed-length hash -- always 20[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // bytes input, always 27 chars output + '='.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] for(in = Sha1.resultHmac(), out=0; ; in += 3) { // octets to sextets[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] b64[out++] = in[0] >> 2;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] b64[out++] = ((in[0] & 0x03) << 4) | (in[1] >> 4);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(out >= 26) break;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] b64[out++] = ((in[1] & 0x0f) << 2) | (in[2] >> 6);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] b64[out++] = in[2] & 0x3f;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] b64[out] = (in[1] & 0x0f) << 2;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Remap sextets to base64 ASCII chars[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] for(i=0; i<=out; i++) b64[i] = pgm_read_byte(&b64chars[b64[i]]);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] b64[i++] = '=';[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] b64[i++] = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("Connecting to server..."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] t = millis();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while((client.connect(host, 80) == false) &&[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] ((millis() - t) < connectTimeout));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(client.connected()) { // Success![/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("OK\r\nIssuing HTTP request..."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Unlike the hash prep, parameters in the HTTP request don't require[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // sorting, but are still somewhat ordered by function: GET parameters[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // (search values), HTTP headers and Oauth credentials.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("GET "));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F2(endpoint));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("?count="));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(maxTweets);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("&since_id="));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(lastId);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("&include_entities=0&q="));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] urlEncode(client, queryString, true, false);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F(" HTTP/1.1\r\nHost: "));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(host);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("\r\nUser-Agent: "));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F2(agent));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("\r\nConnection: close\r\n"[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] "Content-Type: application/x-www-form-urlencoded;charset=UTF-8\r\n"[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] "Authorization: Oauth oauth_consumer_key=\""));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F2(consumer_key));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("\", oauth_nonce=\""));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(nonce);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("\", oauth_signature=\""));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] urlEncode(client, b64, false, false);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(searchTime);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("\", oauth_token=\""));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F2(access_token));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.print(F("\", oauth_version=\"1.0\"\r\n\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("OK\r\nAwaiting results (if any)..."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] t = millis();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while((!client.available()) && ((millis() - t) < responseTimeout));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(client.available()) { // Response received?[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Could add HTTP response header parsing here (400, etc.)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(client.find("\r\n\r\n")) { // Skip HTTP response header[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("OK\r\nProcessing results...\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] resultsDepth = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] jsonParse(0, 0);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else Serial.print(F("response not recognized.\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else Serial.print(F("connection timed out.\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("Done.\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] client.stop();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else { // Couldn't contact server[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("failed\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Update time in seconds. Once per day, re-sync with time server[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] currentTime += pollingInterval / 1000L;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if((++searchCount >= searchesPerDay) && (t = getTime())) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] currentTime = t;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] searchCount = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Sometimes network access & printing occurrs so quickly, the steady-on[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // LED wouldn't even be apparent, instead resembling a discontinuity in[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // the otherwise smooth sleep throb. Keep it on at least 4 seconds.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while((millis() - startTime) < 4000UL);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Pause between queries, factoring in time already spent on network[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // access, parsing, printing and LED pause above.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if((millis() - startTime) < pollingInterval) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("Pausing..."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] sleepPos = sizeof(sleepTab); // Resume following brightest position[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] TIMSK1 |= _BV(TOIE1); // Re-enable Timer1 interrupt for sleep throb[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while((millis() - startTime) < pollingInterval);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("done\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Helper functions. --------------------------------------------------------[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]boolean jsonParse(int depth, byte endChar) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] int c, i;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] boolean readName = true;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] for(;;) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while(isspace(c = timedRead())); // Scan past whitespace[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(c < 0) return false; // Timeout[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(c == endChar) return true; // EOD[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(c == '{') { // Object follows[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(!jsonParse(depth + 1, '}')) return false;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(!depth) return true; // End of file[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(depth == resultsDepth) { // End of object in results list[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Dump to serial console as well[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Serial.print(F(" User: "));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Serial.println(fromUser);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Serial.print(F(" "));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.println(msgText);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Serial.print(F(" Time: "));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Serial.println(timeStamp);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Output to Emic 2[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] emic2TtsModule.say(msgText);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Clear strings for next object[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] timeStamp[0] = fromUser[0] = msgText[0] = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] maxTweets = 1; // After first, subsequent queries allow more tweets[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if(c == '[') { // Array follows[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if((!resultsDepth) && (!strcasecmp(name, "statuses")))[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] resultsDepth = depth + 1;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(!jsonParse(depth + 1,']')) return false;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if((c == '"') || (c == '\'')) { // String follows[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(readName) { // Name-reading mode[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(!readString(name, sizeof(name)-1, c)) return false;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else { // Value-reading mode[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(!readString(value, sizeof(value)-1, c)) return false;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Process name and value strings:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (!strcasecmp(name, "max_id_str")) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] strncpy(lastId, value, sizeof(lastId)-1);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if(!strcasecmp(name, "created_at")) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Use created_at value for tweet, not user[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(depth == (resultsDepth + 1)) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] strncpy(timeStamp, value, sizeof(timeStamp)-1);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if(!strcasecmp(name, "screen_name")) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] strncpy(fromUser, value, sizeof(fromUser)-1);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if(!strcasecmp(name, "text")) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] strncpy(msgText, value, sizeof(msgText)-1);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if((!strcasecmp(name, "id_str")) &&[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] (strcasecmp(value, lastId) > 0) &&[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] (depth == (resultsDepth + 1))) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] strncpy(lastId, value, sizeof(lastId)-1);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if(c == ':') { // Separator between name:value[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] readName = false; // Now in value-reading mode[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] value[0] = 0; // Clear existing value data[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if(c == ',') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Separator between name:value pairs.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] readName = true; // Now in name-reading mode[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] name[0] = 0; // Clear existing name data[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } // Else true/false/null or a number follows. These values aren't[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // used or expected by this program, so just ignore...either a comma[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // or endChar will come along eventually, these are handled above.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Read string from client stream into destination buffer, up to a maximum[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// requested length. Buffer should be at least 1 byte larger than this to[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// accommodate NUL terminator. Opening quote is assumed already read,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// closing quote will be discarded, and stream will be positioned[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// immediately following the closing quote (regardless whether max length[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// is reached -- excess chars are discarded). Returns true on success[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// (including zero-length string), false on timeout/read error.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]boolean readString(char *dest, int maxLen, char quote) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] int c, len = 0, link_buffer_idx;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] boolean done = false, read_next_char = true;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] char link_buffer[10];[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] ParseState state = STATE_NORMAL;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while(!done) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Handle cases where a character needs to be parsed twice.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (read_next_char) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] c = timedRead();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] read_next_char = true;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Exit if current character is the closing quote.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == '"') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] done = true;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] break;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(c == '\\') { // Escaped char follows[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] c = timedRead(); // Read it[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Certain escaped values are for cursor control --[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // there might be more suitable printer codes for each.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == 'b') c = '\b'; // Backspace[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else if(c == 'f') c = '\f'; // Form feed[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else if(c == 'n') c = '\n'; // Newline[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else if(c == 'r') c = '\r'; // Carriage return[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else if(c == 't') c = '\t'; // Tab[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else if(c == 'u') c = unidecode(4);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else if(c == 'U') c = unidecode(8);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // else c is unaltered -- an escaped char such as \ or "[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } // else c is a normal unescaped char[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Special handling for http links & special characters #, :, /, (, ), *[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (state == STATE_NORMAL) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == 'h') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_H;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] memset(link_buffer, 0, sizeof(link_buffer));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] link_buffer_idx = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Special handling for certain characters[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == '#') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] len = writeStringIfPossible(len, maxLen, dest, " hash \0");[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (c == ':') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] len = writeStringIfPossible(len, maxLen, dest, " colon \0");[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (c == '/') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] len = writeStringIfPossible(len, maxLen, dest, " slash \0");[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (c == '(' || c == ')') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] len = writeStringIfPossible(len, maxLen, dest, " parenthesis \0");[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (c == '*') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] len = writeStringIfPossible(len, maxLen, dest, " star \0");[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Handle timeout[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c < 0) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] dest[len] = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] return false;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // In order to properly position the client stream at the end of[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // the string, characters are read to the end quote, even if the max[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // string length is reached...the extra chars are simply discarded.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(len < maxLen) dest[len++] = c;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (state == STATE_LINK_H) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == 't') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_HT;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_FALSE_POSITIVE;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (state == STATE_LINK_HT) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == 't') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_HTT;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_FALSE_POSITIVE;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (state == STATE_LINK_HTT) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == 'p') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_HTTP;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_FALSE_POSITIVE;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (state == STATE_LINK_HTTP) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == ':') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_HTTP_COLON;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (c == 's') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_HTTPS;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_FALSE_POSITIVE;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (state == STATE_LINK_HTTPS) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == ':') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_HTTP_COLON;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_FALSE_POSITIVE;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (state == STATE_LINK_HTTP_COLON) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == '/') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_HTTP_COLON_SLASH;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_FALSE_POSITIVE;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (state == STATE_LINK_HTTP_COLON_SLASH) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c == '/') {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_FOUND;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_FALSE_POSITIVE;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else if (state == STATE_LINK_FOUND) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if (c != '.' && c != '/' && !isalnum(c)) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_LINK_DONE;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] switch (state) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] case STATE_NORMAL:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] case STATE_LINK_FOUND:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Do nothing.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] break;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] case STATE_LINK_FALSE_POSITIVE:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] len = writeStringIfPossible(len, maxLen, dest, link_buffer);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] read_next_char = false;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_NORMAL;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] break;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] case STATE_LINK_DONE:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] len = writeStringIfPossible(len, maxLen, dest, " link \0");[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] read_next_char = false;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] state = STATE_NORMAL;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] break;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] default:[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Save current character in case of false positive.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] link_buffer[link_buffer_idx++] = c;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] break;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] dest[len] = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] return true; // Success (even if empty string)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Write a passed in string out to the destination buffer if possible, advancing the[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// length as needed and returning it to the caller.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]int writeStringIfPossible(int len, int maxLen, char *dest, char *str) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] int str_idx = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] [/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while (str[str_idx] != '\0' && len < maxLen) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] dest[len++] = str[str_idx++];[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] return len;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Read a given number of hexadecimal characters from client stream,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// representing a Unicode symbol. Return -1 on error, else return nearest[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// equivalent glyph in printer's charset. (See notes below -- for now,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// always returns '-' or -1.)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]int unidecode(byte len) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] int c, v, result = 0;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while(len--) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if((c = timedRead()) < 0) return -1; // Stream timeout[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if ((c >= '0') && (c <= '9')) v = c - '0';[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else if((c >= 'A') && (c <= 'F')) v = 10 + c - 'A';[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else if((c >= 'a') && (c <= 'f')) v = 10 + c - 'a';[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else return '-'; // garbage[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] result = (result << 4) | v;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // To do: some Unicode symbols may have equivalents in the printer's[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // native character set. Remap any such result values to corresponding[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // printer codes. Until then, all Unicode symbols are returned as '-'.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // (This function still serves an interim purpose in skipping a given[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // number of hex chars while watching for timeouts or malformed input.)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] return '-';[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Read from client stream with a 5 second timeout. Although an[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// essentially identical method already exists in the Stream() class,[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// it's declared private there...so this is a local copy.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]int timedRead(void) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] unsigned long start = millis();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while((!client.available()) && ((millis() - start) < 5000L));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] return client.read(); // -1 on timeout[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// URL-encoding output function for Print class.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Input from RAM or PROGMEM (flash). Double-encoding is a weird special[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// case for Oauth (encoded strings get encoded a second time).[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]void urlEncode([/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Print &p, // EthernetClient, Sha1, etc.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] const char *src, // String to be encoded[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] boolean progmem, // If true, string is in PROGMEM (else RAM)[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] boolean x2) // If true, "double encode" parenthesis[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]{[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] static const char PROGMEM hexChar[] = "0123456789ABCDEF";[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] uint8_t c;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] while((c = (progmem ? pgm_read_byte(src) : *src))) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ||[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] ((c >= '0') && (c <= '9')) || strchr_P(PSTR("-_.~"), c)) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] p.write(c);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] } else {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(x2) p.print("%25");[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] else p.write('%');[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] p.write(pgm_read_byte(&hexChar[c >> 4]));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] p.write(pgm_read_byte(&hexChar[c & 15]));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] src++;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Minimalist time server query; adapted from Arduino UdpNTPClient tutorial.[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]unsigned long getTime(void) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] EthernetUDP udp;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] DNSClient dns;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] IPAddress addr;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] byte buf[48];[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] unsigned long t = 0L;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("Polling time server..."));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] udp.begin(8888);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] dns.begin(Ethernet.dnsServerIP());[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Get a time server address from NTP pool[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(dns.getHostByName("pool.ntp.org", addr)) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] static const char PROGMEM[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] timeReqA[] = { 227, 0, 6, 236 },[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] timeReqB[] = { 49, 78, 49, 52 };[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Assemble and issue request packet[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] memset(buf, 0, sizeof(buf));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] memcpy_P( buf , timeReqA, sizeof(timeReqA));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] memcpy_P(&buf[12], timeReqB, sizeof(timeReqB));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] udp.beginPacket(addr, 123);[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] udp.write(buf, sizeof(buf));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] udp.endPacket();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] delay(1000); // Allow time for response[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(udp.parsePacket()) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Read result, convert to UNIX time format[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] udp.read(buf, sizeof(buf));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] t = (((unsigned long)buf[40] << 24) |[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] ((unsigned long)buf[41] << 16) |[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] ((unsigned long)buf[42] << 8) |[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] (unsigned long)buf[43]) - 2208988800UL;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] Serial.print(F("OK\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] }[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] udp.stop();[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(!t) Serial.print(F("error\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] return t;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Timer1 interrupt handler for sleep throb[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]ISR(TIMER1_OVF_vect, ISR_NOBLOCK) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Sine table contains only first half...reflect for second half...[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] analogWrite(led_pin, pgm_read_byte(&sleepTab[[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] (sleepPos >= sizeof(sleepTab)) ?[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] (sizeof(sleepTab) * 2 - 1 - sleepPos) : sleepPos]));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(++sleepPos >= (sizeof(sleepTab) * 2)) sleepPos = 0; // Roll over[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] TIFR1 |= TOV1; // Clear Timer1 interrupt flag[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(!t) Serial.print(F("error\r\n"));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] return t;[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]// Timer1 interrupt handler for sleep throb[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]ISR(TIMER1_OVF_vect, ISR_NOBLOCK) {[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] // Sine table contains only first half...reflect for second half...[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] analogWrite(led_pin, pgm_read_byte(&sleepTab[[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] (sleepPos >= sizeof(sleepTab)) ?[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] (sizeof(sleepTab) * 2 - 1 - sleepPos) : sleepPos]));[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] if(++sleepPos >= (sizeof(sleepTab) * 2)) sleepPos = 0; // Roll over[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco] TIFR1 |= TOV1; // Clear Timer1 interrupt flag[/FONT][/COLOR] [COLOR=#2E8B57][FONT=Monaco]}[/FONT][/COLOR][COLOR=#333333][FONT=verdana]
Comments
I am new in Arduino family and I have a problem with a project I am working on with emic2 and an Arduino Uno+Ethernet Shield. I want to make emic reading tweets. This project is similar with Social chatter (http://www.adafruit.com/blog/2012/08/16 ... ad-tweets/). The problem is that twitter API has changed, so now I have to combine the code form socialChatter and thermalPrinter. I have done this very carefully but emic2[FONT=verdana, arial, helvetica, sans-serif] doesn't read the first tweet that is appeared on serial monitor and everything stops there, also if I place [/FONT] emic2TtsModule.say(msgText); for example, before Serial.print(F("OK\r\nProcessing results...\r\n"));[FONT=verdana, arial, helvetica, sans-serif] the operation stops there Any idea why[/FONT]emic2 stops the whole operation? It would be very helpful!!!
Thanks!!
ps. Here is the code I have done!
I am new in Arduino family but also in this forum!! I have a problem with a project I am working on with emic 2 and an ethernet Shield. I want to make emic reading tweets. This project is similar with Social chatter (http://www.adafruit.com/blog/2012/08/16 ... ad-tweets/). The problem is that the twitter API has changed so now I have to combine the code form socialChatter and thermalPrinter. I have done this very carefully but emic 2 doesn't read the first tweet that is appeared on serial monitor and everything stops there. Also if I place emic2TtsModule.say(msgText); for example before Serial.print(F("OK\r\nIssuing HTTP request...")); twitter's operation stops again there (before Issuing HTTP request...) Any idea why emic 2 stops the whole operation? It would be very helpful!!!
Thanks!!
ps. Here is the code I have done!
One comment:
Try breaking your code down into sections to verify the individual segments are functioning properly. I have to do this frequently. In other wordds, have you verified your setup to the EMIC2 as functional using simple code to just make it speak? Once that is good, check your code between the display and EMIC2. Then go after the tweet reading code. Just a thought.