Pageview Counter

i saw someone with a youtube subscriber counter display and decided i wanted one as well, but i don't have many subs, so instead i will do something i have alot of: pageviews! we are coming up on 50k so its a perfect time to build the counter <3 we passed 50k before i could finish, thanks <3 but also cmon >:c (at the final moment working on the project it was: 51823)

back a page

Log 1: Purcha$e$

suprisingly i dont have any led matrix displays and node mcu v3's on hand (though i do have the ESP XXXX units, i just dont know how to use them), so i had to order parts. in addition to that i've begun 3d printing the case:

the middle piece of the case has detached thrice while printing, which never happens. this has caused (un)believable frustration as this has never happened. theories: (1) the glue sticks are too dry cuz i s*ck at closing them. (2) i thought it was supports but i already ruled that out so it better be #1. i'll work on the electronics and programming in the meantime.

here is the code i wrote in python to grab the view count (with censored info):

import pyautogui, sys, time
"""Analytics Reporting"""

from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials

SCOPES = ['https://www.███████.com/auth/analytics.readonly']
KEY_FILE_LOCATION = '██████████████████.json'
VIEW_ID = '██████████'

def initialize_analyticsreporting():
  """Initializes an Analytics Reporting API V4 service object.

    An authorized Analytics Reporting API V4 service object.
  credentials = ServiceAccountCredentials.from_json_keyfile_name(

  # Build the service object.
  analytics = build('analyticsreporting', 'v4', credentials=credentials)

  return analytics

def get_report(analytics):
  """Queries the Analytics Reporting API V4.

    analytics: An authorized Analytics Reporting API V4 service object.
    The Analytics Reporting API V4 response.
  return analytics.reports().batchGet(
        'reportRequests': [
          'viewId': VIEW_ID,
          'dateRanges': [{'startDate': '5000daysAgo', 'endDate': 'today'}],
          #'metrics': [{'expression': 'ga:sessions'}],
          'metrics': [{'expression': 'ga:pageviews'}],
          'dimensions': [{'name': 'ga:country'}]

def print_response(response):
  """Parses and prints the Analytics Reporting API V4 response.

    response: An Analytics Reporting API V4 response.
  count = 0
  for report in response.get('reports', []):
    columnHeader = report.get('columnHeader', {})
    dimensionHeaders = columnHeader.get('dimensions', [])
    metricHeaders = columnHeader.get('metricHeader', {}).get('metricHeaderEntries', [])

    for row in report.get('data', {}).get('rows', []):
      dimensions = row.get('dimensions', [])
      dateRangeValues = row.get('metrics', [])

      #for header, dimension in zip(dimensionHeaders, dimensions):
        #print(header + ': ', dimension)

      for i, values in enumerate(dateRangeValues):
        #print('Date range:', str(i))
        for metricHeader, value in zip(metricHeaders, values.get('values')):
          #print(metricHeader.get('name') + ':f', value)
          count = count + int(value)
  print(str(count) + " views")

def main():
  analytics = initialize_analyticsreporting()
  response = get_report(analytics)

if __name__ == '__main__':

this code just prints it to the console,, now to get it running on the node mcu v3,,, and over wifi,,,, and displayed on the led matrix,,,,,

Log 2: The Agony

everything is here!, but alas it cannot be that simple. programming the software doesn't seem that hard right? well we'll see... i won't bore you with the details, but i wasted time searching for what i need and setlled on micropython for the nodemcuv3, my naive self pressed forward assuming i could get this setup in an afternoon.

ok so i went on over to the micropython docs and began following the tutorial. sure a bit foreign, but nothing too out of my comfot zone right? (see next)

well that can't be good. (awhile later): well turns out the repl just displays jargin if the baud rate is wrong (and sometimes even when its right), but nothing a few resets couldn't fix. lets just configure webrepl now! (see next)

ah, joyous.

reflashed the board with a more modern .bin file, still nothing. to reddit we go! my question was answered by the lovely u/ImpressiveVersion9, and to my suprise, their solution worked (even tho i couldve sworn i had already tried it). nonetheless if finally connected:

as delightful as this may seem, the next occurence soured the mood quite a bit. my python script can't run on the nodemcu v3. not entirely sure why, but i'll come up with something else. and i did! (kinda). i wrote a flask python server that outputs the view count as html (because i was able to write an html grabber in micropython). i could then host this on some random pc/microcomputer in my room somewhere, but theres one caveat. i cant get the localhost to be available over lan (despite my incessant (and correct) use if my device ip). so that is where i'm currently stuck, ill keep trying other methods till i get this server hosted.

p.s. i also discovered the max7219 library by mcauser which allowed me to program the displays (enough to show 1,2,3,4,5,6,7,8, but not much else yet. wish me luck (bonus: they are extremely bright):

Log 3: Part Four

i think i forgot to mention what i printed for the case: but the files i used are this and this. other than that i did all* the rest of the programming, which was quite a hassle.

basically i had to make a local webserver that runs the python script cuz it didnt run properly on the esp8266, so i did that. then using urequests i was able to fetch the html on the local web server and pull the data i need from it it!

so here's the final version of the file running on the esp8266, with probably some unnecessary bits, since its just an amalgamation of code:

  # on MicroPython for ESP8266 & MAX7219 LED display
  # by Alan Wang, Taiwan
  import network, urequests, utime, ntptime
  from machine import Pin, SPI, Timer, reset
  # the new grab function thingy
  import urequests
  # the time loop
  import time
  # MAX7219 driver:
  # Note: this driver is designed for 4-in-1 MAX7219 modules.
  # VCC: 3.3V or 5V
  # GND: GND
  # DIN: MOSI (D7)
  # CS:  D8
  # CLK: SCK (D5)
  from max7219 import Matrix8x8
  # ----------------------------------------
  # WiFi AP ssid and password
  SSID = "█████ ██████"
  PW   = "██████████"

  # config parameters
  TIMEZONE_HOUR_OFFSET = 8      # timezone hour offset
  MAX7219_NUM          = 4      # Number of MAX7219 LED modules
  MAX7219_BRIGHTNESS   = 11     # MAX7219 brightness (0-15)
  MAX7219_INVERT       = False  # Invert MAX7219 display
  MAX7219_SCROLL_DELAY = 30     # MAX7219 display scrolling speed (ms)
  CLOCK_TIMER_DELAY    = 900000 # clock update delay (ms)
  API_TIMER_DELAY      = 300000 # API query delay (ms)
  DISPLAY_TIMER_DELAY  = (len(fmt) + MAX7219_NUM) * 8 * MAX7219_SCROLL_DELAY + 2000
  print("--- the thingy --")
  data           = dict()
  data_available = False
  timer_api      = Timer(-1)
  timer_clock    = Timer(-1)
  timer_display  = Timer(-1)
  led            = Pin(2, Pin.OUT, value=1)
  # setup MAX7219
  spi = SPI(1, baudrate=10000000, polarity=0, phase=0)
  display = Matrix8x8(spi, Pin(15), 8)
  # connect to WiFi
  print("Connecting to WiFi...")
  wifi = network.WLAN(network.STA_IF)
  wifi.connect(SSID, PW)
  while not wifi.isconnected():
  # decorator for checking WiFi status
  def wifi_check_decorator(func):
      def wrapper(*args, **kwargs):
          if not wifi.isconnected():
              # stop timers
              # reboot
              # run decorated functions
              func(*args, **kwargs)
      return wrapper
  global dsp
  dsp = ""
  starttime = time.time()
  while True:
          # the new request grab
          response = urequests.get("")
          alltext = response.text
          alltext = alltext.replace('█████████', '')
          alltext = alltext.replace('████████████', '')
          alltext = alltext.replace('███████████', '')
          alltext = alltext.replace('█████████', '')
          alltext = alltext.replace(' ███████', '')
          alltext = alltext.replace('████████', '')
          alltext = alltext.replace('████████████', '')
          alltext = alltext.replace('██████████', '')
          bruh = int(alltext)
          if bruh > 99999:
              global dsp
              dsp = '00'+str(bruh)
          elif bruh > 999999:
              global dsp
              dsp = '0'+str(bruh)
              global dsp
              dsp = '000'+str(bruh)
          display.text("        ",0,0,1)

          time.sleep(60.0 - ((time.time() - starttime) % 60.0))
          time.sleep(60.0 - ((time.time() - starttime) % 60.0))

now just solder it up and make sure it works:

positive! i decided to keep it on my windowsil:

and its all done! it updates every minute, and honestly it seems like it wasnt much of a hassle, but thats just the result of my brain removing the trauma of me troubleshooting this thing. definitely recommend this to learn about new things, i learned alot!

back a page

┈ ren ​♡