Bitcoin Price Ticker
This is the second (and final) iteration of my office bitcoin price ticker. The concept is simple. Every ten minutes the current price and trading volume for bitcoin is pulled from the Interwebs, parsed and then displayed on a wide led matrix.
Here is my parts list:
- 1 x Arduino Yun (I don't think they make this anymore, but I had it already. Would probably have been easier with a more up to date board (feather?)).
- 1 x Adafruit Panel Mount 2.1mm DC barrel jack ( https://www.adafruit.com/products/610)
- 1 x 5V 2A (2000mA) switching power supply (https://www.adafruit.com/products/276)
- 6 x Adafruit 16x8 1.2" LED Matrix + Backpack - Ultra Bright Round Blue LEDs (https://www.adafruit.com/products/2039)
- 2 x 100K potentiometers and some chrome knobs from the closing sale of our local Radio Snack :-(.
- Some hookup wire and an oak board.
- An on/off switch.
Make a 96x8 Matrix Out of Six 16x8 I2C Matrixes
For the bitcoin price information to be readable and scroll along like a stock ticker, I wanted a wide matrix for display. Just one or two 16x8 matrixes would not do!. Fortunately, you can have many small (16x8) matrixes controlled via I2C. On the back of each matrix a unique I2C address can be set via a solder joint on the back of the matrix: See https://learn.adafruit.com/adafruit-led-backpack/c...
In theory, you could have eight matrixes on one I2C connection (I used six). Just place your small matrixes next to each other, work up a little program to make them function like one matrix, and you should be off and running. But alas, when you put these smaller matrixes together, there is a big gap between them.
However, one end of the 16x8 matrix doesn't have any essential circuits and can be cut off with a cutting wheel on a Dremel tool. I did this to five of the matrixes and which allowed the matrixes to be lined up very close. After testing each matrix to make sure it worked, I used super glue to join the matrixes together. You don't want to find out after you have superglued them that there is an issue with your wiring!
I also glued several posts onto the matrixes to support them. Later I'll make some holes in my mounting board to accept these stands. If you have some other form factor that you want to use, then you can make whatever accommodations you want.
I wired up the connections on the back of the matrixes in parallel. I then wired the 96X8 matrix to an Arduino Uno to test the large matrix. NOTE: Don't try to power the large matrix from the Arduino. I didn't calculate the power draw but I imagine it will be way too much for the Arduino.
To test, I just wired the SDA on the matrix to pin Analog 4 on the Arduino, and the SCL to Analog 5 on the Arduino. (I used an Uno to test, and a YUN on the final board. The pins are different on the YUN.)
Below is the program I came up with to display a string on the large matrix. There is probably a more elegant way to make all the matrixes work together, but this made sense to me. What I am doing is setting an offset for each 16x8 matrix based on their location in the 96x8 matrix. So each matrix will be displaying the string all the time, but as the string starts on the right side of the large matrix, the smaller matrixes are offset so that they are displaying the string at a position "off screen". Eventually, they will work their way up to the start of the string. The position is updated each time through the loop (via updateTicker()). When the message has scrolled off the screen (xposition =17) then the whole things starts over. I used the comparison of milliseconds to trigger the updates since I will want to be doing other stuff between updates (such as pulling the updated bitcoin prices, adjusting brightness, etc.). If I used a wait, the microprocessor would not be able to go do "other stuff."
If my explanation doesn't make and sense, hopefully the code does!
/*************************************************** This is a library for our I2C LED Backpacks Designed specifically to work with the Adafruit 16x8 LED Matrix backpacks ----> <a href="http://www.adafruit.com/products/2035"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2036"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2037"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2038"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2039"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2040"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2041"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2042"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2043"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2044"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> ----> <a href="http://www.adafruit.com/products/2052"> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> </a> These displays use I2C to communicate, 2 pins are required to interface. There are multiple selectable I2C addresses. For backpacks with 2 Address Select pins: 0x70, 0x71, 0x72 or 0x73. For backpacks with 3 Address Select pins: 0x70 thru 0x77 Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, all text above must be included in any redistribution ****************************************************/ #include #include "Adafruit_LEDBackpack.h" #include "Adafruit_GFX.h" Adafruit_8x16matrix firstmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix secondmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix thirdmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix fourthmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix fifthmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix sixmatrix = Adafruit_8x16matrix(); long previousMillisTickerScroll = 0; // for updating the ticker long previousMillisupdatePrice = 0; // for updating the price int brightness = 20;//set inital brightness int xposition = 17; //one row to the right of the start of the text int scrollspeed =40; //set inital speed // How fast to scroll will adjust with a pot later String lastmsg = ""; String msg1 = "Hello World! This is a long test message to see how this works"; int msgStringLength = msg1.length(); void setup() { //set the addresses of the matrizes //A 70 //B 71 //C 72 //D 74 //E 77 //F 73 firstmatrix.begin(0x73); secondmatrix.begin(0x77); thirdmatrix.begin(0x74); fourthmatrix.begin(0x72); fifthmatrix.begin(0x71); sixmatrix.begin(0x70); Wire.begin(); //see if we can put this in the setup firstmatrix.setTextSize (1); firstmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely firstmatrix.setTextColor(LED_ON); firstmatrix.setRotation(1); secondmatrix.setTextSize(1); secondmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely secondmatrix.setTextColor(LED_ON); secondmatrix.setRotation(1); thirdmatrix.setTextSize(1); thirdmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely thirdmatrix.setTextColor(LED_ON); thirdmatrix.setRotation(1); fourthmatrix.setTextSize(0); fourthmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely fourthmatrix.setTextColor(LED_ON); fourthmatrix.setRotation(1); fifthmatrix.setTextSize(0); fifthmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely fifthmatrix.setTextColor(LED_ON); fifthmatrix.setRotation(1); sixmatrix.setTextSize(0); sixmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely sixmatrix.setTextColor(LED_ON); sixmatrix.setRotation(1); } void loop() { unsigned long currentMillis = millis(); if(currentMillis - previousMillisTickerScroll > scrollspeed) { previousMillisTickerScroll = currentMillis; updateTicker(); } } void updateTicker(){ msgStringLength = msg1.length(); xposition = xposition-1; if (xposition <= -(64+(6*msgStringLength))) { xposition =17; //start over } firstmatrix.clear(); firstmatrix.setCursor(xposition+80,1); firstmatrix.print(msg1); firstmatrix.writeDisplay(); secondmatrix.clear(); secondmatrix.setCursor(xposition+64,1); secondmatrix.print(msg1); secondmatrix.writeDisplay(); thirdmatrix.clear(); thirdmatrix.setCursor(xposition+48,1); thirdmatrix.print(msg1); thirdmatrix.writeDisplay(); fourthmatrix.clear(); fourthmatrix.setCursor(xposition+32,1); fourthmatrix.print(msg1); fourthmatrix.writeDisplay(); fifthmatrix.clear(); fifthmatrix.setCursor(xposition+16,1); fifthmatrix.print(msg1); fifthmatrix.writeDisplay(); sixmatrix.clear(); sixmatrix.setCursor(xposition,1); sixmatrix.print(msg1); sixmatrix.writeDisplay(); // later we will use a pot to set the brightness and the speed of the scrol firstmatrix.setBrightness(brightness); secondmatrix.setBrightness(brightness); thirdmatrix.setBrightness(brightness); fourthmatrix.setBrightness(brightness); fifthmatrix.setBrightness(brightness); sixmatrix.setBrightness(brightness); }
Create a Housing or Mounting for the Large Matrix and Other Parts.
I didn't want to spend a lot of money on a housing, so I just cut an oak board to approx 2 foot long, routed the edges to make them round. drilled holes for two potentiometers, a switch, and a small hole to pass wires from the top of the board to the bottom. I used a combination stain/polyurethane to make it little pretty.
I then used a straight router bit to make an area in the back of the board to accommodate parts and wires, and to thin the board so the pots and switch could reach through. This works fine, but you have to go slow with the router.
Mount Matrix to Board, Wire Up, and Update Program to Display Bitcoin Price
To mount the matrix to the board, I first placed the board (with the posts) on the top of the board and marked the post locations. I then drilled holes larger than the posts, filled them with Glue (Loctite GO2® GLUE) and set the matrix back on the board. Let it dry for 24 hours.
The wiring is simple:
- Power to the POTs, matrix, and Arduino Yun (+ and -)
- Center of POTSs to Analog 0 and Analog 1 on the Arduino Yun.
- I2C from matrix to digital 2 and 3 on the Arduino .
I updated the Linux side of the Yun and then connected to my network. That way it could fetch data for the Arduino side of the board.
Below is my code. I struggled with several aspects of the program and bounced around the internet trying to find clues to making it work. Frankly, there is probably some code in here that is from another programmer that I forgot to acknowledge or make a comment about the source. For example, I am sure that I adopted someone else's code for the JSON parsing, but I can't remember where I saw it. That can be a problem with doing a project over a couple of months with just a few minutes here and there.
/*************************************************** This is a library for our I2C LED Backpacks Designed specifically to work with the Adafruit 16x8 LED Matrix backpacks ----> <a href="http://www.adafruit.com/products/2035"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2036"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2037"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2038"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2039"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2040"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2041"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2042"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2043"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2044"> http://www.adafruit.com/products/2035 </a> ----> <a href="http://www.adafruit.com/products/2052"> http://www.adafruit.com/products/2035 </a> These displays use I2C to communicate, 2 pins are required to interface. There are multiple selectable I2C addresses. For backpacks with 2 Address Select pins: 0x70, 0x71, 0x72 or 0x73. For backpacks with 3 Address Select pins: 0x70 thru 0x77 Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, all text above must be included in any redistribution ****************************************************/ /*************************************************** Yun password is arduino and accessed at "http://arduino.local" ****************************************************/ #include <Wire.h> #include "Adafruit_LEDBackpack.h" #include "Adafruit_GFX.h" #include <Bridge.h> #include <HttpClient.h> #include "RTClib.h" #include <ArduinoJson.h> Adafruit_8x16matrix firstmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix secondmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix thirdmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix fourthmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix fifthmatrix = Adafruit_8x16matrix(); Adafruit_8x16matrix sixmatrix = Adafruit_8x16matrix(); int speedPin = 0; //Pots to read for spead of scrolling int scrollspeed; //set inital speed int brightPin = 1; //pot to read for brightness int brightness = 20;//set inital brightness unsigned long howOftenToUpdatePrice = 600000; //set interval for updaint price 10 minutes = 600000 int xposition = 17; long previousMillisTickerScroll = 0; // for updating the ticker long previousMillisupdatePrice = 0; // for updating the price String lastmsg = "UNKNOWN "; String msg1 = ""; String ProperSeconds = ""; String ProperMinues = ""; String ProperHour = ""; String response =""; int msgStringLength = msg1.length(); void setup() { //set the addresses of the matrizes firstmatrix.begin(0x73); secondmatrix.begin(0x77); thirdmatrix.begin(0x74); fourthmatrix.begin(0x72); fifthmatrix.begin(0x71); sixmatrix.begin(0x70); Wire.begin(); //see if we can put this in the setup firstmatrix.setTextSize (1); firstmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely firstmatrix.setTextColor(LED_ON); firstmatrix.setRotation(1); secondmatrix.setTextSize(1); secondmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely secondmatrix.setTextColor(LED_ON); secondmatrix.setRotation(1); thirdmatrix.setTextSize(1); thirdmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely thirdmatrix.setTextColor(LED_ON); thirdmatrix.setRotation(1); fourthmatrix.setTextSize(0); fourthmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely fourthmatrix.setTextColor(LED_ON); fourthmatrix.setRotation(1); fifthmatrix.setTextSize(0); fifthmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely fifthmatrix.setTextColor(LED_ON); fifthmatrix.setRotation(1); sixmatrix.setTextSize(0); sixmatrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely sixmatrix.setTextColor(LED_ON); sixmatrix.setRotation(1); // Initialize Bridge Bridge.begin(); Serial.begin(9600); //need to wait a bit for the linino board to be ready to process data Serial.print("Waiting for Linino to boot"); for(int i=0;i<3000;i++){ //change to 3000 msg1 = "Waiting for Linino to boot !!"; updateTicker(); delay(10); } updatePrice(); } void loop() { // check to see if it's time to update the ticker; that is, if the // difference between the current time and last time you updated // the ticker is bigger than the interval at which you want to // update the ticker. scrollspeed = (analogRead(speedPin)/16)*8; // read the value from the sensor unsigned long currentMillis = millis(); if(currentMillis - previousMillisTickerScroll > scrollspeed) { previousMillisTickerScroll = currentMillis; updateTicker(); } if(currentMillis - previousMillisupdatePrice > howOftenToUpdatePrice) { previousMillisupdatePrice = currentMillis; updatePrice(); } } void updatePrice(){ response =""; xposition =17; //start the ticker counter over Process p; // Create a process and call it "p" p.begin("curl"); // Process that launch the "curl" command p.addParameter("-k"); p.addParameter("https://www.bitstamp.net/api/ticker"); // Add the URL parameter to "curl" p.run(); // Run the process and wait for its termination while (p.available() > 0) { char c = p.read(); Serial.print(c); response += c; } Serial.flush(); delay (500); char json[200]; StaticJsonBuffer<200> jsonBuffer; response.toCharArray(json, 200); JsonObject& root = jsonBuffer.parseObject(json); const char* last = root["last"]; const char* basictime = (root["timestamp"]); const char* volume = root["volume"]; DateTime CentralTime = atol(basictime); int RoundVolume = atoi(volume); if (!root.success()) { Serial.println("parseObject() failed"); msg1 = "Update Failed. Will Attempt in 10 min. Prev Bitcoin Price: "; msg1.concat (lastmsg); } else { Serial.println(last); Serial.println(CentralTime.day(), DEC); Serial.print(CentralTime.hour(),DEC); Serial.print(":"); Serial.print(CentralTime.minute(),DEC); Serial.print(":"); Serial.println(CentralTime.second(),DEC); Serial.println(volume); ProperSeconds =""; ProperSeconds.concat (CentralTime.second()); ProperMinues =""; ProperMinues.concat (CentralTime.minute()); ProperHour =""; ProperHour.concat (CentralTime.hour()); msg1 = "Current Bitcoin Price: "; lastmsg = ""; lastmsg.concat (last); lastmsg.concat ("USD Volume: "); lastmsg.concat (RoundVolume); lastmsg.concat (" Last Updated: "); lastmsg.concat (CentralTime.month()); lastmsg.concat ("/"); lastmsg.concat (CentralTime.day()); lastmsg.concat ("/"); lastmsg.concat (CentralTime.year()); lastmsg.concat (" "); if ((ProperHour.length())<2){ lastmsg.concat ("0"); } lastmsg.concat (ProperHour); lastmsg.concat (":"); if ((ProperMinues.length())<2){ lastmsg.concat ("0"); } lastmsg.concat (ProperMinues); lastmsg.concat (":"); if ((ProperSeconds.length())<2){ lastmsg.concat ("0"); } lastmsg.concat (ProperSeconds); lastmsg.concat (" GMT. Ticker by @JScottMO"); msg1.concat (lastmsg); } Serial.flush(); } void updateTicker(){ msgStringLength = msg1.length(); xposition = xposition-1; if (xposition <= -(64+(6*msgStringLength))) { xposition =17; //start over } firstmatrix.clear(); firstmatrix.setCursor(xposition+80,1); firstmatrix.print(msg1); firstmatrix.writeDisplay(); secondmatrix.clear(); secondmatrix.setCursor(xposition+64,1); secondmatrix.print(msg1); secondmatrix.writeDisplay(); thirdmatrix.clear(); thirdmatrix.setCursor(xposition+48,1); thirdmatrix.print(msg1); thirdmatrix.writeDisplay(); fourthmatrix.clear(); fourthmatrix.setCursor(xposition+32,1); fourthmatrix.print(msg1); fourthmatrix.writeDisplay(); fifthmatrix.clear(); fifthmatrix.setCursor(xposition+16,1); fifthmatrix.print(msg1); fifthmatrix.writeDisplay(); sixmatrix.clear(); sixmatrix.setCursor(xposition,1); sixmatrix.print(msg1); sixmatrix.writeDisplay(); brightness = (analogRead(brightPin)/64); firstmatrix.setBrightness(brightness); secondmatrix.setBrightness(brightness); thirdmatrix.setBrightness(brightness); fourthmatrix.setBrightness(brightness); fifthmatrix.setBrightness(brightness); sixmatrix.setBrightness(brightness); }