Author

Topic: LED Bitcoin Dashboard [weekend project] (Read 358 times)

hero member
Activity: 882
Merit: 5834
not your keys, not your coins!
April 28, 2023, 07:18:57 AM
#10
I'm a big fan of these panels myself, we use them a lot.
What do you use them for? In case you have any additions or improvements to my code, feel free to post your changes here!
hero member
Activity: 882
Merit: 5834
not your keys, not your coins!
April 09, 2022, 08:20:47 PM
#9
Oh ok. I never dig into ESP32 too much.
Anyways, about STM32 is an whole new world. Arduino and such platforms are all based in 8bit chips.
Actually, the ESP32 is a 32-bit dual-core MCU. It's still easy to get it working especially if you get it on a 'dev board' such as a D1 mini, because then it has USB built-in with USB-UART converter chip on the PCB, as you can see in the images.

Ok, hope you keep improving on your design and features of your project. I'll keep taking a look to this thread!
Thanks for sharing!
Sure, glad you guys like it Smiley

I've considered doing this myself before, but just didn't get around to it. Considering that ready made products are very expensive, a few hundred quid. I've always wanted to put something like this together, I don't need it to be fancy looking, just needed something to satisfy the nerd in me.
Can't agree more! Wink

How easy would this be to scale? For example, making it bigger? I know we're talking about elevated costs then, but if buying these panels in bulk, it could be a little fun project for a wall Cheesy.
In theory, it should be relatively easy. The README implies that for vertical scaling it might involve a bit of custom calculations, but it should just be a matter of a division and a modulus. Like, if the library really doesn't support vertical scaling, you just set it up as a long horizontal 'chain' and calculate coordinates of the real thing in your own code.

Also, would be pretty sweet to be able to turn it off at a set time, I'm also thinking that connecting it to one of those remote controlled plugs could satisfy the nerd in me even more.
You could easily code that in, add a little HTTP listener to turn the thing on or off via API calls or just put it on a ready-made 'smart plug' or IR controlled plug (for better privacy and vendor- / app-independence).

I think I'll order 3 more to get a 2x2 display; should be a nice size and would allow me to play with the horizontal and vertical scaling.
Unfortunately, they jumped in price from ~20€ to ~30€ from what I can tell.
staff
Activity: 3304
Merit: 4115
April 08, 2022, 05:42:49 AM
#8
I've considered doing this myself before, but just didn't get around to it. Considering that ready made products are very expensive, a few hundred quid. I've always wanted to put something like this together, I don't need it to be fancy looking, just needed something to satisfy the nerd in me.

How easy would this be to scale? For example, making it bigger? I know we're talking about elevated costs then, but if buying these panels in bulk, it could be a little fun project for a wall Cheesy.

Also, would be pretty sweet to be able to turn it off at a set time, I'm also thinking that connecting it to one of those remote controlled plugs could satisfy the nerd in me even more.
hero member
Activity: 1274
Merit: 681
I rather die on my feet than to live on my knees
April 08, 2022, 03:23:16 AM
#7
Nice work. I like these types of projects. What is exactly the chip that is processing the code? I mean, the ESP is only for internet connection, right (I have my own but never used it, :p) ? So, what is the chip you're using for the process of communicating with the LED matrix?
The ESP32 is a very capable little chip; it handles WiFi and all the logic for controlling the LEDs. No other chips involved. The setup looks exactly like in the picture above with the shield stuck directly to the back of the matrix and the ESP32 on top of it.

Regarding its capabilities: Someone emulated an IBM PC with FreeDOS on it, running on one of these... Wink.
Here's a video where you see the hardware as well.

I am actually trying a small project myself, but I lose the drive frequently and I end up giving up now to come back to it some time later. I'm using an stm32L412 chip to control a fan that is supposed to cool my NVM2 drive of my LN node, using a thermistor and a couple other additional components, however, this stm32 is proving to be a little bit complex to learn and get along with it!
Well, if you're within the Arduino 'ecosystem', while not being best for performance and all that, it's pretty easy to quickly whip up something that works, especially due to all the libraries that are around. This really didn't take me more than a few hours. In fact, I rushed it to have it up for the 19M BTC milestone and only had a few hours left when I got that idea. Later I put in a little more time to clean up the code and make it more readable for this post.

Oh ok. I never dig into ESP32 too much.
Anyways, about STM32 is an whole new world. Arduino and such platforms are all based in 8bit chips. STM32 it's, well, 32bit. So, there's a lot more to setup before you can put it to work. Schematics and block diagrams are an whole new level. At least for me. I did my graduation project with a bare AtMega328 and some additional electronics and yes, that one was kinda easy to grasp! Well, at least if you want to use non-proprietary libraries. HAL libraries for STM32 are a bit confusing and I gave up on them and started using libopencm3.

Ok, hope you keep improving on your design and features of your project. I'll keep taking a look to this thread!
Thanks for sharing!
hero member
Activity: 882
Merit: 5834
not your keys, not your coins!
April 07, 2022, 12:07:57 PM
#6
This is looking very cool and you finally managed to take a good picture of that LED dashboard  Cheesy
Are you keeping this dashboard running all the time at your home, or you turn it up only when you are home (imagine having one big billboard like this hanged outside your home  Cheesy).
I am sure it spends very little electricity power, but did you ever tested it with for exact power consumption?
It's turned on all the time; well I haven't measured power consumption, but quick napkin math: power supply is 50W and powers the whole panel easily. I use maybe 10% of the LEDs (count them if you wish Wink) so that could be 5W max. Do keep in mind that the PSU doesn't struggle to power the whole panel on full brightness, so that's probably more like 30W, which would at 10% utilization be ~3W for the Bitcoin dashboard.

Putting a big one outside would be fun; but I wouldn't see it much and it's obviously bad OpSec. Wink

By the way, I was thinking about incorporating a mini-webserver into the code with a simple REST API to turn it on or off. That could then be toggled by a cronjob on any server / computer in your network that is always running (like a Bitcoin node). But I just didn't find it necessary.
legendary
Activity: 2212
Merit: 7064
April 07, 2022, 11:20:32 AM
#5
This is looking very cool and you finally managed to take a good picture of that LED dashboard  Cheesy
Are you keeping this dashboard running all the time at your home, or you turn it up only when you are home (imagine having one big billboard like this hanged outside your home  Cheesy).
I am sure it spends very little electricity power, but did you ever tested it with for exact power consumption?
hero member
Activity: 882
Merit: 5834
not your keys, not your coins!
April 06, 2022, 11:21:30 AM
#4
Nice work. I like these types of projects. What is exactly the chip that is processing the code? I mean, the ESP is only for internet connection, right (I have my own but never used it, :p) ? So, what is the chip you're using for the process of communicating with the LED matrix?
The ESP32 is a very capable little chip; it handles WiFi and all the logic for controlling the LEDs. No other chips involved. The setup looks exactly like in the picture above with the shield stuck directly to the back of the matrix and the ESP32 on top of it.

Regarding its capabilities: Someone emulated an IBM PC with FreeDOS on it, running on one of these... Wink.
Here's a video where you see the hardware as well.

I am actually trying a small project myself, but I lose the drive frequently and I end up giving up now to come back to it some time later. I'm using an stm32L412 chip to control a fan that is supposed to cool my NVM2 drive of my LN node, using a thermistor and a couple other additional components, however, this stm32 is proving to be a little bit complex to learn and get along with it!
Well, if you're within the Arduino 'ecosystem', while not being best for performance and all that, it's pretty easy to quickly whip up something that works, especially due to all the libraries that are around. This really didn't take me more than a few hours. In fact, I rushed it to have it up for the 19M BTC milestone and only had a few hours left when I got that idea. Later I put in a little more time to clean up the code and make it more readable for this post.
hero member
Activity: 1274
Merit: 681
I rather die on my feet than to live on my knees
April 06, 2022, 09:04:04 AM
#3
Hi...

Nice work. I like these types of projects. What is exactly the chip that is processing the code? I mean, the ESP is only for internet connection, right (I have my own but never used it, :p) ? So, what is the chip you're using for the process of communicating with the LED matrix?

I am actually trying a small project myself, but I lose the drive frequently and I end up giving up now to come back to it some time later. I'm using an stm32L412 chip to control a fan that is supposed to cool my NVM2 drive of my LN node, using a thermistor and a couple other additional components, however, this stm32 is proving to be a little bit complex to learn and get along with it!
hero member
Activity: 882
Merit: 5834
not your keys, not your coins!
April 06, 2022, 07:57:49 AM
#2
Microcontroller and matrix shield
I got one of these shields before the shop recently closed, paired with a D1 Mini ESP32, since it's much better than an ESP8266 in a few ways, like multithreading.
https://www.tindie.com/products/brianlough/esp32-matrix-shield-mini-32/
Similar products and microcontrollers should work, though they will require small changes to the code.

Can you explain what exactly is matrix shield? Is it used to connect ESP32 with LED Matrix?
Yes, it's just an easy way to connect ESP32 microcontroller board, LED matrix and power supply all together. You could get away with jumper wires and some hot glue, though.

You can see the wiring for different matrices and microcontrollers here: https://github.com/2dom/PxMatrix#set-up-and-cabling

This is kind of what it looks like without the shield:


And with breakout shield:


These are both smaller, 32x64 matrices. It makes the MCU look pretty huge. Cheesy
hero member
Activity: 882
Merit: 5834
not your keys, not your coins!
April 05, 2022, 08:45:08 AM
#1
LED Bitcoin Dashboard
This is a topic dedicated to my latest little Bitcoin project!



This is a 64x64 (P3) RGB LED matrix panel that can be bought for roughly $20 to $30. I'm sure they are sold for much less when used in their intended application and bought in bulk, to form the large billboards that we know and love. They indeed often consist of tons of these panels chained together.




This project utilizes a microcontroller and libraries that do support chaining, though I've yet to try it. This would allow to cram more data onto this 'dashboard'.

Parts

LED Matrix
Of course, the main component is the LED matrix. They can be acquired for roughly $20-30 or equivalent in other currencies.
I would recommend a 64x64; the pitch (P) shouldn't really matter, but I got a tighter P3 pitch so it looks sharper instead of having huge gaps between individual LEDs. That would give you a larger end result, though if that's what you're into.


Microcontroller and matrix shield
I got one of these shields before the shop recently closed, paired with a D1 Mini ESP32, since it's much better than an ESP8266 in a few ways, like multithreading.
https://www.tindie.com/products/brianlough/esp32-matrix-shield-mini-32/
Similar products and microcontrollers should work, though they will require small changes to the code.

Power Supply
The whole thing is powered by a 5V barrel jack power supply. The amperage / wattage is kind of up to you; especially in such a text-based application, it doesn't have to be very high since not a lot of LEDs are on at the same time. Mine with 10A is on the higher side (one of the highest ratings I could find at 5V that are still in a power-brick package and not 'LED power supply style' - nowadays known for their usage in 3D printers). It can illuminate the whole panel, so you'll be good with half of that for this project.
The matrix shield powers the matrix as well as the microcontroller.

Information
At the moment, it shows:
  • Block height
  • Bitcoin mined
  • Fastest fee rate
  • USD per BTC
  • sat per USD
  • Time based on time zone

As we spoke about this project in another thread, I got a few suggestions on how to improve it, and this is my current idea for adding more data, especially about Lightning, without adding more displays.

I'm glad about any feedback on this as I believe cycling through screens will make it annoying, and I don't think all that data is needed very often. I do check it a few times a day as it is now and it's already pretty handy honestly.

I believe I'll split it up into multiple screens then:
Blockchain Data
  • Block height
  • Bitcoin mined
  • Blockchain size

Mining
  • Blocks until next diff
  • Blocks until next halving
  • Hashrate

Mempool
  • Size
  • Fee fastest
  • Fee medium
  • Fee slow

Price
  • USD per coin
  • [insert other currency] per coin
  • sat per USD
  • sat per [insert other currency]

Lightning
  • Capacity
  • Number of channels
  • ...

It would become tricky to set a useful speed though; if it's too fast, you can't read it in time and it is distracting. If it's too slow, you can miss milestones like the 19M BTC (it was fun to watch it live!)..

Code
Of course, I'm sharing my code here. It's nothing special, Arduino-IDE based, just using a few libraries and calling some APIs. What's a little bit nifty though is the usage of the ESP32's secondary core to poll the APIs without interfering with the clock loop, so that the clock is updated independently from the internet speed.
I'm also not too fond of having to use 'Arduino Strings' here instead of std::string, but the latter don't seem to work on Arduino, so it is what it is.

If you use the same hardware, you'll only really have to change
#define MYTIMEZONE "..." (to your time zone)
char ssid[] = "..."; (to your WiFi SSID)
char password[] = "..."; (to your WiFi password)

Other microcontrollers might just need a change of #define P_OE 2 and possibly change of the multithreading / background thread part.
If your matrix is different (less LEDs), you'll want to set its size in PxMATRIX display(64, 64, ...); correctly and of course need to remove a few lines of text to display on it.

Code:
#include
#include
#include

// https://github.com/2dom/PxMatrix
#include

// Adafruit GFX library is a dependancy for the PxMatrix Library
// Can be installed from the library manager
// https://github.com/adafruit/Adafruit-GFX-Library
// Library used for getting the time and adjusting for DST
// Search for "ezTime" in the Arduino Library manager
// https://github.com/ropg/ezTime
#include

// ---- Stuff to configure ----
// Initialize Wifi connection to the router
char ssid[] = "...";     // your network SSID (name)
char password[] = "..."; // your network key

// Set a timezone using the following list
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
#define MYTIMEZONE "..."
// -----------------------------

// ----- Wiring -------
#define P_LAT 22
#define P_A 19
#define P_B 23
#define P_C 18
#define P_D 5
#define P_E 15
#define P_OE 2 // Generic ESP32
// ---------------------

struct NetworkData {
  String height, btccount, price, sats, fees;
  bool refresh;
} networkdata;

portMUX_TYPE displayMux = portMUX_INITIALIZER_UNLOCKED;
hw_timer_t* displayTimer = NULL;
TaskHandle_t dataRefreshTask;

PxMATRIX display(64, 64, P_LAT, P_OE, P_A, P_B, P_C, P_D, P_E);
Timezone myTZ;

// Set up the LED Bitcoin Dashboard
void setup() {
  Serial.begin(115200);

  // Attempt to connect to Wifi network:
  Serial.print("Connecting Wifi: ");
  Serial.println(ssid);

  // Set WiFi to station mode and disconnect from an AP if it was Previously
  // connected
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // Do not set up display before WiFi connection as it will crash!

  // Intialise display
  display.begin(32);
  display.setRotate(true);
  display.clearDisplay();
  display.setTextColor(display.color565(0, 255, 255));
  display.setCursor(10,10);
  startDisplayTimer(true);
  yield();
  display.clearDisplay();

  display.print("BITCOIN");
  display.showBuffer();

  setDebug(INFO);
  waitForSync();

  Serial.println();
  Serial.println("UTC: " + UTC.dateTime());

  myTZ.setLocation(F(MYTIMEZONE));
  Serial.print(F("Time in your set timezone: "));
  Serial.println(myTZ.dateTime());
  display.clearDisplay();

  startBackgroundTask();
}

// Update the screen once per second
unsigned long timeDue = 0;
void loop() {
  unsigned long now = millis();
  if (now > timeDue) {
    updateDisplay();
    timeDue = now + 1000;
  }
}

// -- Display refresh is necessary --
uint8_t displayDrawTime=6;
void IRAM_ATTR displayUpdater(){
  // Increment the counter and set the time of ISR
  portENTER_CRITICAL_ISR(&displayMux);
  display.display(displayDrawTime);
  portEXIT_CRITICAL_ISR(&displayMux);
}

void startDisplayTimer(bool is_enable) {
  if (is_enable) {
    displayTimer = timerBegin(0, 80, true);
    timerAttachInterrupt(displayTimer, &displayUpdater, true);
    timerAlarmWrite(displayTimer, 2000, true);
    timerAlarmEnable(displayTimer);
  }
  else {
    timerDetachInterrupt(displayTimer);
    timerAlarmDisable(displayTimer);
  }
}

// -- Data is refreshed on parallel core 0
void startBackgroundTask() {
  xTaskCreatePinnedToCore(refreshData, "refreshData", 10000, NULL, 0, &dataRefreshTask, 0);
}

void refreshData(void* parameter) {
  unsigned long timeDue = 0;
  while(true) {
    unsigned long now = millis();
    if (now > timeDue) {
      networkdata.price = getPrice();
      networkdata.height = getBlockHeight();
      networkdata.btccount = getBTCCount(networkdata.height);
      networkdata.fees = getFees();
      networkdata.sats = getSatsPerUSD(networkdata.price);
      networkdata.refresh = true;
      timeDue = now + 30000; // update every 30s
    }
    delay(2); // give CPU time for other tasks
  }
}

// -- Begin Fetch Data --
String getBTCCount(String blockHeight) {
  unsigned int height = blockHeight.toInt();
  unsigned int btcCount = 210000*50 + 210000*25 + 210000*12.5 + (height-630000)*6.25;
  String btcCountStr = String(btcCount);
  return btcCountStr.substring(0,2) + "," + btcCountStr.substring(2,5) + "," + btcCountStr.substring(5,8);
}

String getPrice() {
  String price = "";
  HTTPClient http;
  http.useHTTP10(true);
  http.begin("https://api.hitbtc.com/api/2/public/ticker/btcusd");
  if (http.GET()) {
    DynamicJsonDocument doc(2048);
    deserializeJson(doc, http.getStream());
    price = doc["last"].as();
  }
  http.end();
  return price;
}

String getBlockHeight() {
  HTTPClient http;
  http.begin("https://mempool.space/api/blocks/tip/height");
  if (http.GET()) {
    String payload = http.getString();
    http.end();
    return payload;
  }
  else {
    return "";
  }
}

String getFees() {
  String fees = " ";
  HTTPClient http;
  http.begin("https://mempool.space/api/v1/fees/recommended");
  if (http.GET()) {
    DynamicJsonDocument doc(2048);
    deserializeJson(doc, http.getStream());
    fees += doc["fastestFee"].as();
  }
  return fees.substring(fees.length()-2);
}

String getSatsPerUSD(String price) {
  unsigned int _price = price.toInt();
  unsigned int sats = 100000000/_price;
  return String(sats);
}
// -- End Fetch Data --

// -- Updating display contents --
void writeToScreen(uint8_t x, uint8_t y, String text) {
  display.setCursor(x,y);
  display.print(text);
}

String prevTimeString;
void updateDisplay() {
  Serial.println("Update display");
  String timeString = "";
  // g = 12-hour format of hour, no leading zeros
  // h = 12-hour format of hour with leading zeros
  // i = minutes with leading zeros
  timeString = myTZ.dateTime("h:i");
  //If the length is only 4, pad it with space at the beginning
  if (timeString.length() == 4) { timeString = " " + timeString; }

  if (networkdata.refresh == true) {
    for (int i=0; i<64; i++) for (int j=0; j<64-8; j++) display.drawPixel(i, j, 0);
    writeToScreen(4, 0*8+2, networkdata.height+" blk");
    writeToScreen(3, 1*8+2, networkdata.btccount);
    writeToScreen(15, 2*8+2, networkdata.fees+"sat/vB");
 
    writeToScreen(9, 4*8+1, networkdata.price+"$");
    writeToScreen(9, 5*8+1, networkdata.sats+"sat/$");
    networkdata.refresh = false;
  }

  if (timeString != prevTimeString) {
    prevTimeString = timeString;
    for (int i=0; i<64; i++) for (int j=63; j>63-8; j--) display.drawPixel(i, j, 0);
    writeToScreen(33, 7*8+1, timeString);
  }
}
Jump to: