Alan Wang
Published © CC BY-NC-SA

Fortune Cookie Teller Hat

A hat with a ESP8266 board and 4 MAX7219 LED matrix modules which can tell you a random fortune like those you find in cookies.

BeginnerShowcase (no instructions)250
Fortune Cookie Teller Hat

Things used in this project

Hardware components

NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
×1
MAX7219 8x8 LED matrix module
×1
Capacitive touch sensor
×1
Rotary potentiometer (generic)
Rotary potentiometer (generic)
×1

Software apps and online services

MicroPython
MicroPython

Story

Read more

Code

Fortune Cookie Teller Hat

MicroPython
# Fortune Cookie Teller Hat on ESP8266 and MAX7219 LED matrix modules
# by Alan Wang

import network, urequests, ntptime, utime, urandom, framebuf, gc
from machine import Pin, SPI, ADC, freq
from micropython import const

# wifi information
ssid = "your_wifi_ssid"
pw = "your_wifi_pw"

# API: http://fortunecookieapi.herokuapp.com/
url = "http://fortunecookieapi.herokuapp.com/v1/fortunes?limit=1&skip="

# pic of a cookie
cookie = bytearray((0x1c, 0x3e, 0x7f, 0x7f, 0x77, 0x77, 0x63, 0x00))
cookie_buf = framebuf.FrameBuffer(cookie, 8, 8, framebuf.MONO_HMSB)

# pic of a fortune teller
fortuneteller = bytearray((0x3c, 0x18, 0x08, 0xfc, 0x7c, 0x6e, 0x0e, 0x1e))
fortuneteller_buf = framebuf.FrameBuffer(fortuneteller, 8, 8, framebuf.MONO_HMSB)

freq(160000000)
gc.enable()

# MAX7219 driver based on https://github.com/mcauser/micropython-max7219
class MAX7219_8X8:
    
    _NOOP = const(0)
    _DIGIT0 = const(1)
    _DECODEMODE = const(9)
    _INTENSITY = const(10)
    _SCANLIMIT = const(11)
    _SHUTDOWN = const(12)
    _DISPLAYTEST = const(15)
    
    def __init__(self, spi, cs, num):
        self.spi = spi
        self.cs = cs
        self.cs.on()
        self.num = num
        self.buffer = bytearray([0] * self.num * 8)
        self.fb = framebuf.FrameBuffer(self.buffer, self.num * 8, 8, framebuf.MONO_HMSB)
        self._initalize()

    def _write(self, command, data):
        self.cs.off()
        for _ in range(self.num):
            self.spi.write(bytes([command, data]))
        self.cs.on()

    def _initalize(self):
        for command, data in (
            (_SHUTDOWN, 0),
            (_DISPLAYTEST, 0),
            (_SCANLIMIT, 7),
            (_DECODEMODE, 0),
            (_SHUTDOWN, 1),
        ):
            self._write(command, data)

    def brightness(self, value):
        self._write(_INTENSITY, value)

    def show(self, rotate=False):
        if rotate:
            self.rotate()
        for i in range(8):
            self.cs.off()
            for m in range(self.num):
                self.spi.write(bytes([_DIGIT0 + i, self.buffer[self.num * i + m]]))
            self.cs.on()
            
    def rotate(self):
        for m in range(self.num):
            matrix = [bytearray([0] * 8) for _ in range(8)]
            for i in range(8):
                binary = "{:08b}".format(self.buffer[self.num * i + m])
                matrix[i] = map(int, binary)
            matrix = list(zip(*reversed(matrix)))
            for i in range(8):
                value = sum(map(lambda el: el[1] * 2 ** el[0], enumerate(matrix[i])))
                self.buffer[self.num * i + m] = value

spi = SPI(1, baudrate=10000000, polarity=0, phase=0)
display = MAX7219_8X8(spi, Pin(15, Pin.OUT), 4)
button = Pin(5, Pin.IN)
adc = ADC(0)

print("Connecting to WiFi...")
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect(ssid, pw)

index = 0
display.brightness(3)
while not wifi.isconnected():
    display.fb.fill(0)
    display.fb.text(".", 8 * index, 0, 1)
    index += 1
    if index >= 4:
        index = 0
    display.show(rotate=True)
del index
print("Connected.")

ntp_get_success = False
while not ntp_get_success:
    try:
        ntptime.settime()
        ntp_get_success = True
    except:
        utime.sleep(1)

urandom.seed(utime.time())
print("Random seed set based on NTP time:", utime.time())
utime.sleep(1)

display.fb.fill(1)
for i in range(15):
    display.brightness(i)
    display.show()
    utime.sleep_ms(50)
for i in reversed(range(15)):
    display.brightness(i)
    display.show()
    utime.sleep_ms(75)

display.fb.fill(0)
display.show()

utime.sleep(0.5)

while True:
    
    scroll_str = "FORTUNE COOKIE TELLER"
    p = display.num * 8
            
    while p >= len(scroll_str) * -8:
        
        if button.value() == 1:
            display.fb.fill(0)
            display.show()
            while button.value() == 1:
                pass
            break
        
        display.fb.fill(0)
        display.brightness(int(adc.read() * 15 / 1023))
        display.fb.text(scroll_str, p, 0, 1)
        display.show(rotate=True)
        p -= 1
        
    else:
        continue

    print("Open a new fortune cookie:")
    for i in range(8):
        display.fb.fill(0)
        display.brightness(int(adc.read() * 15 / 1023))
        display.fb.blit(fortuneteller_buf, i, 0)
        display.fb.blit(cookie_buf, display.num * 8 - 8 - i, 0)
        display.show(rotate=True)
        utime.sleep_ms(100)

    response = urequests.get(url=(url + str(urandom.getrandbits(9))))
    
    if response.status_code == 200:
        
        parsed = response.json()
        scroll_str = parsed[0]["message"]
        scroll_str = scroll_str.upper()
        utime.sleep(0.5)
            
        for i in reversed(range(4)):
            display.fb.fill(0)
            display.brightness(int(adc.read() * 15 / 1023))
            display.fb.fill_rect(i, i, display.num * 8 - i * 2, 8 - i * 2, 1)
            display.show(rotate=True)
            utime.sleep_ms(50)
        
        print("Your fortune:")
        print(scroll_str)
        
        p = display.num * 8
        while p >= len(scroll_str) * -8:
        
            display.fb.fill(1)
            display.brightness(int(adc.read() * 15 / 1023))
            display.fb.text(scroll_str, p, 0, 0)
            display.show(rotate=True)
            p -= 2
            
        scroll_str = ""
        for _ in range(6):
            num = -1
            while num < 0 or num > 59:
                num = urandom.getrandbits(6)
            scroll_str += (str(num) + " ")
        
        print("Your lottery numbers:")
        print(scroll_str)
        
        p = display.num * 8
        while p >= len(scroll_str) * -8:
        
            display.fb.fill(1)
            display.brightness(int(adc.read() * 15 / 1023))
            display.fb.text(scroll_str, p, 0, 0)
            display.show(rotate=True)
            p -= 1
            
        for i in range(4):
            display.fb.fill(0)
            display.brightness(int(adc.read() * 15 / 1023))
            display.fb.fill_rect(i, i, display.num * 8 - i * 2, 8 - i * 2, 1)
            display.show(rotate=True)
            utime.sleep_ms(100)
        
        display.fb.fill(0)
        display.show()
        utime.sleep(2)

Credits

Alan Wang

Alan Wang

24 projects • 37 followers
Former novel translator, amateur film photographer. Sometime Maker.

Comments