15. Interface and Application Programming
Assignments
Here you can find a recording of the lecture from the 30th of April.
This week's assignments and learning outcomes, see here:
Group assignment:
- Compare as many tool options as possible.
- Document your work on the group work page and reflect on your individual page what you learned
You can find the documentation for our group assignments here.
Individual assignment:
- Write an application for the embedded board that you made that interfaces a user with an input and/or output device(s)
Questions to be answered/from Nueval:
Have you answered these questions?
- Linked to the group assignment page.
- Documented your process.
- Explained the UI that you made and how you did it.
- Explained how your application communicates with your embedded microcontroller board.
- Explained any problems you encountered and how you fixed them.
- Included original source code (or a screenshot of the app code if that's not possible).
- Included a ‘hero shot’ of your application running & communicating with your board.
Link to group page
Here you can find the documentation of our group assignment.
Hero shots
Video
Here you can see how the Blynk website is used to control the Neopixels that are connected to the board that I made for the final project. See more information about the board here.
Blynk and Raspberry Pi Pico W with Neopixels
Turning the globe
This video shows how I used templates in D3, mixed different codes and added information on longitude and latitude points. The globe can be rotated in all directions by grabbing it with the mouse and moving it. The red dots are the latitude and longitude points that I added to a .csv file to display the travels of an imaginary Humpback whale. The website can be found here. P.S. Note that in this project I was not communicating with a microcontroller.
Turning the globe
Summary
This week I looked into interface and application programming. I compared different tools but I worked mainly with D3 and Blynk.
Work process detail
Group assignment
Link to group page
You can find the documentation for our group assignments here.
Individual assignment
Copyright
Note that I used three templates when working with D3, as described her below, and all of them are made by Mike Bostock. You can read about the copyright here. The copyright permission is also signed by Ricky Reusser, but his name is not mentioned in the templates. I added the full text from the copyright permission with files at the bottom of this page.
D3 basics
D3
I chose to look at D3 and it's possibilities. D3 is a free, open source online editor that has broad variety of templates for visualizing data. It is low-level JavaScript based library and it offers multiple ways of displaying all kinds of information. I like the possibilities of D3 so much and could have spent many days checking out different templates if I had the time, but this is all new to me and everything I did took me a very long time. It is highly likely that I will try out more of D3's tools.
Animation and interaction
You can use animation like D3-timer, D3-ease and D3-transition.
You can also use different types of interaction like D3-brush, D3-dispatch, D3-zoom and D3-drag.
Observable inputs
You can use observable inputs like D3-timer, D3-ease and D3-transition
Many examples
You can check out many examples in the D3 gallery.
Fork an example
If you want to work with an example, you can Fork the example into your project folder. You choose if you want your project to be public or unlisted, for a team or just for yourself.
Beginning to work
You need to sign up before working with 3D. You create workspaces and in them you create notebooks. Here below you can see an overview of a workspace and the notebooks created in it. To create a new notebook you click on the plus in the top right corner. From there you can choose to work in a blank notebook or use a template.
Templates
There is a wide variety of templates to work with.
Help panel and database
There are many ways to find information when you begin working with D3. When you are working in a notebook you can click on a question mark to open help panel. You can also search for more information on the website, f.ex. in this summary of data.
My projects
An example forked into projects folder
What is a versor dragging?
The template, that I will tell you about here below, is based on versor dragging. It means that you can rotate the globe with a mouse. This method was introduced by Jason Davies and Mike Bostock. You can read more about it here.
Template showing World airports
I found a template showing World airports Voronoi by Mike Bostock. You can see another view of it here. I forked this example into my projects folder and began editing it.
Here below you can see the original code. I split the code into codeblocks, just like it is setup on the original. I wrote what I deleted (with hashtags). Below the code you can see what changed in the image.
chart = {
const context = DOM.context2d(width, height);
const path = d3.geoPath(mutable projection, context).pointRadius(1.5);
function render() {
context.clearRect(0, 0, width, height);
context.beginPath();
path(graticule);
context.lineWidth = 0.5;
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath(); # I deleted this
path(mesh); # I deleted this
context.lineWidth = 0.5; # I deleted this
context.strokeStyle = "#000"; # I deleted this
context.stroke(); # I deleted this
context.beginPath();
path(sphere);
context.lineWidth = 1.5;
context.strokeStyle = "#000";
context.stroke();
context.beginPath();
path({type: "MultiPoint", coordinates: points});
context.fillStyle = "#f00";
context.fill();
}
function dragged() {
mutable projection = mutable projection;
render();
}
return d3.select(context.canvas)
.call(drag(mutable projection).on("drag.render", dragged))
.call(render)
.node();
}
mutable projection = d3.geoOrthographic()
.fitExtent([[1, 1], [width - 1, height - 1]], sphere)
.rotate([0, -30])
points = (await FileAttachment("airports.csv").csv({typed: true})).map(({longitude, latitude}) => [longitude, latitude])
After deleting mesh
I wanted to get rid of the lines that connected the dots, so I deleted the part about mesh (see where I marked it in the code here above). Se here below in these two images how the map changed
Finding out how to delete airports and add location of whale
I wondered how I could change the information on all these red dots, which marked airports, and change them into dots that showed the location of the Humpback whale. I noticed that there was a symbol for file attachments. There I found a .csv file with the longitude and latitude for all these airports. I do not know how to create a .csv file, so I decided to download the file, make a copy, delete all information and fill in new ones.
Latitude and longitude for whale
Now I had one problem. The information here I got from Marine and freshwater research institute did not include gps or latitude and longitude. It only showed a paths that the whales had travelled.
Travels of Humpback whales, Marine and freshwater research institute, 2025.
Chosen path
I decided to use the path that the yellow arrow is pointing at. The path is short and that is most likely because of the battery running out or the gps tracking tool falling off, but according to the information here the migration routes lead south. Some humpback whales travel to West Africa and some of them travel all the way to the Caribbean. The path I chose looks as if the whale is travelling in the direction to the Caribbean, maybe to the Dominican Republic. I choose to imagine that the whale had travelled all this way, so my project will not be based on exact information. It will be a story of a humpback whale that might have travelled from the East of Iceland to the Dominic Republic.
Comparing path to Google maps
I compared the path to Google maps and clicked on the location on Google maps to see the longitude and latitude.
Writing longitude and latitude in .csv file
When I opened the .csv file that I downloaded, it opened up as an excel file. I saw an notification warning that maybe not all I used a copy of the .csv file, deleted the airport information and wrote my longitude and latitude for the points where the whale was located.
Changing .csv files
I deleted the .csv file with the airport information and added the file with the longitude and latitude points for the whale. The dots appeared on the map but I noticed that the dots were not in the right places. I had the latitude and the longitude mixed, so I changed this in the .csv file. Then the dots appeared in the right places. Now I only needed to find out how to add countries to the globe. You can see the globe here.
Using information from a template to add to project
Template showing World map
I found another template with the World map by Mike Bostock. I wanted to use the countries showed on it and add it to the map with the airports, because there were no countries on it.
Mixed code for two maps that did not unite
Mix of two codes
Here is a link to a map that I tried to merge together, but it did not work.
I mixed codes from this template and from this template showing World airports Voronoi by Mike Bostock. Here you can see the mixture:
chart = {
const context = DOM.context2d(width, height);
const path = d3.geoPath(mutable projection, context).pointRadius(1.5);
function render() {
context.clearRect(0, 0, width, height);
context.beginPath();
path(graticule);
context.lineWidth = 0.5;
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath(); # I deleted this
path(mesh); # I deleted this
context.lineWidth = 0.5; # I deleted this
context.strokeStyle = "#000"; # I deleted this
context.stroke(); # I deleted this
context.beginPath();
path(sphere);
context.lineWidth = 1.5;
context.strokeStyle = "#000";
context.stroke();
context.beginPath();
path({type: "MultiPoint", coordinates: points});
context.fillStyle = "#f00";
context.fill();
}
function dragged() {
mutable projection = mutable projection;
render();
}
return d3.select(context.canvas)
.call(drag(mutable projection).on("drag.render", dragged))
.call(render)
.node();
}
map = {
const context = DOM.context2d(width, height);
const path = d3.geoPath(projection, context);
context.save();
context.beginPath(), path(outline), context.clip(), context.fillStyle = "#fff", context.fillRect(0, 0, width, height);
context.beginPath(), path(graticule), context.strokeStyle = "#ccc", context.stroke();
context.beginPath(), path(land), context.fillStyle = "#000", context.fill();
context.restore();
context.beginPath(), path(outline), context.strokeStyle = "#000", context.stroke();
return context.canvas;
path({type: "MultiPoint", coordinates: points});
context.fillStyle = "#f00";
context.fill();
context.beginPath();
path({type: "MultiPoint", coordinates: points});
context.fillStyle = "#f00";
context.fill();
}
mutable projection = d3.geoOrthographic()
.fitExtent([[1, 1], [width - 1, height - 1]], sphere)
.rotate([0, -30])
points = (await FileAttachment("Whales@1.csv").csv({typed: true})).map(({longitude, latitude}) => [longitude, latitude])
Rotating world map with Humpback whale's travels marked on it
Template showing rotating World map
Since my previous experiments did not work I decided to keep on looking. I found one more example by Mike Bostock that showed Earth and by grabbing it, you can rotate it. I wanted to see if I could add points that show the travels of a Humpback whale. I did not change much this time. I added the .csv file with the longitude and langitude of points that show examples of the whale's travels.
You can see the project with the added longitude and latitude points here.
Code with changes
Code with added longitude and latitude points
Here you can see the original code from here and how I added points with certain longitude and latitude that is an attached .csv file. Look at the hashtag in front of the sentence: # I added..., to see what I added.
chart = {
const context = DOM.context2d(width, height);
const path = d3.geoPath(projection, context);
function render(land) {
context.clearRect(0, 0, width, height);
context.beginPath(), path(sphere), context.fillStyle = "#fff", context.fill();
context.beginPath(), path(land), context.fillStyle = "#000", context.fill();
context.beginPath(), path(sphere), context.stroke();
# I added the next four lines from [template](https://observablehq.com/@d3/world-airports-voronoi) showing World airports Voronoi by Mike Bostock
context.beginPath();
path({type: "MultiPoint", coordinates: points});
context.fillStyle = "#f00";
context.fill();
}
return d3.select(context.canvas)
.call(drag(projection)
.on("drag.render", () => render(land110))
.on("end.render", () => render(land50)))
.call(() => render(land50))
.node();
}
function drag(projection) {
let v0, q0, r0, a0, l;
function pointer(event, that) {
const t = d3.pointers(event, that);
if (t.length !== l) {
l = t.length;
if (l > 1) a0 = Math.atan2(t[1][1] - t[0][1], t[1][0] - t[0][0]);
dragstarted.apply(that, [event, that]);
}
// For multitouch, average positions and compute rotation.
if (l > 1) {
const x = d3.mean(t, p => p[0]);
const y = d3.mean(t, p => p[1]);
const a = Math.atan2(t[1][1] - t[0][1], t[1][0] - t[0][0]);
return [x, y, a];
}
return t[0];
}
function dragstarted({x, y}) {
v0 = versor.cartesian(projection.invert([x, y]));
q0 = versor(r0 = projection.rotate());
}
function dragged(event) {
const v1 = versor.cartesian(projection.rotate(r0).invert([event.x, event.y]));
const delta = versor.delta(v0, v1);
let q1 = versor.multiply(q0, delta);
// For multitouch, compose with a rotation around the axis.
const p = pointer(event, this);
if (p[2]) {
const d = (p[2] - a0) / 2;
const s = -Math.sin(d);
const c = Math.sign(Math.cos(d));
q1 = versor.multiply([Math.sqrt(1 - s * s), 0, 0, c * s], q1);
}
projection.rotate(versor.rotation(q1));
// In vicinity of the antipode (unstable) of q0, restart.
if (delta[0] < 0.7) dragstarted.apply(this, [event, this]);
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged);
}
height = {
const [[x0, y0], [x1, y1]] = d3.geoPath(projection.fitWidth(width, sphere)).bounds(sphere);
const dy = Math.ceil(y1 - y0), l = Math.min(Math.ceil(x1 - x0), dy);
projection.scale(projection.scale() * (l - 1) / l).precision(0.2);
return dy;
}
land50 = FileAttachment("land-50m.json").json().then(world => topojson.feature(world, world.objects.land))
land110 = FileAttachment("land-110m.json").json().then(world => topojson.feature(world, world.objects.land))
points = (await FileAttachment("Whales.csv").csv({typed: true})).map(({longitude, latitude}) => [longitude, latitude]) # I added this from [template](https://observablehq.com/@d3/world-airports-voronoi) showing World airports Voronoi by Mike Bostock and swithced the original .csv file with an .csv file with my latitude and longitude points for the whale
Video showing how the globe can be turned around
Turning the globe
This video shows how the globe can be rotated in all directions by grabbing it with the mouse and moving it. The red dots are the latitude and longitude points that I added to a .csv file to display the travels of an imaginary Humpback whale. The website can be found here.
Turning the globe
Communicating with my embedded microcontroller
Can you interact with D3js.org?
I really wanted to find a way for my microcontroller to interact with the globe in the video above. I checked out different things and I found out that when you add a codeblock in D3, it is possible to choose a command that fetches information from HTTPS. I wondered if I could make the Raspberry Pi Pico W send information to a website that this command could fetch!
Trying to use RPP to serve a website
Can RPP send data to a website?
I found these directions and tried to follow them, hoping that I could use the website to connect the RPP and D3. It was easy to create a HTML document and open it online, but when it came to letting the RPP serve the website it did not work. I copied the codes from the website, so it was exactly the same, but I got error notifications on some of the commands. I decided to quit and look for something else.
Blynk
Blynk basics
A few months ago I heard someone mention Blynk and I decided to create an account, but I never got to try it out. So, when I saw that Blynk was on the list of user interfaces, I decided to check it out. According to this site, Blynk is an IoT cloud platform and it is based on low-code. It works as an online editor and also as an app. It is possible to control different IoT devices, f.ex. devices that connect through satellite. In Blynk, you can also gather data and view it on a dashboard. Here you can see an overview of the developer zone in Blynk:
Installing hardware in Blynk
Installing hardware
When I planned on connecting my Raspberry Pi Pico W to Blynk, I ran into endless problems. I found a Quickstart template on my dashboard that led me through a process step by step. Here you can see the directions on how to use the quickstart.. I chose Raspberry Pi Pico as my microcontroller. Then I chose Wifi.
Choosing other
In the next step I chose Other, because I planned on using Thonny.
Installing Blynk library in Thonny (not the right one)
Installing library in Thonny
I installed a Blynk library for Thonny from here.
Installing Blynk library in Arduino IDE
Installing library in Arduino IDE
I also decided to install a Blynk library in Arduino IDE, just to be ready for anything. It was done by clicking on Tools, Manage libraries, search for Blynk and install all. When I opened the Blynk examples, it seemed to have many possibilities.
Failure
In the next step I followed the directions when I added information, such as the name of my wifi and the wifi key. When everything was ready the directions told me to copy the code and paste it into my programmer. I pasted it into Thonny and waited for a connection, but it did not happen.
Info
I unplugged the microcontroller and plugged it in again. Then I tried to find directions on how to use Arduino IDE to connect a microcontroller. I found information here by Elen . She seems to have had problems just like me, but this code from Blynk worked for her:
#define BLYNK_FIRMWARE_VERSION "0.1.0"
#define BLYNK_PRINT Serial
// libraries and defines the WiFi credentials and Blynk authorization token
#define BLYNK_TEMPLATE_ID "TMPL62FAZkgPN"
#define BLYNK_TEMPLATE_NAME "Fading light ESP12F"
#define BLYNK_AUTH_TOKEN "Syz7_B8V-5aH91ZoqOg00eoDAbIrP383"
#include <ESP8266WiFi.h> // wifi library for esp8266-esp12
#include <BlynkSimpleEsp8266.h>
char ssid[] = "****";
char pass[] = "****";
// Blynk widgets and variables used in the sketch.
WidgetLED led1(V0); // Virtual pin in Blynk
const int ledPin = 2; // gpio2 pin on the board
BlynkTimer timer; //variable is used to create a timer that runs every 300 milliseconds.
//function is called when the microcontroller connects to the Blynk server.
BLYNK_CONNECTED() {
Blynk.syncVirtual(V0); // synchronizes the state of V0 Blynk widget with the microcontroller.
int value = millis() / 1000;
Blynk.virtualWrite(V0, value); //sending value in elapsed time
// You can send any value at any time.
// Please don't send more that 10 values per second.
}
// This function will be called every time Slider Widget writes values to the Virtual Pin V0
BLYNK_WRITE(V0) {
int sliderValue = param.asInt(); // assign incoming value from V0 to variable
Serial.print("V0 Slider value is: "); // print on serial monitor the value from the widget
Serial.println(sliderValue);
analogWrite(ledPin, sliderValue); // LED pin gets the value from the slider
delay(30);
}
void setup() {
Serial.begin(9600); // initializes the serial connection
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass); // sets the wifi connection.
pinMode(ledPin, OUTPUT); // Led pin set output
}
void loop() {
Blynk.run(); // runs the Blynk and timer functions, allowing the microcontroller to communicate
timer.run(); //with the Blynk server and update the state of the Blynk widgets.
}
Raspberry Pi not emphasized in Arduino IDE Blynk library
Failure
I changed the wifi information, wrote which version of library I was using and took away the BlynkSimpleEsp8266 library and ESP8266WiFi.h wifi. At this point I did not know what to do and the code did not work. I looked into Blynk examples from the Blynk library that I installed and I could not see the Raspberry Pi Pico on the list. I could choose the RPP as always, by choosing Tools and then Board, but Blynk did not seem to emphasize the use of Raspberry Pi Pico in the examples.
Trying to use a Blynk library with a code in Thonny
Info
I had installed a Blynk library for Thonny from here. Then took another look at that page and ran this code from the same page. This is the code:
import BlynkLib
# Initialize Blynk
blynk = BlynkLib.Blynk('YourAuthToken')
# Register Virtual Pins
@blynk.VIRTUAL_WRITE(1)
def my_write_handler(value):
print('Current V1 value: {}'.format(value))
@blynk.VIRTUAL_READ(2)
def my_read_handler():
# this widget will show some time in seconds..
blynk.virtual_write(2, int(time.time()))
while True:
blynk.run()
Failed to connect Raspberry Pi Pico to Blynk
Failure
The Blynk logo appeared in the shell, and that made me happy, but also a notification that said: OSError: -6. When I checked, the RPP had not managed to connect to Blynk.
I have been trying to solve problems on my own lately, because I want to be independent when facing problems in Fab Academy, but at this point I was starting to ask others if they knew the solution to this. Unfortunately, that did not help either.
Another attempt
Directions with Node.js (not accepted)
I found another directions here in the Blynk community here, written by the co-founder of Blynk. In these directions it is recommended to install Node.js. I tried to download it but I was notified that my organization does not accept it.
Help from Copilot to connect RPPW to Blynk
Board with Raspberry pi pico W and Neopixel ring
I made the board that I will use in my final project in Wildcard week (week 17), but I documented it here. I used it in the following attempts to connect to Blynk. At that point I had connected the board to the magnetic levitation module with a voltage regulator, so that I could use the same power cord with both.
Copilot asked for information
Question
I should have finished this weeks assignments before May 7th, but the problem with connecting the Raspberry Pi to Blynk continued. I had searched for answers but found nothing helpful. On May 21st to May 24th I attended a Bootcamp for Fab Lab Ísland. At this point I had no idea what to do next, so I asked Árni Björnsson for assistance on the last day of the Bootcamp. He went over what I had done and tried to connect the Raspberry Pi Pico W to Blynk. That did not work so he suggested that we would ask Copilot about what could be wrong. I had not used Copilot or ChatGPT before in Fab Academy and I agreed to it. Árni began with this prompt (Note that I put stars instead of sensitive information):
Hello. I'm using thonny and I'm trying to connect raspberry pi pico 2 w to blynk.
I have the blynk library installed along with micropython. This is my current code:
import BlynkLib
# Initialize Blynk
blynk = BlynkLib.Blynk('**********')
# Register Virtual Pins
@blynk.VIRTUAL_WRITE(1)
def my_write_handler(value):
print('Current V1 value: {}'.format(value))
@blynk.VIRTUAL_READ(2)
def my_read_handler():
# this widget will show some time in seconds..
blynk.virtual_write(2, int(time.time()))
while True:
blynk.run()
I'm probably missing the wifi information. Can you help me ?
Answer from Copilot
This was the answer from the Copilot:
You're absolutely right — to connect your Raspberry Pi Pico W to Blynk, you need to establish a Wi-Fi connection first. The Blynk library for MicroPython doesn't handle Wi-Fi setup automatically, so you need to do that manually before calling blynk.run().
Here’s how you can modify your code to include Wi-Fi setup using the network module:
import network
import time
import BlynkLib
# Replace with your Wi-Fi credentials
WIFI_SSID = 'YourWiFiSSID'
WIFI_PASSWORD = 'YourWiFiPassword'
# Connect to Wi-Fi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
print("Connecting to Wi-Fi...", end="")
while not wlan.isconnected():
print(".", end="")
time.sleep(1)
print("\nConnected to Wi-Fi")
print("IP address:", wlan.ifconfig()[0])
# Initialize Blynk
blynk = BlynkLib.Blynk('*****')
# Register Virtual Pins
@blynk.VIRTUAL_WRITE(1)
def my_write_handler(value):
print('Current V1 value: {}'.format(value))
@blynk.VIRTUAL_READ(2)
def my_read_handler():
blynk.virtual_write(2, int(time.time()))
# Main loop
while True:
blynk.run()
A few tips: Make sure your Raspberry Pi Pico W is running a version of MicroPython that supports Wi-Fi (you can get it from the official Raspberry Pi website). Ensure your Blynk library is compatible with MicroPython and supports the Pico W.
Next question
Then Árni Björnsson asked the following (Note that the Blynk logo appeared in the shell in Thonny but when I added the text here the logo did not appear the same way - for that reason I added the image instead):
Ok, this got me a bit further! Now I get this error:
Connecting to Wi-Fi.......
Connected to Wi-Fi
IP address: **
Connecting to blynk.cloud:443...
Traceback (most recent call last):
File "boot.py", line 22, in
File "/lib/BlynkLib.py", line 214, in init
File "/lib/BlynkLib.py", line 81, in init
File "/lib/BlynkLib.py", line 239, in connect
AttributeError: 'module' object has no attribute 'create_default_context'
MicroPython v1.25.0 on 2025-04-15; Raspberry Pi Pico W with RP2040
Type "help()" for more information.
What should I do
Second answer from Copilot
This is the answer from Copilot:
You're encountering this error because the BlynkLib library you're using is trying to use ssl.create_default_context, which is not available in MicroPython — it's a function from standard Python's ssl module, not supported on the Raspberry Pi Pico W.
Here's how to fix it: You need to use a version of the Blynk library that is compatible with MicroPython. The standard BlynkLib.py from the original Blynk Python library is meant for full Python environments, not MicroPython.
Solution: Use a MicroPython-Compatible Blynk Library
Download the MicroPython-compatible Blynk library:
Use this version: https://github.com/vshymanskyy/blynk-library-python Or directly download blynk_mp.py Rename it to BlynkLib.py (if needed) and upload it to your Pico W using Thonny into the /lib folder.
Update your code to use port 80 instead of 443, since MicroPython doesn't support SSL out of the box on all boards.
Here’s a revised version of your code using the MicroPython-compatible library:
import network
import time
import BlynkLib
# Wi-Fi credentials
WIFI_SSID = 'YourWiFiSSID'
WIFI_PASSWORD = 'YourWiFiPassword'
# Connect to Wi-Fi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
print("Connecting to Wi-Fi...", end="")
while not wlan.isconnected():
print(".", end="")
time.sleep(1)
print("\nConnected to Wi-Fi")
print("IP address:", wlan.ifconfig()[0])
# Initialize Blynk (use port=80 for non-SSL)
blynk = BlynkLib.Blynk('*****', port=80)
# Virtual pin handlers
@blynk.VIRTUAL_WRITE(1)
def my_write_handler(value):
print('Current V1 value: {}'.format(value))
@blynk.VIRTUAL_READ(2)
def my_read_handler():
blynk.virtual_write(2, int(time.time()))
# Main loop
while True:
blynk.run()
Results
After getting these answers I drove back from Húsavík home to Neskaupstaður and read the results. The answers from Copilot suggest that I should use port 80 instead of port 443, because of SSL issues in the RPPW and I could see how to add this port to the code. The answers also said that I had been using the wrong library. I opened up the link that Copilot said was the right edition here. It was the same library that I had tried to install before. I looked into what was on the page and then I saw that Version 2 had been released, as you can see in the image below.
A newer version of Blynk library
Adding Version0 2.0 of Blynk library
I clicked on the button for Release v0 2.0 which took me to this page and downloaded two zip folders.
Installing Version0 2.0 of Blynk library
In Thonny I clicked on Tools and then Manage packages. Then I clicked on the Install from local file option, opened the Downloads folder and chose the zip folder that I had downloaded. I did this two times to choose both zip folders.
A blueprint template for MQTT in Blynk
Directions - MQTT
Árni Björnsson had found directions by Volodymyr Shymanskyy, co-founder of Blynkkk, on how to use a MQTT example in Blynk to connect a device to Blynk, see here. I followed the directions and created a new template on Blynk based on a blueprint example. Then I created a new device in this template. Then I used this code that was provided on the page:
Syntax error
When I ran this code there was a syntax error. I wondered if this mpremote was not installed, so I opened the Tools and then Manage packages. There I used the search window and looked for mpremote. There I could see that this was a tool to interact remotely with MicroPython devices and I decided to click on it and install it.
Not MicroPython package
Then a warning appeared that this did not look like a MicroPython package, but I decided to continue with the installation anyway because when I read about Mpremote here it looked as if it could be useful.
Not working
Then the next step in the directions was to reset my board and open MicroPython REPL by using this code:
Unfortunately, the only thing that happened was a warning that this was an invalid syntax and I did not know what to do.
Another question
When I tried the code again, there was an error. I decided to ask the Copilot again:
After following your directions the error messages are as following:
Connecting to blynk.cloud:80...
Traceback (most recent call last):
File "
File "/lib/BlynkLib.py", line 214, in init
File "/lib/BlynkLib.py", line 81, in init
File "/lib/BlynkLib.py", line 239, in connect
AttributeError: 'module' object has no attribute 'create_default_context'
Answer from Copilot
This is the answer from Copilot:
Thanks for sharing the error — this confirms that the version of BlynkLib.py you're using is still trying to use ssl.create_default_context, which is not supported in MicroPython.
This means that you likely still have the wrong version of the Blynk library — probably the standard Python version, not the MicroPython-compatible one.
Let's fix this step-by-step: 1. Download the correct MicroPython version of the Blynk library Use this direct link to download the correct file:
Download blynk_mp.py
Save it as: BlynkLib.py (rename it during or after download)
- Upload it to your Pico W
Open Thonny
Go to View > Files
In the bottom pane, select "Raspberry Pi Pico"
Navigate to the /lib folder
If BlynkLib.py already exists there, delete it
Upload your new BlynkLib.py (the one you just downloaded and renamed)
- Update your code to use port 80
Make sure your code includes:
Checking if the right library was installed
Is the library there?
I saw that the Blink Library was in the right place.
Can I verify that this is the right library?
The next thing I needed to do was to verify that this was the right library. I asked the Copilot again:
How do I verify the contents of BlynkLib.py?
One more answer
To verify the contents of BlynkLib.py on your Raspberry Pi Pico W using Thonny, follow these steps:
Step-by-Step: View the Contents of BlynkLib.py
Open Thonny and connect your Pico W.
Go to View > Files (if not already visible).
In the bottom-right pane, select "Raspberry Pi Pico" to view the files on your device.
Navigate to the /lib folder.
Double-click BlynkLib.py — this will open the file in the Thonny editor.
You're checking to make sure this is the MicroPython-compatible version. Here's how you can tell:
Near the top, it should not import ssl or use ssl.create_default_context.
It should contain a class like class Blynk: and use usocket instead of socket.
You might see a comment like:
Example of a correct MicroPython version snippet:If you see this (wrong version):
Then you're still using the standard Python version, which won’t work on MicroPython.
Looking into the Blynk library file
I tried to open up the Blynk Library file in Thonny by double-clicking on it, but got a warning that this was not supported. After some consideration I decided to try one thing. I opened the zip folder in downloads and extracted everything in it. Then I opened the BlynkLib.py file by double-clicking on it. Then I used CTRL+F and searched for the word Socket. The word was found, as you can see in the image below, so this was the wrong version.
Saving the BlinkLib.py again and overwriting
The next thing I did was to save the file to the Raspberry Pi Pico W and overwrite the exsisting BlynkLib.py. Then I used this code that I had tried to use before:
import network
import time
import BlynkLib
# Replace with your actual Wi-Fi credentials
WIFI_SSID = 'Here I wrote the name of my WiFi'
WIFI_PASSWORD = 'Here I wrote the password for my WiFi'
# Replace with your actual Blynk Auth Token
BLYNK_AUTH_TOKEN = 'Here I wrote the Blynk Auth Token'
# Connect to Wi-Fi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
print("Connecting to Wi-Fi...", end="")
while not wlan.isconnected():
print(".", end="")
time.sleep(1)
print("\nConnected to Wi-Fi")
print("IP address:", wlan.ifconfig()[0])
# Initialize Blynk (use port=80 for MicroPython)
blynk = BlynkLib.Blynk('Here I wrote the Blynk Auth Token', server='blynk.cloud', port=80)
# Register Virtual Pins
@blynk.VIRTUAL_WRITE(1)
def my_write_handler(value):
print('Current V1 value: {}'.format(value))
@blynk.VIRTUAL_READ(2)
def my_read_handler():
blynk.virtual_write(2, int(time.time()))
# Main loop
while True:
blynk.run()
Failed to connect
Connection with the Blynk server failed
When I ran the code this error message appeared:
ValueError: Connection with the Blynk server blynk-cloud.com:80 failed
I decided to ask the Copilot one more time and ask what could be wrong.
Answer to the Blynk server failed
When I asked the Copilot, this was the answer:
Thanks for the detailed error message — this is very helpful!
The error:
ValueError: Connection with the Blynk server blynk-cloud.com:80 failed
means that your Raspberry Pi Pico W connected to Wi-Fi successfully, but couldn’t reach the Blynk server. This is usually caused by one of the following:
Possible Causes & Fixes
- Incorrect Blynk server address
The MicroPython Blynk library you're using might be trying to connect to blynk-cloud.com, which is outdated.
Fix: Try changing the server to blynk.cloud (the current official domain).
Update this line:
to: 2. Firewall or DNS issuesSometimes, your router or network might block outbound connections to certain domains or ports.
Fix: Try connecting your Pico W to a different Wi-Fi network (like a mobile hotspot) to test if the issue is network-related.
- Outdated or broken Blynk library
Even though you uploaded the MicroPython-compatible version, it might still be outdated or not fully compatible with the latest Blynk server.
Fix: I can help you test a minimal connection script or even modify the library if needed.
Raspberry Pi Pico W finally connected to Blynk
Connection established
I changed this line:
blynk = BlynkLib.Blynk('Here I wrote the Blynk Auth Token', server='blynk.cloud', port=80)
Then the code was like this and it worked!!!!!! Finally!!!!
import network
import time
import BlynkLib
# Replace with your actual Wi-Fi credentials
WIFI_SSID = 'Here I wrote the name of my WiFi'
WIFI_PASSWORD = 'Here I wrote the password for my WiFi'
# Replace with your actual Blynk Auth Token
BLYNK_AUTH_TOKEN = 'Here I wrote the Blynk Auth Token'
# Connect to Wi-Fi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
print("Connecting to Wi-Fi...", end="")
while not wlan.isconnected():
print(".", end="")
time.sleep(1)
print("\nConnected to Wi-Fi")
print("IP address:", wlan.ifconfig()[0])
# Initialize Blynk (use port=80 for MicroPython)
blynk = BlynkLib.Blynk(BLYNK_AUTH_TOKEN, server='blynk.cloud', port=80)
# Register Virtual Pins
@blynk.VIRTUAL_WRITE(1)
def my_write_handler(value):
print('Current V1 value: {}'.format(value))
@blynk.VIRTUAL_READ(2)
def my_read_handler():
blynk.virtual_write(2, int(time.time()))
# Main loop
while True:
blynk.run()
Connection established in Blynk
Here you can see that Blynk has established connection.
Using Blynk to control devices
New template in Blynk
New template
I created a new template.
Adding widgets
Adding widgets
When you choose devices and open one device by clicking on it, this is what you see (image below). Then you add widgets by dragging them from the left side to the area on the bottom right.
Controlling widgets in Blynk
Controlling widgets
I found information on how to control widgets here.
Setting up a datastream in Blynk
Datastream
I found information on how to set up a datastream here. I followed them and set up two types of datastream. One of them was virtual pin datastream and it can be used to send random values from the device. The other one was an enumerable datastream and it can be used to send a value from mobile or web dashboards to a certain device.
Using button in Blynk to turn Neopixel ring on and off
Virtual pins
Here are information on virtual pins.
A template ID
A template ID should always be first in line in a code, as you can read about here.
How to create a code for Blynk?
Svavar Konráðsson wrote a simple code, based on Neopixel code from week 08 with all eight Neopixels turning red. We used this code just to see if everything worked as it should after connecting the Magnetic levitation module and the Raspberry Pi Pico W to the same voltage regulator, see week 08. This is the code:
import neopixel
from machine import Pin
import time
pixPin = 0 # Numper of pin
pixSize = 2 # How many LEDs
pix = neopixel.NeoPixel(Pin(pixPin), pixSize) # Initialization
red = (255,0,0)
while True:
pix[0] = red
pix[1] = red
pix[2] = red
pix[3] = red
pix[4] = red
pix[5] = red
pix[6] = red
pix[7] = red
pix.write()
time.sleep(1)
A code for a button in Blynk - first attempt
I tried to use the Neopixel code here above and mix it with the code that worked when connecting the Raspberry Pi Pico W to Blynk - see here. The plan was to write a code that would make a Neopixel ring turn on and off by clicking on a button in Blynk but it did not work. This is the code that I wrote as a mixture of the two codes that I mentioned:
import network
import time
import BlynkLib
import neopixel
from machine import Pin
import time
pixPin = 10
pixSize = 8
pix = neopixel.NeoPixel(Pin(pixPin), pixSize)
# Replace with your actual Wi-Fi credentials
WIFI_SSID = 'Here I wrote the name of my WiFi'
WIFI_PASSWORD = 'Here I wrote the password for my WiFi'
# Replace with your actual Blynk Auth Token
BLYNK_AUTH_TOKEN = 'Here I wrote the Blynk Auth Token'
# Connect to Wi-Fi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
print("Connecting to Wi-Fi...", end="")
while not wlan.isconnected():
print(".", end="")
time.sleep(1)
print("\nConnected to Wi-Fi")
print("IP address:", wlan.ifconfig()[0])
# Initialize Blynk (use port=80 for MicroPython)
blynk = BlynkLib.Blynk(BLYNK_AUTH_TOKEN, server='blynk.cloud', port=80)
# Register Virtual Pins
@blynk.VIRTUAL_WRITE(1)
def my_write_handler(value):
print('Current V1 value: {}'.format(value))
@blynk.VIRTUAL_READ(2)
def my_read_handler():
blynk.virtual_write(2, int(time.time()))
# Main loop
while True:
blynk.run()
red = (255,0,0)
pix[0] = red
pix[1] = red
pix[2] = red
pix[3] = red
pix[4] = red
pix[5] = red
pix[6] = red
pix[7] = red
A code for a button in Blynk - second attempt
I asked Copilot what I had to change in the code and this was the answer:
You're very close! Your code sets up the NeoPixel ring and connects to Blynk, but there are a few things to adjust so that the Blynk button or switch can actually turn the NeoPixels on and off.
What You Need to Do
Use a virtual pin (e.g., V1) to control the NeoPixels. Move the NeoPixel update logic into the Blynk handler, so it only runs when the button is pressed. Avoid writing to the NeoPixels continuously in the main loop — that would override Blynk control.
Code with adjustments from Copilot
Here you can see the code after Copilot changed it.
import network
import time
import BlynkLib
import neopixel
from machine import Pin
# NeoPixel setup
pixPin = 10
pixSize = 8
pix = neopixel.NeoPixel(Pin(pixPin), pixSize)
# Wi-Fi credentials
WIFI_SSID = 'Here I wrote the name of my WiFi'
WIFI_PASSWORD = 'Here I wrote the password for my WiFi'
# Blynk Auth Token
BLYNK_AUTH_TOKEN = 'Here I wrote the Blynk Auth Token'
# Connect to Wi-Fi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
print("Connecting to Wi-Fi...", end="")
while not wlan.isconnected():
print(".", end="")
time.sleep(1)
print("\nConnected to Wi-Fi")
print("IP address:", wlan.ifconfig()[0])
# Initialize Blynk
blynk = BlynkLib.Blynk(BLYNK_AUTH_TOKEN, server='blynk.cloud', port=80)
# Virtual pin V1 controls the NeoPixels
@blynk.VIRTUAL_WRITE(1)
def control_neopixels(value):
print('V1 value:', value)
if int(value[0]) == 1:
# Turn on (red)
for i in range(pixSize):
pix[i] = (255, 0, 0)
pix.write()
else:
# Turn off
for i in range(pixSize):
pix[i] = (0, 0, 0)
pix.write()
# Main loop
while True:
blynk.run()
Video showing the embedded board with output device receiving communications from Blynk website
Video
Here you can see how the Blynk website is used to control the Neopixels that are connected to the board that I made for the final project. See more information about the board here.
Blynk and Raspberry Pi Pico W with Neopixels
Learning outcome
This was a very difficult week for me and it took me much more than one week to finish the assignments. I have spent enormous time searching for solutions online but I just could not find answer to all problems. It was interesting to try out Copilot to find solutions. I think it is important not to use the answers from Copilot blindly, because they can be wrong. I also think it is right to try to understand the answers so that I can learn from them.
When I tried again and again to connect the Raspberry Pi Pico W to Blynk I could not find directions online that explained how to write code to connect to Blynk, and I spent a lot of time searching. The Copilot helped me solve this. I also had not discovered that the library saved on the Raspberry Pi Pico W was not the right library. I thought I had the right one. Then there was one more thing that Copilot helped me with; I had no idea that I needed to use another port and that I was not using the right server for blynk.cloud.
When I tried to write a code for using a button in Blynk to turn Neopixels on and off, I learned that I did not need to import time. I also learned that I could write pix[i] = (255, 0, 0) to control all the Neopixels and that I did not have to write a line for each one of them, if I was only going to turn them all on or off at the same time and use the same colour. I could also see that the code for the Neopixels was not in the main loop. The Copilot said that the Neopixel update logic should be placed in the Blynk handler (above the main loop), for it to run only by pressing the button. Writing the Neopixel update logic in the main loop would override the Blynk control, like Copilot mentioned. The Copilot also used def control_neopixels(value): instead of def my_write_handler(value):.
I thought about it many times to give up on the project with Blynk, but now I am glad that I did not give up. I learned so much.
Files
- Original file by Mike Bostock from here with longitude and latitude for airports
Original file from Mike Bostok
- File with longitude and latitude mixed up
- File with correct longitude and latitude
Correct longitude and latitude
Copyright permission - full text
Copyright
Note that I used three templates when working with D3 and all of them are made by Mike Bostock. You can read about the copyright here. The copyright permission is also signed by Ricky Reusser, but his name is not mentioned in the templates. Here is the full text:
Copyright permission - full text
Copyright 2013-2021 Mike Bostock
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
MIT License for https://github.com/scijs/integrate-adaptive-simpson
The MIT License (MIT)
Copyright 2015 Ricky Reusser
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.