Bitcoin Price Ticker

by JScottMO in Circuits > Arduino

4884 Views, 20 Favorites, 0 Comments

Bitcoin Price Ticker

Bitcoin price ticker
FullSizeRender.jpg

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

Testing the large 96x8 matrix for bitcoin stock ticker
Cut off.jpg
together top.jpg
together back.jpg
cutting line.jpg
File Mar 06, 10 57 54 PM.jpeg

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.

File Mar 06, 10 57 36 PM.jpeg
File Mar 06, 10 57 03 PM.jpeg
File Mar 06, 10 50 42 PM.jpeg

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

post in board.jpg
File Mar 13, 8 02 33 PM.jpeg

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);
}