Iteration 4 - Analog Out (Daniel Lawton)
IoT Apps Iteration 4 - Micro:bit Spotify Integration (Analog Output )
For this iteration, the goal was to come up with a use case that involves the use of a transport layer, sending data from the cloud to a micro:bit. Working with Jay Langford, we created a use case that integrates with Spotify to send an artist's track information to a micro:bit when a button is pressed, allowing users to see what song is currently playing on the micro:bit's LED display.
Focusing on the Analog output I found difficulties integrating IFTTT with the micro:bit to receive values, this was not an issue when triggering the IFTTT applet with a webhook through the makecode micro:bit (AI) but when trying to receive a value from my IFTTT applet, GET instead of POST, this caused issues leading to the pivot from using IFTTT to using a local server that could be used to send HTTP requests (GET, POST, PUT .etc).
Project Features
- Skip Track: Press Button A on the micro:bit to skip to the next track on Spotify.
- Display Current Song: Press Button B to show the currently playing track information on the LED display.
- Cloud Communication: Utilises HTTP requests for bidirectional communication between the micro:bit and Spotify's API
Monday Class 24/02/2025
Jason devised this iteration, following up on the analog input iteration, giving the task to create a full AI/AO application using cloud messaging protocols like MQTT, Webhook, and HTTP requests. We brainstormed for the first half of class, each coming up with ideas, I landed on the idea of creating a micro:bit MP3 player, that allows users to skip tracks and display currently playing songs.
Analog Input Implementation:
Jay and I decided to work on this iteration together, with Jay taking the lead on developing the Analog Input section. Using IFTTT (as shown in the previous blog), he configured the webhook to call Spotify's API with the "skip track" command when triggered. With the micro:bit code from our previous project, pressing Button A would trigger the webhook, sending an HTTP POST to the IFTTT endpoint.
Jay found a Python library that provides access to the Spotify API called Spotipy. For this to work we had to create a developer app in Spotify to obtain API and secret keys, allowing us to retrive the currently playing track information.
Friday's Class February 28/02/2025
With the Analog Input working successfully by the end of the first class, I focused on the Analog Output components. The challenge was finding a way for IFTTT to trigger a response from the Spotify API, sending artist and track information as a string to display on the LED matrix-essentially the revers of our original implementation.
I encountered issues with the IoT extension in MakeCode, which made it difficult to receive IFTTT events. This led to a pivot in our approach using direct HTTP requests instead of IFTTT as they offered more flexibility for both POST and GET operations.
With access to Spotify directly, the next step was to use a cloud based messaging protocol that would work with the micro:bit. Flask, is a python library that allows you to set up your own local server that uses HTTP requests for publishing an subscribing to data. I set up an HTTP server using Flask, running on my machine locally, we could publish the Spotipy values to an endpoint that the micro:bit could access when connected to the same network.
Class Discussion
Flask Server with Spotipy
from flask import Flask, jsonify
import spotipy
from spotipy.oauth2 import SpotifyOAuth
app = Flask(__name__) # Set up local Flask server
# Spotify API credentials
SPOTIPY_CLIENT_ID = "Insert Client ID here"
SPOTIPY_CLIENT_SECRET = "Insert Secret Key here"
SPOTIPY_REDIRECT_URI = "http://localhost:8888/callback"
# Set up authentication
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
client_id=SPOTIPY_CLIENT_ID,
client_secret=SPOTIPY_CLIENT_SECRET,
redirect_uri=SPOTIPY_REDIRECT_URI,
scope="user-read-currently-playing user-modify-playback-state"))
@app.route('/current-track', methods=['GET']) # route to get current playing-track
def get_current_track():
current_track = sp.currently_playing()
if current_track and current_track.get('item'):
track_name = current_track['item']['name']
artist_name = current_track['item']['artists'][0]['name']
return jsonify({"track": track_name, "artist": artist_name})
else:
return jsonify({"track": None, "artist": None})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000) # Runs Flask server on local network (Server Hosts IP) on port 5000
Micro:bit Configuration:
The code involved creating a new route called '/current_track' in the Flask server to receive data from Spotipy through an HTTP request. On the micro:bit side, we used the same IoT extension from the previous iteration to configure the connection to the Flask server using the server's IP address and port 5000. During the Friday class on February 28, 2025, I was able to get the Flask server successfully configured with the micro:bit. The http GET request was able to reach the Flask Server.
The main challenges in this class was getting the micro:bit configured to my local Flask server. The first step in solving this was using a python script on my machine that subscribed to the Flask server using http get and got the correct response, in this case the current playing track and the artist.
from datetime import date
import requests
FLASK_SERVER_URL = "http://192.168.0.92:5000/current-track"
def get_current_track():
try:
response = requests.get(FLASK_SERVER_URL)
if response.status_code == 200:
data = response.json()
if data.get("track"):
print(f"Now playing: {data['track']} - {data['artist']}")
else:
print("No song currently playing.")
else:
print(f"Error: {response.status_code} - {response.text}")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
if __name__ == "__main__":
get_current_track()
With this working, but with no access to a CloudBoard, I pivoted slightly and connected my micro:bit serially to my local machine, taking the values from my subscribe.py file to then write the values to my micro:bit's LED display. This worked and at this point I was confident now how the data would need to be parsed to display on my micro:bit using HTTP get.
from datetime import date
import requests
import serial
import time
FLASK_SERVER_URL = "http://192.168.0.92:5000/current-track"
def get_current_track():
try:
response = requests.get(FLASK_SERVER_URL)
if response.status_code == 200:
data = response.json()
if data.get("track"):
track_info = f"{data['track']} - {data['artist']}"
print(track_info)
return track_info
else:
print("No song currently playing.")
return "No song currently playing."
else:
error_message = f"Error: {response.status_code} - {response.text}"
print(error_message)
return error_message
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return f"Request failed: {e}"
if __name__ == "__main__":
COM_PORT = '/dev/tty.usbmodem141302'
ser = serial.Serial(COM_PORT, 115200)
try:
while True:
track_info = get_current_track()
ser.write(track_info.encode())
time.sleep(30)
except KeyboardInterrupt:
print("Program stopped manually")
finally:
ser.close()
Monday's Class 3/03/2025
With the micro:bit successfully configured to the Flask server, and knowing how the values being sent need to be through using Serial. The task now was simply to drop the Serial comms, use the cloud board on the same network as the Flask Server, and retrieve the response from the http get requests to display on the micro:bits LED display.
When having both AI/AO on the one micro:bit board the AI part with IFTTT would not work, only the http request would successfully trigger the
Comments
Post a Comment