/ledlight-alarm

How to turn on a ledstrip based on time (or using a public transport api) :alarm_clock:

Primary LanguageC++

Ledlight alarm clock ⏰

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

What do you need for this project?

  • NodeMCU esp8266 board
  • RGB LED strip
  • Arduino IDE & Wifi (not 5G)

1. Turning on the ledstrip 💡

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! 🎉

2. Setting a turn-on time 🕘

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/

Timezone

First I needed to set my timezone. You can find your timezone string on this site: https://remotemonitoringsystems.ca/time-zone-abbreviations.php

Internet connection

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():

Print localTime

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)

Test

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! 🎉

3. NS Reisinformatie API. 🚄

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.

3.1 - Create your NS API account

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.

3.2 - Get the connection string & API key

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().

3.3 - Do a GET request

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
  }

Error 🚩

I kept getting this error about HttpClient:

🚨 No matching function for call to 'HttpClient::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);

Error 🚩

But I still had another error with this library:

🚨 'class HttpClient' has no member named 'begin'

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.

Changing my URL

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);

You can find the fingerprint of a page by clicking on the lock in the address bar and then clicking certificate.

Error 🚩

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."

Authentication & headers

Maybe there's a problem with my API key? Since I'm not sure how API keys work exactly, I did some research:

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! 🎉 🥳

Conclusion

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.

Sources 🗃️