Community project

Bitcoin Price Ticker

ESP32
Photo of Bitcoin Price Ticker

schematik

Last updated September 26, 2026

This project builds a real-time Bitcoin price ticker using an ESP32 microcontroller, OLED display, and WiFi connectivity. The device fetches the current BTC-USD price from the Coinbase API every five minutes and displays it on a small screen, making it perfect for monitoring Bitcoin's value at a glance from a desk or shelf.

This guide provides everything needed to assemble and program the ticker: a wiring diagram showing how to connect the OLED display and status LED to the ESP32, a complete parts list, the Arduino firmware with WiFi and API integration, and step-by-step assembly instructions. Simply place the board in its case, connect USB power, and the ticker will start fetching and displaying live price data.

Wiring diagram

Assemble it in 2 steps

1. Place the board in its case

Slide the purple ESP32-C3 OLED board upright into the front opening of the matte-black 3D-printed case. Keep the tiny screen facing outward and leave the USB-C socket exposed at the case edge.

  • The board already contains the display, so there are no small screen wires to connect.
  • Do not force the board against the case; pressure on the OLED glass can crack the screen.

2. Connect USB power

Plug a USB-C cable into the board’s USB-C socket, then connect the other end to a normal USB power source or computer. The cable powers the board and is also used when you press Deploy.

  • Use a cable that carries data as well as power so Schematik can deploy the firmware.
  • Keep the metal USB-C plug away from loose metal parts; a short circuit can damage the board.

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <U8g2lib.h>


// Forward declarations
void showPrice(const String &price);
void showUpdateAnimation();
String fetchBtcUsd();
void updateTicker();

constexpr uint8_t OLED_SDA = 5;
constexpr uint8_t OLED_SCL = 6;
constexpr uint8_t STATUS_LED = 8;  // Active-low blue LED.
constexpr unsigned long UPDATE_INTERVAL_MS = 5UL * 60UL * 1000UL;

const char *WIFI_SSID = "my_wifi";
const char *WIFI_PASSWORD = "password1234!";
const char *COINBASE_URL = "https://api.coinbase.com/v2/prices/BTC-USD/spot";

U8G2_SSD1306_72X40_ER_F_HW_I2C display(U8G2_R0, U8X8_PIN_NONE, OLED_SCL, OLED_SDA);
String shownPrice;
unsigned long lastUpdateMs = 0;

void showPrice(const String &price) {
  display.clearBuffer();
  display.setFont(u8g2_font_9x18_tf);

  const char *symbol = "BTCUSD";
  const int symbolX = (display.getDisplayWidth() - display.getStrWidth(symbol)) / 2;
  const int priceX = (display.getDisplayWidth() - display.getStrWidth(price.c_str())) / 2;
  display.drawStr(symbolX, 17, symbol);
  display.drawStr(priceX, 39, price.c_str());
  display.sendBuffer();
  shownPrice = price;
}

void showUpdateAnimation() {
  digitalWrite(STATUS_LED, LOW);  // The blue LED is active-low.
  display.clearBuffer();
  display.sendBuffer();

  display.setFont(u8g2_font_9x18_tf);
  for (uint8_t dots = 1; dots <= 3; ++dots) {
    display.clearBuffer();
    String frame;
    for (uint8_t i = 0; i < dots; ++i) {
      frame += ".";
    }
    const int x = (display.getDisplayWidth() - display.getStrWidth(frame.c_str())) / 2;
    display.drawStr(x, 29, frame.c_str());
    display.sendBuffer();
    delay(180);
  }

  display.clearBuffer();
  display.sendBuffer();
  digitalWrite(STATUS_LED, HIGH);  // Turn the blue LED off before showing the price.
}

String fetchBtcUsd() {
  WiFiClientSecure client;
  client.setInsecure();
  HTTPClient http;

  if (!http.begin(client, COINBASE_URL)) {
    return "";
  }

  const int status = http.GET();
  if (status != HTTP_CODE_OK) {
    http.end();
    return "";
  }

  const String body = http.getString();
  http.end();

  const String key = "\"amount\":\"";
  const int start = body.indexOf(key);
  if (start < 0) {
    return "";
  }
  const int valueStart = start + key.length();
  const int valueEnd = body.indexOf('"', valueStart);
  if (valueEnd < 0) {
    return "";
  }

  String price = body.substring(valueStart, valueEnd);
  const int dot = price.indexOf('.');
  if (dot >= 0) {
    price.remove(dot);
  }
  return "$" + price;
}

void updateTicker() {
  if (WiFi.status() != WL_CONNECTED) {
    WiFi.disconnect();
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    const unsigned long started = millis();
    while (WiFi.status() != WL_CONNECTED && millis() - started < 15000UL) {
      delay(250);
    }
  }

  if (WiFi.status() == WL_CONNECTED) {
    const String price = fetchBtcUsd();
    if (price.length() > 0) {
      showUpdateAnimation();
      showPrice(price);
    }
  }
}

void setup() {
  pinMode(STATUS_LED, OUTPUT);
  digitalWrite(STATUS_LED, HIGH);  // Keep the blue LED off until a price refresh starts.
  Wire.begin(OLED_SDA, OLED_SCL);
  display.begin();
  display.setPowerSave(0);
  showPrice("--");
  WiFi.mode(WIFI_STA);
  updateTicker();
  lastUpdateMs = millis();
}

void loop() {
  if (millis() - lastUpdateMs >= UPDATE_INTERVAL_MS) {
    lastUpdateMs = millis();
    updateTicker();
  }
  delay(50);
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik