Anthony's WiFi Stock Ticker Tape
Another fun project I am currently working on is a stock ticker tape for my dad. Well, it’s not really a “tape” it’s a display, but the idea is the same. This small device hangs on the wall and shows my dad how his stock portfolio is doing in real-time. Like most of my latest projects, it has an ATmega328P embedded processor in it running the Arduino boot loader.
I like to use the Boarduino PC board (see Schematic) as a daughter board. For only $17.50, you get the processor, power regulator, clock, LEDs and board you can solder wires to. It's a great buy and I always keep a few kits in my supplies. Another great find for this project was the LTM-8647AP 14-segment display chips. I wanted to use big bright LEDs, but most displays have one pin per segment. With 14-segments per character and 20 characters, that would have been 280 pins to solder to and drive. I figured there must be a better way, and there was. The 8647 not only had 2 digits (28 segments), but it has an internal shifter in it so that you can transfer the information to it serially. Now the Arduino only needed to drive a Clock, Data and 10 selection wires. This was still to many, so I used a SN74159 4-Line to 16-line Demultiplexer to enable each display chip. Now the whole display only used 6 output pins (clock, data and 4 address wires).
The next problem was to come up with a character set. Not finding one on the Internet, I made my own. Originally for a 16-segment display:Look how nice the lowercase letters look. Unfortunately, the only serial LED display chip I could find had only 14 (not 16) segments. That means that the top and bottom are one segment each (not 2). So I had to change the character set to this.
The lowercase "g", "p", "s" look weird and the "[]" and "{}" brackets went from looking wonderful to terrible. But what can you do? Moving on, I wrote a program to turn this into the following hex table:
/* */0x0000, /*!*/0x3001, /*"*/0x2020, /*#*/0x07A4, /*$*/0x5BA4, /*%*/0x13DA, /*&*/0x4D68, /*'*/0x0010, /*(*/0x0018, /*)*/0x0042, /***/0x01FE, /*+*/0x01A4, /*,*/0x0002, /*-*/0x0180, /*.*/0x0001, /*/*/0x0012, /*0*/0x7E12, /*1*/0x3010, /*2*/0x6D80, /*3*/0x7880, /*4*/0x3380, /*5*/0x4B08, /*6*/0x5F80, /*7*/0x7200, /*8*/0x7F80, /*9*/0x7380, /*:*/0x0024, /*;*/0x0022, /*<*/0x0118, /*=*/0x0980, /*>*/0x00C2, /*?*/0x6085, /*@*/0x6E90, /*A*/0x7780, /*B*/0x78A4, /*C*/0x4E00, /*D*/0x7824, /*E*/0x4F00, /*F*/0x4700, /*G*/0x5E80, /*H*/0x3780, /*I*/0x4824, /*J*/0x3C00, /*K*/0x0718, /*L*/0x0E00, /*M*/0x3650, /*N*/0x3648, /*O*/0x7E00, /*P*/0x6780, /*Q*/0x7E08, /*R*/0x6788, /*S*/0x5B80, /*T*/0x4024, /*U*/0x3E00, /*V*/0x0612, /*W*/0x360A, /*X*/0x005A, /*Y*/0x2384, /*Z*/0x4812, /*[*/0x3098, /*\*/0x0048, /*]*/0x0742, /*^*/0x0240, /*_*/0x0800, /*`*/0x0040, /*a*/0x0D44, /*b*/0x0F04, /*c*/0x0D00, /*d*/0x0D24, /*e*/0x0D02, /*f*/0x40A4, /*g*/0x4B24, /*h*/0x0704, /*i*/0x0004, /*j*/0x0422, /*k*/0x003C, /*l*/0x0024, /*m*/0x1584, /*n*/0x0504, /*o*/0x1D80, /*p*/0x4720, /*q*/0x4324, /*r*/0x0500, /*s*/0x4B04, /*t*/0x01A4, /*u*/0x0C04, /*v*/0x0402, /*w*/0x140A, /*x*/0x005A, /*y*/0x0054, /*z*/0x0902, /*{*/0x4942, /*|*/0x0600, /*}*/0x4898, /*~*/0x0050, /*¦*/0x7FFE
As you may have noticed, the first and last bits are unused for reasons that will become clear if you look at my
display driver C++ source code:
Getting messages to the display and even scrolling on the screen was only the beginning of this project. Next I needed to communicate with a server on the Internet, so I bought a WiFi Shield from AsyncLabs to communicate with my server. It's the one that actually gathers the stock info. Currently my server code is using Yahoo's data stream, but I'm switching to Google's Finance API. Google's API is much better and there is no 15-minute delay.
Now this is the fun part. I really didn't want to hard code display logic into this device forever. Maybe my dad would want a weather channel or messages from people like me. If so, I would have had to re-burn code by going down there, taking it apart and connecting it to my laptop. Ok, this is wouldn't have been that hard, but I wanted to solve the general problem of how to change the code remotely. I didn't want the problems or the risk of remotely burning in a new Kernel. What I wanted was a Virtual Machine! Like the one Java uses. Java started that way, but now it is far to big for such a small chip. I needed a VM that works in a few hundred bytes of memory. Thus was born the:
MackVM and MackASM
The MackVM is tiny, I mean it's absolutely miniscule. The whole run-time is only 276 lines (and that's mostly comments). Also the byte code that it interprets is also very compact. I can hear you out there saying, "Anthony, what in the hell are you talking about?" Well, it's really quite simple. If you want your embedded processor to run different code at different times, you don't re-burn the ROM each time. You put the program in RAM and interpret the data as code. This can be totally safe, reliable flexible and portable too.
Let me start with MackASM. MackASM is a new computer language that is very similar to the byte code that will be sent to the chip. It's a stack based assembler language that is really very simple, but can do anything (like a RISK architecture). One day I plan to modify a C compiler to generate my new byte code, but for now, there is only MackASM. I wrote a simple MackASM compiler using JavaCC. If you are interesting, here is the EBNF for the language. However, the MackASM Programmers Guide is a much easier read. Not that it's cakewalk or even finished or anything, but you can at least get the idea of how the language generates the byte code with this MackAssembler. (this Java jar file requires JRE6 to run). Here's a test file that compiles, and an early version on the stock ticker code that is not finished yet. I haven't finished it yet, but I hope to get back to it soon. Here is all the source code in its current non-working state. Feel free to use it or any part of it. If you fix something, tell me about it.