Global parts shortage, amirite?! Say you reeeeeally need to get your hands on a Raspberry Pi single board computer. They're in short supply, but they are still in stock at irregular intervals. We at Adafruit have been releasing them in small batches as we get them in. They usually sell out in a few minutes. So, this project is designed to help you know the moment they are available. This is the RPi Stock Alert Alarm!

This project uses a Feather ESP32-S2 TFT (hey, it would be pretty mean if we made this project using a Raspi!) to check the rpilocator.com Twitter feed, searching for new Tweets with the keyword "Adafruit". When it finds one, that means there is most likely a fresh batch for sale. This info is displayed on the TFT (including which model of RPi it is), and even better, a bright red spinning light and siren goes off to get your attention!

Parts

Video of warning light glowing swirling LEDs.
THE REACTOR'S MELTING DOWN! GRAB THE FUEL CORE, JENNA, AND EVACUATE THE COMMAND CENTER! With this Rotating LED Warning Light with Adjustable Volume Buzzer Alarm,...
$12.50
In Stock
Adafruit ESP32-S2 TFT Feather powered on by a USB- C power source displaying the product tittle in a red, yellow, green, white and blue.
We've got a new machine here at Adafruit, it can uncover your deepest desires. Don't believe me? I'll turn it on right now to prove it to you! What, you want unlimited...
Out of Stock
Prototyping feather wing PCB with loose headers
A Feather board without ambition is a Feather board without FeatherWings!This is the FeatherWing Proto - a prototyping add-on for all Feather boards. Using our...
$4.95
In Stock
3 pack TIP120 Power Darlington Transistors
Transistors are powerful little electronic switches, and when our little NPN transistors aren't power enough for your project, we have been known to use these beefy TIP120...
$2.50
In Stock
Angled shot of 25 Through-Hole Resistors - 2.2K ohm 5% 1/4W.
ΩMG! You're not going to be able to resist these handy resistor packs! Well, axially, they do all of the resisting for you!This is a 25 Pack of 2.2K...
$0.75
In Stock
Five pack of 20-pin 0.1 Female Header - Yellow plastic
Female header is like the duct tape of electronics. It's great for connecting things together, soldering to perf-boards, sockets for wires or break-away header, etc. We go through...
$2.50
In Stock
1 x USB A to USB C Cable
3 feet / 1 meter
1 x Terminal Block
2.54mm/0.1" Pitch - 2-pin

CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.

CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

Click the link above to download the latest CircuitPython UF2 file.

Save it wherever is convenient for you.

Plug your board into your computer, using a known-good data-sync cable, directly, or via an adapter if needed.

Double-click the reset button (highlighted in red above), and you will see the RGB status LED(s) turn green (highlighted in green above). If you see red, try another port, or if you're using an adapter or hub, try without the hub, or different adapter or hub.

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

A lot of people end up using charge-only USB cables and it is very frustrating! Make sure you have a USB cable you know is good for data sync.

You will see a new disk drive appear called FTHRS2BOOT.

 

Drag the adafruit_circuitpython_etc.uf2 file to FTHRS2BOOT.

The BOOT drive will disappear and a new disk drive called CIRCUITPY will appear.

That's it!

Text Editor

Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.

Alternatively, you can use any text editor that saves simple text files.

Download the Project Bundle

Once you've finished setting up your Feather TFT ESP32-S2 Express with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.

# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import time
import ssl
import wifi
import terminalio
import socketpool
import displayio
import board
from adafruit_display_text import bitmap_label,  wrap_text_to_lines
import adafruit_requests
from digitalio import DigitalInOut, Direction, Pull
from adafruit_debouncer import Debouncer

alarm_out = DigitalInOut(board.A1)
alarm_out.direction = Direction.OUTPUT
alarm_out.value = False

button_in = DigitalInOut(board.BUTTON)  # on-board Boot button on Feather ESP32-S2 TFT
button_in.pull = Pull.UP
button = Debouncer(button_in)


# Get wifi details and more from a secrets.py file
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

print("Adafruit Raspberry Pi In Stock Tweet Listener")

#  import your bearer token
bear = secrets['bearer_token']

#  query URL for tweets. looking for hashtag partyparrot sent to a specific username
#  disabling line-too-long because queries for tweet_query & TIME_URL cannot have line breaks
#  pylint: disable=line-too-long
tweet_query = 'https://api.twitter.com/2/tweets/search/recent?query=In Stock at Adafruit from:rpilocator&tweet.fields=created_at'

headers = {'Authorization': 'Bearer ' + bear}

print("Connecting to %s"%secrets["ssid"])
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to %s!"%secrets["ssid"])
print("My IP address is", wifi.radio.ipv4_address)

pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())

#  gets and formats time from adafruit.io
aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]
location = secrets.get("timezone", None)
TIME_URL = "https://io.adafruit.com/api/v2/%s/integrations/time/strftime?x-aio-key=%s" % (aio_username, aio_key)
TIME_URL += "&fmt=%25Y-%25m-%25dT%25H%3A%25M%3A%25S.%25L%25j%25u%25z%25Z"

display = board.DISPLAY

group = displayio.Group()
font = terminalio.FONT
text = ''
text_area = bitmap_label.Label(font, text=text, scale=2, color=0xFFFFFF)
text_area.x = 0
text_area.y = 15
clock = ''
clock_area = bitmap_label.Label(font, text=clock, color=0xFFFFFF)
clock_area.x = 125
clock_area.y = 128
group.append(text_area)
group.append(clock_area)
display.show(group)

last_value = 0 #  checks last tweet's ID
check = 0 #  time.monotonic() holder

#  fetching current time
print("Fetching text from", TIME_URL)
current_time = requests.get(TIME_URL)
print("-" * 40)
print(current_time.text)
print("-" * 40)

alarm_out.value = False  # this starts the alarm so you can test turning it off w the boot button


while True:
    button.update()
    if button.fell:
        print("Alarm off")
        alarm_out.value = False

    #  every 30 seconds...
    if (check + 30) < time.monotonic():
        #  updates current time
        current_time = requests.get(TIME_URL)
        print(current_time.text)
        #  get tweets from rpilocator containing in stock at adafruit
        the_tweet = requests.request("GET", url=tweet_query, headers=headers)
        #  gets data portion of json
        data = the_tweet.json()['data']
        #  tweet ID number
        value = data[0]['id']
        #  tweet text
        stock_check = data[0]['text']
        #  timestamp
        timestamp = data[0]['created_at']
        #  reset time count
        check = time.monotonic()
        #  compare last tweet ID and current tweet ID
        if last_value != value:
            #  compares date of tweet timestamp with current date
            if timestamp.startswith(current_time.text[0 : 10]):
                print("match")
                #  grabs the hour of the tweet timestamp
                tweet_hour = int(timestamp[11:13])
                print(tweet_hour)
                #  grabs the current hour
                current_hour = int(current_time.text[11:13])
                print(current_hour)
                #  if it's been less than an hour since the tweet...
                if abs(current_hour - tweet_hour) < 1:
                    print("in the last hour")
                    #  displays tweet text and time on screen
                    text_area.text = "\n".join(wrap_text_to_lines(stock_check, 21))
                    print(stock_check)
                    clock_area.text = timestamp
                    print("Raspberry Pi in stock at Adafruit!")
                    alarm_out.value = True

                else:
                    #  if it's not new, then the wait continues
                    no_tweet_text = ("No stock in last hour :( Last stock: %s" % (timestamp))
                    text_area.text = "\n".join(wrap_text_to_lines(no_tweet_text, 21))
                    print("no new in stock notifications :(")
            #  updates tweet ID
            last_value = value
        #  if the tweet wasn't today
        else:
            #  if it's not new, then the wait continues
            no_tweet_text = ("No stock in last hour :( Last stock: %s" % (timestamp))
            text_area.text = "\n".join(wrap_text_to_lines(no_tweet_text, 21))
            print("no new in stock notifications :(")

Upload the Code and Libraries to the Feather TFT ESP32-S2

After downloading the Project Bundle, plug your Feather TFT ESP32-S2 into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the Feather M4 Express' CIRCUITPY drive. 

  • lib folder
  • code.py

Your Feather TFT ESP32-S2 CIRCUITPY drive should look like this after copying the lib folder and the code.py file.

circuitpy

How the CircuitPython Code Works

The code has a query that is sent through the Twitter API to check if a tweet that contains the text "In Stock at Adafruit" has been sent from the account @rpilocator. You can customize this query to look for other text or to look at tweets from a different account.

tweet_query = 'https://api.twitter.com/2/tweets/search/recent?query=In Stock at Adafruit from:rpilocator&tweet.fields=created_at'

In the loop, a request is sent every 30 seconds to check the current time and to send the tweet query. The response from Twitter is parsed into a JSON feed stored in data. The feed is sliced to see the tweet ID, tweet text and the tweet timestamp.

The tweet ID is compared to the last tweet ID that was stored in last_value. If they don't match, that means a new tweet has been received that matches the query. The tweet's timestamp is compared against the current time. If the tweet was sent in the last hour, then the alarm goes off and the text on the TFT display is updated.

#  every 30 seconds...
    if (check + 30) < time.monotonic():
        #  updates current time
        current_time = requests.get(TIME_URL)
        print(current_time.text)
        #  get tweets from rpilocator containing in stock at adafruit
        the_tweet = requests.request("GET", url=tweet_query, headers=headers)
        #  gets data portion of json
        data = the_tweet.json()['data']
        #  tweet ID number
        value = data[0]['id']
        #  tweet text
        stock_check = data[0]['text']
        #  timestamp
        timestamp = data[0]['created_at']
        #  reset time count
        check = time.monotonic()
        #  compare last tweet ID and current tweet ID
        if last_value != value:
            #  compares date of tweet timestamp with current date
            if timestamp.startswith(current_time.text[0 : 10]):
                print("match")
                #  grabs the hour of the tweet timestamp
                tweet_hour = int(timestamp[11:12])
                print(tweet_hour)
                #  grabs the current hour
                current_hour = int(current_time.text[11:12])
                print(current_hour)
                #  if it's been less than an hour since the tweet...
                if abs(current_hour - tweet_hour) < 2:
                    print("in the last hour")
                    #  displays tweet text and time on screen
                    text_area.text = "\n".join(wrap_text_to_lines(stock_check, 21))
                    print(stock_check)
                    clock_area.text = timestamp
                    print("Raspberry Pi in stock at Adafruit!")
                    alarm_out.value = True

You can access Twitter data for your projects using the Twitter API. To begin, you'll need to apply for a developer account with your Twitter account at https://developer.twitter.com/en/apply-for-access

Once you click apply, you'll be prompted to select your use case. Under Hobbyist, select Exploring the API.

After confirming your contact details, you'll be asked to describe how you'll be using the Twitter API. Enter in information as applicable. If you're planning to use the API for other projects beyond this one, you may want to include that information here for the future.

After confirming your information and agreeing to terms of service, you'll click Submit Application. Applications are usually approved fairly quickly, usually within a few minutes. 

Create a Project

When you log into the developer portal, you'll see your developer dashboard. Here you can access all of your account information and documentation.

In order to pull from the Twitter API, you need to create a Project. At the bottom of the Dashboard page, click on New Project and then complete the information about your Project.

Each Project has an App, which stores your API keys and access points.

Add an App to the Project your just created. Enter your desired name for your App and then click Complete.

After you've created the Project and App, you can access the Project page which supplies Usage stats and allows access to your App. 

If you click on the App Settings button (the cog wheel), you can adjust access settings for the App and access your Authentication tokens.

Do not share your Twitter API Authentication tokens with anyone!

Keep Your Secrets Secret

The project's code needs access to your Twitter API Authorization tokens and AdafruitIO credentials. To keep this information secret, but still accessible, you can enter this information into a file called secrets.py.

secrets = {
    'ssid' : 'insert your network name here',
    'password' : 'insert your network password here',
  	'aio_username' : "your-aio-username-here",
    'aio_key' : 'your-aio-key-here',
    'timezone' : "America/New_York", # http://worldtimeapi.org/timezones
    'twitter_api_key' : 'insert your twitter api key here',
    'twitter_secret_key' : 'insert your twitter secret key here',
    'bearer_token' : 'insert your bearer token here'
    }

In order to drive the alarm from the Feather, you'll use a transistor to switch the 5V USB power on an off. This works great because the siren can run on anywhere from 3V - 12V DC power. It requires more current than a 3.3V GPIO pin can provide, which is why we'll tap into the 5V line.

Driver Circuit

The diagram for the driver circuit is shown below. The red LED is a stand-in for the alarm.

We are using pin A1 of the Feather through a resistor to the left leg of the transistor (base) in order to toggle when the transistor opens or closes the middle leg (collector), which allows the alarm circuit to go to ground via the right leg (emitter).

Build the Proto FeatherWing Circuit

Replicate the circuit shown above on the Proto FeatherWing.

Also add some full sized Feather socket headers as shown so that we can connect the TFT Feather on top of it later, allowing the screen to be read.

Solder header pins to the Feather as shown.

Stand

You can make a nice neat assembly for your RPi Stock Alarm using a simple 3D printed stand and some M2.5 fasteners.

If you don't have access to a 3D printer you can make a pretty good stand out of cardboard, such as the box the alarm came in.

Attach Feather to Stand

You'll make a sandwich of the stand between the Feather and FeatherWing Proto.

First, place the Feather on top of the stand and screw in four long M2.5 screws, being careful not to crush the TFT screen.

Flip the stand over and screw on the short standoffs.

Attach FeatherWing

Place the FeatherWing under the Feather and press its headers into place, joining them.

Then, use short screws to attach.

USB Wiring

Feed the USB cable through the large hole in the stand as shown.

Alarm Mount

Feed the alarm wire through the same large stand hole and press the three M4 bolts through their respective holes.

Use the included nuts to secure.

Alarm Wiring

Screw the alarm wires into the respective positions on the screw terminal block.

To avoid confusion, I used white paint marker to indicate the ground position for the black wire.

Plug in USB

Plug the USB C cable into the Feather.

Use the Alarm

Your RPi Stock Alert Alarm is ready for use! Plug the USB cable into power and it will begin checking rpilocator.com tweets. If there is no new stock, it will report this. As soon as stock comes in, the alarm will flash and make a terrible noise! (You can set the volume of the buzzer with the knob.)

Press the Boot button on the Feather to stop the alarm, then go get yourself that coveted Rapsberry Pi!

This guide was first published on May 11, 2022. It was last updated on May 11, 2022.