How to turn on a ledstrip based on time (or using a public transport API)?
My challenge was to turn on a ledstrip at a certain time based on data from a Dutch public transport API: NS Reisinformatie API.
The first step is to make the ledstrip turn on at a certain time, for example 09.00 AM. ๐
After that I want the ledstrip to turn on 10 minutes earlier when there is a calamity, disruption or maintenance in public transport. ๐ง
๐ Date: 26/10/2022
- NodeMCU esp8266 board
- RGB LED strip
- Arduino IDE & Wifi (not 5G)
If you haven't connected you ledstrip to your esp8266 yet, this is how I did it: The 5V wire goes to 3V3, the GND wire goed to GND, and the middle wire (Din) goes to D5.
First I uploaded the example code Simple from Adafruit Neopixel to my board to test whether my ledstrip turns on. I changed my pin to D5 and my number of pixels to 14.
Succes! ๐
The next step is to make the ledstrip turn on at a certain time.How can I make my ESP know the time? To do this I used the Time library by Michael Margolis. I read the documentation for this library here: https://playground.arduino.cc/Code/Time/
First I needed to set my timezone. You can find your timezone string on this site: https://remotemonitoringsystems.ca/time-zone-abbreviations.php
Then I needed to connect to the internet to get the time from the Network Time Protocol (NTP). To connect you need can use the libraries WiFi/ESP8266WiFi. You need to define you connection SSID and password, and then set up the connection in void setup():
To test the connection I added this function to print the day of the week and current time:
void showTime(tm localTime) {
Serial.print(localTime.tm_mday);
Serial.print('/');
Serial.print(localTime.tm_mon + 1);
Serial.print('/');
Serial.print(localTime.tm_year - 100);
Serial.print('-');
Serial.print(localTime.tm_hour);
Serial.print(':');
Serial.print(localTime.tm_min);
Serial.print(':');
Serial.print(localTime.tm_sec);
Serial.print(" Day of Week ");
if (localTime.tm_wday == 0) Serial.println(7);
else Serial.println(localTime.tm_wday);
}
It printed this info to the Serial Monitor every second:
Great! ๐
Now I have to set this data as conditions for the ledstrip to turn on. I did this by writing an if-statement.
If I want to turn on the lights at 09.00 AM, I should write in the if-statament:
if (localTime.tm_hour == 9) {...}
When I run this code, the lights will go on at the set time and also print out the current time:
if (localTime.tm_hour == 9) {
Serial.print("Time to turn on the lights at: ")
Serial.print(localTime.tm_hour);
Serial.print(':');
Serial.print(localTime.tm_min);
for(int i=0; i<NUM_PIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(255, 255, 255));
pixels.show();
delay(1000);
}
}
(I uploaded the full code to this github repo)
To test the code I put in the current hour and minute like this:
if (localTime.tm_hour == 14 && localTime.tm_min == 32)
Do the lights go on at the set time?
Yes they do! ๐
We can now set a time for our ledstrip to turn on, just like a real alarm clock. ๐ But if we want to make this even more interesting, we should use data from the NS Reisinformatie API.
To use this API you need to create a free account at https://apiportal.ns.nl/signin and subscribe to their Reisinformatie API. After doing this you can find your API key on your profile page:
The NS API website has a lot of great code examples, but unfortunately none of them are for Arduino/C++.
So I had to look for other sources on how to connect and get data from this API, and started with this manual that explains how to get weather data: https://www.dfrobot.com/blog-917.html. I also watched this video on how to connect to an API using an ESP8266: https://www.youtube.com/watch?v=HUjFMVOpXBM. This one was very helpful but only covered part of what I needed to do.
On the NS API website you can find the connection string for the API you want to connect to. I need to declare this URL and the API key in my code:
const String endpoint = "https://gateway.apiportal.ns.nl/reisinformatie-api/api/v3/disruptions[?type][&isActive]";
const String key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
I will use these endpoint and key variables later in void loop().
Next I needed to do a GET request. This will get me data about the delays/calamities. I used the HttpClient library from Adrian McEwen for this (github: https://github.com/amcewen/HttpClient).
void loop() {
if ((WiFi.status() == WL_CONNECTED)) { //Check the current connection status
HttpClient http;
http.begin(endpoint + key); //Specify the URL
int httpCode = http.GET(); //Make the request
if (httpCode > 0) { //Check for the returning code
String payload = http.getString();
Serial.println(httpCode);
Serial.println(payload);
}
else {
Serial.println("Error on HTTP request");
}
http.end(); //Free the resources
}
I kept getting this error about HttpClient:
I found an answer on the Arduino forum: https://forum.arduino.cc/t/no-matching-function-to-call-for-httpclient/688817
This linked me to the libraries Github page where I could find more information. The error was that I didn't declare a client, this was fixed by including the EthernetClient library and initializing the http client this way:
WiFiClient c;
HttpClient http(c);
But I still had another error with this library:
First I tried deleting and redownloading the library but that didn't do anything. I couldn't figure it out so I decided to use the method of this source: https://randomnerdtutorials.com/esp8266-nodemcu-http-get-post-arduino/ and use these libraries instead:
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecure.h>
#include <Arduino_JSON.h>
This means I used WifiClientSecure instead of EthernetClient & ESP8266HTTPClient instead of HttpClient. After that I didn't get any error messages anymore but I still couldn't GET any data.
I tried to give the URL & API key in the following line:
http.begin(client, endpoint + key);
But something must be wrong with this, because I cannot request anything and get the 'Error on HTTP request' message in the Serial Monitor.
I think I had to change the host URL before trying to GET.
The URL looks like this: https://gateway.apiportal.ns.nl/reisinformatie-api/api/v3/disruptions[?type][&isActive]
But you have to fill in the values for type and isActive, like this: https://gateway.apiportal.ns.nl/reisinformatie-api/api/v3/disruptions?type=calamity&isActive=true
I also added the hosts fingerprint, you can use this with WiFiClientSecure as extra verification.
#define HOST_FINGERPRINT "XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX"
client.setFingerprint(HOST_FINGERPRINT);
Now I have a 401 message instead of 404. Something's been found, but I'm not allowed to see it.
๐จ "401 Access denied due to missing subscription key. Make sure to include subscription key when making requests to an API."
Maybe there's a problem with my API key? Since I'm not sure how API keys work exactly, I did some research:
- According to this source: https://blog.stoplight.io/api-keys-best-practices-to-authenticate-apis, there are multiple ways of using an API key. I think NS uses API headers. The headers usually come after the request line or response line. I need to add a header in my code with the authentication key.
- I found out on Arduino forum (https://forum.arduino.cc/t/esp8266http-authentication/647375) that I can define my key like this:
http.addHeader("Authorization:", "Basic key", true);
The NS API uses a Ocp-Apim-Subscription-Key, so the way I did it was:
http.addHeader("Ocp-Apim-Subscription-Key", key, true);
Now I get access to the data! Finally a 200 message! ๐ ๐ฅณ
I could get data from the API but I didn't have time to use it to change the turn-on time for the ledstrip. I did manage to turn on the light at a set time. This could make a simple prototype for an alarmclock with ledstrip.
- https://playground.arduino.cc/Code/Time/
- https://remotemonitoringsystems.ca/time-zone-abbreviations.php
- https://www.ns.nl/reisinformatie/ns-api
- https://www.dfrobot.com/blog-917.html
- https://www.youtube.com/watch?v=HUjFMVOpXBM
- https://forum.arduino.cc/t/no-matching-function-to-call-for-httpclient/688817
- https://randomnerdtutorials.com/esp8266-nodemcu-http-get-post-arduino/
- https://blog.stoplight.io/api-keys-best-practices-to-authenticate-apis
- https://forum.arduino.cc/t/esp8266http-authentication/647375
- https://learn.microsoft.com/nl-nl/azure/cognitive-services/translator/translator-text-apis?tabs=csharp