VFD Clock Connects to the Internet
September 26, 2011 22 Comments
Here is, finally, one week later than planned, the sequel to my initial Ice Tube VFD clock .
It is able to :
- get the time from the internet and automatically synchronise itself (why have to set the time and use a battery to keep it running when disconnected, if we have Internet access ?)
- periodically fetch some weather data and display it
- do searches on Twitter and display the 1st result
So rather than “simply” displaying the time, every 15secs it alternates between the time, the weather and a Twitter message.
Building this clock from Ladyada’s kit was all nice and fun, but almost too simple to be fully rewarding…
So I started having a look at the firmware and trying to modify it to make it do more interesting stuff.
Step 1 – Add scrolling text
Adding custom text wasn’t hard, I just had to insert a new mode in the state machine logic, and then to make it scroll I had to call a custom method from one of the interrupts occurring every millisecond that would move all the display[] array (containing the text currently displayed) one position to the left.
The ease with which I was able to insert new logic into the code, and get something working in literally a couple of hours, shows how nicely the code was initially written… all the logic is in one big file, which might seem hard to follow initially, but it’s nicely commented and divided into methods… thank you Ladyada !
Step 2 – Connect to the serial port
There’s not much point in being able to display some custom text, if it has to be hard-coded into the firmware… so the obvious next step was to make it easy to send data to the AtMega168 which drives this clock.
I hesitated between the serial and the I2C ports. I’m a big fan of I2C which I like much more than simple serial, as you can have plenty of different slaves…
I however went for serial, mainly because there was already code in the firmware that was communicating on that port for debugging purposes. There was also already a mod using this port, so overall it would be much quicker / easier to use this instead of I2C.
So let’s solder the wires to expose this port (pins 2 & 3 of the AtMega 168) :
I’ve also added 2 wires to GND and +5V.
Now we can mount the bottom of the case back :
And here is the final result, with the case mostly put back and the wires getting out of it through the battery hole:
Step 3 – Integrate a WiFi board (WiFly / RN-134)
This is all nice and good, BUT a clock like this is not supposed to stay connected to a PC to keep receiving messages… it would be nice if it could just sit there by itself, and periodically get data from the Internet and display it…
The initial idea was to simply make it display some tweets, but then I thought it would be really simple and quite useful for when I leave for work in the morning to see some weather forecast. Also, as I was working on these 2 goals I realised that with an Internet connection it is really easy to get the current exact time, so why bother setting it up or making sure the crystal doesn’t draft… just synch it up automatically !
Do note the voltage converter between the two, as the clock microcontroller (AtMega 168) is 5V whereas the WiFly runs at 3.3V !
The Ugly
Everything looked good at this point, but unfortunately this was the beginning of a very frustrating week…
Now let’s have a quick look at what took the longest in this project: setting up the WiFly to make the HTTP requests and download the data…
This board is a great piece of equipment, don’t get me wrong, I love it… however there were some issues and it got very frustrating for a while.
I’ll also mention straight away that the Roving Networks support is great, I’ve never seen such a quick and knowledgeable reply !
In a nutshell, and without going into the numerous possible config details that this board offers I started by trying the obvious thing: use it in “HTTP mode” (set ip proto 18), which is supposed to allow one to simply specify the name of a server and the desired page and then the board will add the required HTTP headers.
Something doesn’t work quite right with this mode (probably adding some extra spaces / new lines) but believe it or not, I have spent an entire week-end trying to debug it…
The convoluted thing was that the same query was working when calling it from the PC (TeraTerm connected to the WiFly board) but would not when it was the AtMega microcontroller sending the commands !
Here are 2 threads with more details:
http://groups.google.com/group/nycresistormicrocontrollers/browse_thread/thread/686b328bc10a8b36
http://groups.google.com/group/london-hack-space/browse_thread/thread/4c40f6abc6dbad74
And by the way, a big THANK YOU to members of both the NYC and London hack spaces for their very valuable help !
Also, from what the roving networks support was saying, the HTTP mode should simply do this:
<remote string><your data><SPACE>HTTP/1.0\nHost:<SPACE><the hostname in config>\n\n
which is what the HTTP standard specifies…
In any case, the solution was to simply go into “TCP mode” (set ip proto 2 , the factory default) and manually construct the HTTP request, as you’ll see in the attached code.
And after a little more frustration, due to the fact that I had reverted the board to factory defaults and had forgotten to remove the “remote string”, which meant that it kept adding “*HELLO*” to whatever I was sending … lol 🙂 , I got it working!
Again, things look frustratingly simple now, but the very confusing thing with this 2nd problem was that for some reason the weather.yahooapis.com server would happily work in spite of the extra “*HELLO*” in the request, whereas search.twitter.com would not…
The Beautiful
Code is always beautiful 🙂
Here is what I had to add to the basic firmware.
– wiFly.h & .c – deal with controlling the WiFi board, and constructing the HTTP requests
– textParser.h & .c– deal with parsing the text or XML outputs from the servers and search for data between 2 tags
– serialMessages.h & .c – deal with putting all this together and keep track of 3 different connections and parsers, one for each service
– the few methods that I had to add to the main iv.c code which do the scrolling and periodically call the updates from the servers
– and finally the util.h & .c files, which are not mine, but I put here because I have done several modifications (don’t dare calling them “improvements” ! 🙂 )
If you’re really interested and want to replicate this mod without having to put together and compile the code yourself, then let me know and I could definitely send you a HEX file.
_________________________ wiFly.h /* * wiFly.h * * Created: 17/09/2011 Author: trandi */ #ifndef WIFLY_H_ #define WIFLY_H_ #include "util.h" char wf_httpOpen(const char* server, const char* page); #endif /* WIFLY_H_ */ _________________________ wiFly.c /* * wiFly.c * * Created: 17/09/2011 Author: trandi */ #include "wiFly.h" #include "util.h" #include <util/delay.h> #include <string.h> uint8_t findInResponse(const char * str){ if(uart_waitChar(1000)){ // wait for the first char char ch = uart_getchar(); while(ch != 0 && ch != str[0]){ uart_waitChar(300); ch = uart_getchar(); } if(ch != 0){ for(uint8_t offset = 1; offset < strlen(str); offset ++){ uart_waitChar(300); if(uart_getchar() != str[offset]) return 0; // wrong char found } return 1; // good, found what we were looking for } } // nope, not good.... return 0; } #define CMD(x) uart_emptyReadBuffer();uart_puts_nl(x); /* * Immediately afterwards the response will be available on the serial link ! * It waits a while for data to come back... */ char wf_httpOpen(const char* server, const char* page){ CMD("exit") CMD("close") _delay_ms(250); uart_puts("$$$"); // enter command mode _delay_ms(250); CMD("ver") if(! findInResponse("WiFly Ver")) return 'v'; uart_puts("open "); uart_puts(server); uart_puts_nl(" 80"); if(! findInResponse("*OPEN*")) return 'o'; uart_puts("GET "); uart_puts(page); uart_puts(" HTTP/1.0\r\nHost: "); uart_puts(server); uart_puts_nl("\r\n"); return 'n'; } _________________________ textParser.h /* * textParser.h * * Created: 15/09/2011 Author: trandi */ #ifndef TEXTPARSER_H_ #define TEXTPARSER_H_ #include "util.h" #define TEXT_DELIM '"' #define TEXT_SEP '-' typedef struct { const char* server; const char* page; const char* tagBegin; const char* tagEnd; uint8_t useTextDelimiters; uint8_t* usedTextIndices; uint8_t usedTextIndicesSize; uint8_t tagBeginSize; uint8_t tagEndSize; uint8_t tagBeginPos; uint8_t tagEndPos; uint8_t currentTextIsValid; uint8_t currentTextIndex; char msg[MAX_MSG_SIZE]; uint8_t msgSize; char err; uint8_t diffCharsCount; } text_parser_struc; void tp_init(text_parser_struc* tp, const char* server, const char* page, const char* tagBegin, const char* tagEnd, uint8_t useTextDelimiters, uint8_t* usedTextIndices, uint8_t usedTextIndicesSize); void tp_start(text_parser_struc* tp); uint8_t tp_feedChar(text_parser_struc* tp, char newChar); uint8_t tp_isMsgReady(text_parser_struc* tp); #endif /* TEXTPARSER_H_ */ _________________________ textParser.c /* * textParser.c * * Created: 15/09/2011 Author: trandi */ #include <avr/pgmspace.h> #include <string.h> #include <ctype.h> #include "textParser.h" void tp_init(text_parser_struc* tp, const char* server, const char* page, const char* tagBegin, const char* tagEnd, uint8_t useTextDelimiters, uint8_t* usedTextIndices, uint8_t usedTextIndicesSize){ tp->server = server; tp->page = page; tp->tagBegin = tagBegin; tp->tagEnd = tagEnd; tp->useTextDelimiters = useTextDelimiters; tp->tagBeginSize = strlen(tagBegin); tp->tagEndSize = strlen(tagEnd); tp->usedTextIndices = usedTextIndices; tp->usedTextIndicesSize = usedTextIndicesSize; // default values tp->err = 'a'; /* uart_puts("Text Parser initialised with: "); uart_puts(tagBegin); uart_puts(tagEnd); uart_puts("\n\r"); */ } void tp_start(text_parser_struc* tp){ tp->tagBeginPos = 0; tp->tagEndPos = 0; tp->msgSize = 0; // if we don't use text delimiters then ALL text inside is valid tp->currentTextIsValid = tp->useTextDelimiters ? 0 : 1; tp->currentTextIndex = 0; tp->diffCharsCount = 0; // assume the message hasn't changed... storeChar(tp, TEXT_SEP);storeChar(tp, TEXT_SEP);storeChar(tp, TEXT_SEP); } uint8_t tp_isMsgReady(text_parser_struc* tp){ return ((tp->tagEndPos >= tp->tagEndSize) || (tp->msgSize >= MAX_MSG_SIZE)); } void storeChar(text_parser_struc* tp, char ch){ if(ch != tp->msg[tp->msgSize]){ tp->msg[tp->msgSize] = ch; tp->diffCharsCount ++; } tp->msgSize++; } uint8_t tp_feedChar(text_parser_struc* tp, char newChar){ // can't store anymore chars, no point in continuing ! if(tp_isMsgReady(tp)) return 1; if(tp->tagBeginPos < tp->tagBeginSize){ // still looking for the beginning tag if(newChar == tp->tagBegin[tp->tagBeginPos]){ tp->tagBeginPos ++; }else{ uint8_t prevPos = tp->tagBeginPos; // false hope, wrong tag, start from the beginning tp->tagBeginPos = 0; // start from the beginning with the latest chars received, which we know are the same as the beginning of _tagBegin. In case there are repetitions in the tag for(uint8_t i=1; i<prevPos; i++) tp_feedChar(tp, tp->tagBegin[i]); } }else if(tp->tagEndPos < tp->tagEndSize){ if(tp->useTextDelimiters){ if(tp->currentTextIsValid){ // in the middle of valid text if(TEXT_DELIM == newChar){ // end of valid text tp->currentTextIsValid = 0; tp->currentTextIndex ++; }else if (tp->usedTextIndices[tp->currentTextIndex]){ // normal data, store it storeChar(tp, newChar); } }else{ if(TEXT_DELIM == newChar){ // beginning of valid text tp->currentTextIsValid = 1; // add a delimitator with the previous text if (tp->usedTextIndices[tp->currentTextIndex]) { storeChar(tp, TEXT_SEP); } }else { if(newChar == tp->tagEnd[tp->tagEndPos]){ // this might be the end tp->tagEndPos ++; if(tp->tagEndPos >= tp->tagEndSize){ storeChar(tp, 0); //end of string } }else{ uint8_t prevPos = tp->tagEndPos; // false hope, wrong tag, start from the beginning tp->tagEndPos = 0; // start from the beginning with the latest chars received, which we know are the same as the beginning of _tagBegin. In case there are repetitions in the tag for(uint8_t i=1; i<prevPos; i++) tp_feedChar(tp, tp->tagEnd[i]); } } } }else { //! tp->useTextDelimiters if(newChar == tp->tagEnd[tp->tagEndPos]){ // this might be the end tp->tagEndPos ++; if(tp->tagEndPos >= tp->tagEndSize){ storeChar(tp, 0); //end of string } }else if (tp->tagEndPos > 0) { // we thought we started the end tag and actually not... uint8_t prevPos = tp->tagEndPos; // false hope, wrong tag, start from the beginning tp->tagEndPos = 0; // start from the beginning with the latest chars received, which we know are the same as the beginning of _tagBegin. In case there are repetitions in the tag for(uint8_t i=1; i<prevPos; i++) tp_feedChar(tp, tp->tagEnd[i]); }else { // normal data, in the middle of the text store it storeChar(tp, newChar); } } } // hasn't yet finish, there's only one way of finishing... return tp_isMsgReady(tp); } _________________________ serialMessages.h /* * serialMessages.h * * Created: 17/09/2011 Author: trandi */ #ifndef SERIALMESSAGES_H_ #define SERIALMESSAGES_H_ typedef struct { uint8_t time_s; uint8_t time_m; uint8_t time_h; } time_date_struc; void sm_init(void (*funcSignalUpdt)(void)); time_date_struc* sm_getTime(); char* sm_getMsg(uint8_t switchMode); void sm_updateTwitter(); void sm_updateWeather(); #endif /* SERIALMESSAGES_H_ */ _________________________ serialMessages.c /* * serialMessages.c * * Created: 16/09/2011 Author: trandi */ #include <util/delay.h> #include "serialMessages.h" #include "util.h" #include "textParser.h" #include "wiFly.h" #include <stdlib.h> static char ERR[] = {'e', 'r', 'r', ' ', ' ', ' ', ' ', ' ', 0}; #define setErr(x, y) ERR[4] = x; ERR[5] = y static uint8_t UTI_YAHOO_WEATHER[] = {1, 0, 1, 1, 1, 0}; static uint8_t UTI_TWITTER[] = {1}; text_parser_struc _yahooWeatherParser; text_parser_struc _twitterParser; text_parser_struc _timeParser; time_date_struc _timeDate; // pointer to the function to call when we receive a new message void (*funcSignalUpdate)(void); uint8_t _mode = 0; // 0 - weather, 1 - twitter void sm_init(void (*funcSignalUpdt)(void)){ funcSignalUpdate = funcSignalUpdt; tp_init(&_yahooWeatherParser, "weather.yahooapis.com", "/forecastrss?w=44418&u=c", // London, in Celsius "<yweather:forecast", // tag begin "/>", // tag end 1, // do use the '"' values delimiters, next params are used UTI_YAHOO_WEATHER, // which bits of text to use 6 // how many bits have we defined ); tp_init(&_twitterParser, "search.twitter.com", "/search.json?q=arduino&rpp=1", "\"text\"", // tag begin "\"to_user_id\"", // tag end 1, // 0 if we wanted NO '"' text delimiters, next 2 params are ignored ! UTI_TWITTER, // which bits of text to use 1 // how many bits have we defined ); tp_init(&_timeParser, "www.timeanddate.com", "/worldclock/city.html?n=136", "=big>", // tag begin "</strong>", // tag end 0, // 0 if we wanted NO '"' text delimiters, next 2 params are ignored ! 0, // which bits of text to use 0 // how many bits have we defined ); } void getSerialMsg(text_parser_struc* tp){ tp->err = '1'; if(uart_waitChar(3000)){ // be generous with the initial wait ! tp_start(tp); while(uart_waitChar(1000) && ! tp_feedChar(tp, uart_getchar())); tp->err = '2'; if(tp_isMsgReady(tp)) tp->err = 'n'; // everything ok // the message is done, but empty the buffer uart_emptyReadBuffer(); } } char* sm_getMsg(uint8_t switchMode){ if(switchMode){ _mode = ! _mode; } if(_mode){ setErr('y', _yahooWeatherParser.err); return _yahooWeatherParser.err == 'n' ? _yahooWeatherParser.msg : ERR; }else{ setErr('w', _twitterParser.err); return _twitterParser.err == 'n' ? _twitterParser.msg : ERR; } } void sm_updateTwitter(){ _twitterParser.err = wf_httpOpen(_twitterParser.server, _twitterParser.page); if(_twitterParser.err == 'n') { getSerialMsg(&_twitterParser); // call the signal only if there are at least 10 different chars. For some reason there can be small diffs in messages... if(_twitterParser.diffCharsCount > 10){ _twitterParser.diffCharsCount = 0; funcSignalUpdate(); } } } void sm_updateWeather(){ _yahooWeatherParser.err = wf_httpOpen(_yahooWeatherParser.server, _yahooWeatherParser.page); if(_yahooWeatherParser.err == 'n') { getSerialMsg(&_yahooWeatherParser); } } time_date_struc* sm_getTime(){ _timeParser.err = wf_httpOpen(_timeParser.server, _timeParser.page); if(_timeParser.err == 'n') { getSerialMsg(&_timeParser); // the msg should be something like Saturday, "24 September 2011, 09:31:40 " extract hours and minutes char num[2]; num[0] = _timeParser.msg[_timeParser.msgSize - 9]; num[1] = _timeParser.msg[_timeParser.msgSize - 8]; _timeDate.time_h = atoi(num); num[0] = _timeParser.msg[_timeParser.msgSize - 6]; num[1] = _timeParser.msg[_timeParser.msgSize - 5]; _timeDate.time_m = atoi(num); num[0] = _timeParser.msg[_timeParser.msgSize - 3]; num[1] = _timeParser.msg[_timeParser.msgSize - 2]; _timeDate.time_s = atoi(num); }else{ setErr('t', _timeParser.err); _timeDate.time_h = 0; _timeDate.time_m = 0; _timeDate.time_s = 0; } return &_timeDate; } _________________________ util.h #ifndef UTIL_H_ #define UTIL_H_ //BRR = (F_CPU / 16 / BaudRate ) - 1 #if (F_CPU == 16000000) #define BRRL_2400 416 // for 16MHZ #define BRRL_9600 103 // for 16MHZ #define BRRL_19200 52 // for 16MHZ #define BRRL_115200 8 #elif (F_CPU == 8000000) #define BRRL_2400 207 #define BRRL_9600 52 #define BRRL_19200 26 #define BRRL_115200 3 #endif #include <inttypes.h> // very tricky, uses a lot of space ! // for some reason IF this gets too big and the "Data" part of the program goes beyond 90%, // the text parser for yahoo weather doesn't work anymore... don't ask WHY !? 🙂 #define MAX_MSG_SIZE 60 //void delay_ms(unsigned char ms); void delay_10us(uint8_t us); void delay_s(uint8_t s); int uart_putchar(unsigned char c); void uart_init(uint16_t BRR); unsigned char uart_getchar(void); uint8_t uart_isCharAvailable(void); void uart_emptyReadBuffer(void); uint8_t uart_waitChar(uint16_t timeoutMs); void uart_putc_hex(uint8_t b); void uart_putw_hex(uint16_t w); void uart_putdw_hex(uint32_t dw); void uart_putw_dec(uint16_t w); void uart_putdw_dec(uint32_t dw); void uart_puts(const char* str); void RAM_putstring(char *str); void ROM_putstring(const char *str, uint8_t nl); #define uart_puts_nl(x) uart_puts(x);uart_puts("\r\n") #define putstring(x) ROM_putstring(PSTR(x), 0) #define putstring_nl(x) ROM_putstring(PSTR(x), 1) #define NOP asm("nop"); #define nop asm volatile ("nop\n\t") #endif /* UTIL_H_ */ _________________________ util.c /*************************************************************************** Ice Tube Clock firmware August 13, 2009 (c) 2009 Limor Fried / Adafruit Industries Modified by Tr@ndi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <avr/pgmspace.h> #include "util.h" void delay_10us(uint8_t ns) { uint8_t i; while (ns != 0) { ns--; for (i=0; i< 30; i++) { nop; } } } void delay_s(uint8_t s) { while (s--) { _delay_ms(1000); } } // setup the main UART void uart_init(uint16_t BRR) { UBRR0 = BRR; // set baudrate counter UCSR0B = _BV(RXEN0) | _BV(TXEN0); // Enable receiver and transmitter UCSR0C = 3<<UCSZ00; // Frame format: 8data, No parity, 1stop bit DDRD |= _BV(PD1); DDRD &= ~_BV(PD0); } int uart_putchar(unsigned char c) { loop_until_bit_is_set(UCSR0A, UDRE0); UDR0 = c; return 0; } unsigned char uart_getchar(void) { //if (uart_isCharAvailable()) return UDR0; //return 0; } uint8_t uart_isCharAvailable(void) { return (UCSR0A & _BV(RXC0)); } void uart_emptyReadBuffer(void){ char ch; while(uart_waitChar(100)) ch = uart_getchar(); } uint8_t uart_waitChar(uint16_t timeoutMs){ for(uint16_t i=0; i<timeoutMs && !uart_isCharAvailable(); i++) _delay_ms(1); return uart_isCharAvailable(); } void ROM_putstring(const char *str, uint8_t nl) { for (uint8_t i=0; pgm_read_byte(&str[i]); i++) { uart_putchar(pgm_read_byte(&str[i])); } if (nl) { uart_putchar('\n'); uart_putchar('\r'); } } void uart_puts(const char* str) { while(*str){ uart_putchar(*str++); } } void uart_putc_hex(uint8_t b) { /* upper nibble */ if((b >> 4) < 0x0a) uart_putchar((b >> 4) + '0'); else uart_putchar((b >> 4) - 0x0a + 'a'); /* lower nibble */ if((b & 0x0f) < 0x0a) uart_putchar((b & 0x0f) + '0'); else uart_putchar((b & 0x0f) - 0x0a + 'a'); } void uart_putw_hex(uint16_t w) { uart_putc_hex((uint8_t) (w >> 8)); uart_putc_hex((uint8_t) (w & 0xff)); } void uart_putdw_hex(uint32_t dw) { uart_putw_hex((uint16_t) (dw >> 16)); uart_putw_hex((uint16_t) (dw & 0xffff)); } void uart_putw_dec(uint16_t w) { uint16_t num = 10000; uint8_t started = 0; while(num > 0) { uint8_t b = w / num; if(b > 0 || started || num == 1) { uart_putchar('0' + b); started = 1; } w -= b * num; num /= 10; } } void uart_putdw_dec(uint32_t dw) { uint32_t num = 1000000000; uint8_t started = 0; while(num > 0) { uint8_t b = dw / num; if(b > 0 || started || num == 1) { uart_putchar('0' + b); started = 1; } dw -= b * num; num /= 10; } } _________________________ ADDED this bit AT THE END of iv.c #define SCROLL_SPEED 200 // milliseconds #define TIME_MSG_MODES_SWITCH_SPEED 15000 // milliseconds #define TIME_MSG_TIME_UPDATE 3600 // seconds #define TIME_MSG_WEATHER_UPDATE 3600 // seconds #define TIME_MSG_TWITTER_UPDATE 60 // seconds // only beeps if the alarm is on, so that we can control the stuff... void myBeep() { if(alarm_on){ beep(1500, 1);beep(3500, 1);beep(5000, 1); } } // Scrolling of the message. void scrollMsg(){ // Move the text uint8_t firstElem = display[1]; for(uint8_t i = 1; i < _msgSize; i++) display[i] = display[i+1]; display[_msgSize] = firstElem; } // alternate between SHOW_TIME and SHOW_MSG void alternateTimeMsgModes(){ //tick(); if(displaymode == SHOW_TIME){ displaymode = SHOW_MSG; display_str(sm_getMsg(1)); }else if(displaymode == SHOW_MSG){ displaymode = SHOW_TIME; _msgSize = 0; //stop scrolling ! } } void updateTime(){ time_date_struc* timeDate_s = sm_getTime(); time_h = timeDate_s->time_h; time_m = timeDate_s->time_m; time_s = timeDate_s->time_s; } // called from an interrupt, every millisecond, for short and vital tasks void customInterruptPeriodicActions(){ if(_msgSize >= DISPLAYSIZE && _myMillisCounter % SCROLL_SPEED == 0) scrollMsg(); if(_myMillisCounter % TIME_MSG_MODES_SWITCH_SPEED == 0) alternateTimeMsgModes(); } uint16_t _lastPeriodicActionSecs = 0; // called from the main loop, for LONGER running tasks void customPeriodicActions(){ // don't even do the next calculations if the seconds have not changed if(_lastPeriodicActionSecs != _mySecondsCounter){ if(_mySecondsCounter % TIME_MSG_TIME_UPDATE == 0) updateTime(); if(_mySecondsCounter % TIME_MSG_TWITTER_UPDATE == 0) sm_updateTwitter(); if(_mySecondsCounter % TIME_MSG_WEATHER_UPDATE == 0) sm_updateWeather(); _lastPeriodicActionSecs = _mySecondsCounter; }
In iv.c, the 2 methods that “inject” my mod into the rest of the code are
- customPeriodicActions(), called at the beginning of the infinite look within the main() method
- and customInterruptPeriodicActions(), called in the middle of SIGNAL (SIG_OVERFLOW0) {} which occurs roughly every millisecond
And here is the final photo, installed in its rightful place, along my collection of miniature single malts 🙂
As usual, if you’ve made it up to here and had the patience to read all my ramblings, then don’t hesitate to leave a commend and give some feedback…
Update 27Feb2012 _ Here are some improvements I need/want to make:
1) RE-CONNECT when wireless is turned back on !– SLEEP action
– set wlan join 1 – will try to join lola when comes back from sleep (“This policy is used when the module powers up, including wake up from the sleep timer “)– use REBOOT ? if sleep doesn’t work…2) paint the LEDs (especially the blue one)3) do NOT update the msg if error, keep the old ones – Already DONE4) if can’t get the messages (WiFly down) then update the timers and keep trying every 20 seconds or so for both the time and the weather !
Hi dan,
I am not sure if you can notice my reply.
I have to finish my project with RN-134 and RN-131 these months. The only experience related to this kind of work is that i wrote some codes on zigbee sensor nodes. I have no idea about RN-131 and I do not know where to find the answers.
1.
When I connect the device with my PC and reboots this device, there are something meangless on TeraTerm screen. so i think this device do connect to my PC. But when I try to enter the command mode using “$$$” quickly and without as the user guide said, nothing happened. On the board, the blue Led is on, red LED blinks quickly while the green on blink slowly.
2.
how can i control or send commands to RN-131 with my own implementation. In my last coursework about zigbee, i wrote my code, compile it and then upload it to the device. what is the process in RN-131 device? I want PC send command to RN-131 periodically.
If you are not available to answer me, can you introduce me some books or materials that i can read?
Wish you could receive my message.
Hi,
Thank you for your message.
It’s been a while since I did this project so I don’t remember all the details, but from what I vaguely recollect:
1. have you checked the transfer speed (bps rate) ? It sounds like a problem with that…
2. I’m not sure I understand what you want to do? Do you want to run your own code on the RN131? In my project I don’t run any code on the device itself, it just works as an internet access point for the AVR micro controller that is the brain of the clock. It’s this MCU, not the RN131 itself that makes http requests and process the data. The RN131 works as an internet router if you want…
Hope this helps,
dan
Pingback: Innovative Clock that Connects to the Internet | HACKOLOG - Amazing Hacks and Mods
like the last picture !!
I’ve just bought an Arduino Wireless Shield with a Wifly RN-XV module. My objective is to pilot a dagu rover 5 with an android app !
I’m new to arduino programming, so i think this will not be an easy task for me 😛
If you have good tutorials links, it could be nice 🙂
Pingback: Simple serial transceiver – Aurel RTX-MID « Robotics / Electronics / Physical Computing
hey trandi,
first of all pls pardon me for any misspelling or linguistic inaccuracy(ain’t native-speaking english)
I’m stuck between a Basic Atom Pro 40 Microcontroller (http://www.basicmicro.com/Basic-ATOM-Pro-40-M_p_63.html) and the Android IOIO. The basic idea is to read information from the Basic Atom Pro on an I2C-Bus and then display on the Android device (Acer Iconia Tab A500).
To be honest, I’m a total newbie concerning the IOIO. Nevertheless, I have to get this going, no matter what the cost.
I tried to go through your coding, even though it was not that revealing to me.
If you could get me a first draft or any written assistance, I’d be extremely thankful.
The point is, I basically know the coding syntax but don’t really understand how all this works, that’s why I stumble on every obstacle.
thanks,
Bohlsche
Hi Bohlsche,
I might have misunderstood you, but I don’t think you can compare the Basic Atom Pro with the IOIO.
Simply because the Atom can NOT be connected DIRECTLY to and Android device !
If you’re point is not comparing them but simply make them talk over I2C, then it should be fairly straight forward.
Have you had a look at these 2 posts of mine ?
They are both about getting information from a Wii device, over I2C to the IOIO and then to the Android phone.
You have the exact code example and all the explanations.
If you have any concrete questions about the IOIO/Android code then let me know (preferably on one of these 2 posts)
However I can’t really help you with the code on the Basic Atom side.
Dan
P.S. out of curiosity WHY did you choose to use a Basic Atom ?
The price doesn’t seem specially attractive, and I vaguely remember from 5 years ago when I used a Basic Atom Pro 24, that it was frustratingly hard to program due to the lack of community and examples around… maybe things have changed, hence my curiosity ….
hi trandi,
thanks for your quick reply 🙂
to put this straight: a friend of mine used the Basic Atom Pro to control a hydraulic anti-roll system on a model tractor.
By now we are trying to install that same system he conceived on a real tractor.
(my homeland’s alpine so this would be quite in demand)
We still use the Atom to control all the mechanics, so we need the IOIO only to read information from the Atom (e.g. inclination angle, oil pressure, …) and display them on the Acer Tab he insists on mounting on the tractor (to “revolutionize” agriculture … ^^).
By way of trial we managed to apply the I2COUT code on the Atom, but to program the IOIO turned out to be much more complicated, since we both don’t have experience with that.
Nevertheless, we have to get this going, no matter the cost. So if you could get us a short draft on how to read on the Atom’s I2COUT or at least get across how the coding works, pls let us know.
If you need any code samples or syntax description from the Basic Atom, pls contact us. We’ve spend to much time on all this to drop it now.
For any further aid, we won’t hesitate on an appropriate recompense.
thanks,
Bohlsche
Buy the time choosing the Atom Pro 40 I wasn’t involved yet, so actually I don’t know the “why”. All I know is that now it would be much to laborious to reorganize.
I’ve just sent you an e-mail directly to your e-mail address.
dan
hi Dan,
pls check your e-mail box.
I’ve replied directly on your last message.
Bohlsche
Pingback: Electronics-Lab.com Blog » Blog Archive » Wifi Ice Tube Clock
Very interesting, gave me some good ideas for my next project!
Pingback: Wifi Ice Tube Clock « adafruit industries blog
Pingback: Punerea Twitter într-un ceas VFD | ro-Stire
Nice work, just ordered the wifly board to get this going with my clock!
Pingback: Putting Twitter in a VFD clock | You've been blogged!
Pingback: Putting Twitter in a VFD clock | Earn Money Online
Pingback: Putting Twitter in a VFD clock - Hack a Day
Oh, here you can probably say “mostly sunny” without the Internet 🙂
California is great so far and is expected to be even greater once I found out about all the cool stuff. Keep those great projects coming, and don’t let your IOIO gather too much dust – very exciting new features are just around the corner!
You probably don’t need the Internet in the UK to say “mostly cloudy” 🙂
Nice build!
🙂 just laughed by myself for an entire 5 mins… you will be surprised, but this week-end we had really nice weather, with a few clouds it’s true but really nice, and, hold your breath, this week the forecast announces 27degrees ! This is hotter than summer !! 🙂
Dan
P.S. how’s California ?