picture

I’ve wanted an Internet connected read-out for some time now, inspired by the awesome shadow box IoT projects Becky Stern has been doing (weather, YouTube subscribers). I’m certainly not to the same level of packaging as her yet, but I’ve got a functional display working with a Hazzah and an eBay seven segment display module.

Video:

Bill of Materials:

I soldered the Hazzah and display onto a piece of perf board I had laying around, which didn’t work so well since the display was longer than the grid of the perf board.

picture

picture

I programmed the ESP8266 through the Arduino environment, which means you need to install the Arduino IDE, install the ESP8266 board module following their instructions, and install the YouTube API library through the library manager.

You’ll also need to generate a Google API key for yourself, which you can do following the instructions on the YouTube API library’s README.

The CHANNEL_ID macro is the random characters at the end of the URL for any channel of interest. For example, for my channel you’d want the part in bold:

https://www.youtube.com/channel/UCKzaXL9_V1qxGj5lzIyLBpw
The hardware is relatively simple: solder the provided headers onto the Hazzah, and connect the five needed lines from the Hazzah to the MAX7219 display:
  • Hazzah - MAX7219
  • V+ - Vcc
  • GND - GND
  • Pin 12 - CS
  • Pin 13 - DIN
  • Pin 14 - CLK
If you wire up the chip select, clock, and data lines differently, you should only need to change the macros at the top of the source code to correctly reflect your setup.
// YouTube Channel View Counter
// Kenneth Finnegan, 2017
//
// Given a WiFi SSID + password, and YouTube API key + channel ID, displays the
// current total views count on an attached MAX7219 eight digit display

#include <YoutubeApi.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

//------- Replace the following! ------
char ssid[] = "WIFISSIF";       // your network SSID (name)
char password[] = "WIFIPASSWORD";  // your network key
#define API_KEY "INSERTYOURAPPSAPIKEYHERE"  // your google apps API Token
#define CHANNEL_ID "INSERTCHANNELIDHERE" // makes up the url of channel

// This depends on which pins you've wired the MAX7219 driver to.
#define DISP_CS 12
#define DISP_CLK  14
#define DISP_DATA 13


WiFiClientSecure client;
YoutubeApi api(API_KEY, client);

// The total channel view count is updated very rarely (daily?) so we don't need to check regularly
unsigned long api_mtbs = 5 * 60 * 1000; // Desired update interval in milliseconds
unsigned long api_lasttime = 0-api_mtbs;   //last time api request has been done
  // Note that we're pre-loading the "last update" variable to an interval before zero so the
  // display updates immediately on boot

////////////////////////////
// DISPLAY DRIVER SECTION //
////////////////////////////

// Soft implementation of SPI because I don't even know if the ESP-12 has SPI hardware available
// [TODO: insert rant about how much Arduino platform documentation sucks these days]
// This only sends eight bits, which is half a MAX7219 command
void disp_send(uint8_t data) {
  int i;
  for (i=0; i<8; i++) {
    // Send the most significant bit first
    digitalWrite(DISP_DATA, !!(data & (1<<(7-i))));
    delay(1); // These delays might not be needed, but I like the visual effect of the slow update
    // Then toggle the clock line
    digitalWrite(DISP_CLK, HIGH);
    delay(1);
    digitalWrite(DISP_CLK, LOW);
    delay(1);
  }
}

// Sends two bytes, the register select and the payload for that data
// See the datasheet for the full memory map:
// https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf
void disp_cmd(uint8_t reg, uint8_t data) {
  digitalWrite(DISP_CS, LOW);
  disp_send(reg);
  disp_send(data);
  digitalWrite(DISP_CS, HIGH);
}

// Given an integer, write it to the display
void disp_number(int data) {
  int i;
  for (i=1; i<=8; i++) {
    int output = data % 10;
    // Turn on the decimal place at 4 and 7 to make it easier to read
    if (i == 4 || i == 7) output |= 0x80;
    // Blank leading zeros if we've run out of data and we're not the first digit
    if (data == 0 && i != 1) output = 0x0F;
    disp_cmd(i,output);
    data = data / 10;
  }
}

// When we aren't working, display "00000000" then "--HELP--"
void disp_error() {
  int i;
  for (i=8; i>0; i--) {
    disp_cmd(i,0x00);
  }
    
  disp_cmd(0x08,0x0A); // -
  disp_cmd(0x07,0x0A); // -
  disp_cmd(0x06,0x0C); // H
  disp_cmd(0x05,0x0B); // E
  disp_cmd(0x04,0x0D); // L
  disp_cmd(0x03,0x0E); // P
  disp_cmd(0x02,0x0A); // -
  disp_cmd(0x01,0x0A); // -
}


////////////////////////////////
// MAIN SETUP AND MASTER LOOP //
////////////////////////////////

void setup() {

  Serial.begin(115200);

  // Set up the MAX7219 display and load the command registers
  pinMode(DISP_CS, OUTPUT);
  pinMode(DISP_CLK, OUTPUT);
  pinMode(DISP_DATA, OUTPUT);
  disp_cmd(0x0C,0x01); // Leave shutdown
  delay(30);
  disp_cmd(0x0F,0x00); // Leave display test
  delay(30);
  disp_cmd(0x09,0xFF); // Use B code on all eight digits
  delay(30);
  disp_cmd(0x0A,0x02); // Set software intensity 0-15
  delay(30);
  disp_cmd(0x0B,0x07); // Scan all eight digits (blank individual digits with 0xF)
  delay(30);
  disp_error();

  // Set WiFi to station mode and disconnect from an AP if it was Previously
  // connected
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);

  // Attempt to connect to Wifi network:
  Serial.print("Connecting Wifi: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  IPAddress ip = WiFi.localIP();
  Serial.println(ip);


  
}

void loop() {

  if (millis() - api_lasttime > api_mtbs)  {
    Serial.println("Trying to query API");
    if(api.getChannelStatistics(CHANNEL_ID)) {
      Serial.println("---------Stats---------");
      Serial.print("Subscriber Count: ");
      Serial.println(api.channelStats.subscriberCount);
      Serial.print("View Count: ");
      Serial.println(api.channelStats.viewCount);
      Serial.print("Comment Count: ");
      Serial.println(api.channelStats.commentCount);
      Serial.print("Video Count: ");
      Serial.println(api.channelStats.videoCount);
      Serial.println("------------------------");

      disp_number(api.channelStats.viewCount);

      api_lasttime = millis();
    } else {
      // Something went wrong, so try again in 30s
      Serial.println("YOUTUBE API ERROR!");
      disp_error();
      api_lasttime += 30 * 1000;
    }
  }
}

Of course, the possibilities here for packaging this display better are endless, as is using either the ESP8266 and/or these $2 displays to display any other information than YouTube channel view counts. The fact that these displays are chainable even makes it possible to make large multi-line displays with them.