tszebeni
Published © MIT

On Air Sign with Oplà kit #CloudGames2022

To let family members know when a call is in progress during home office. Arduino IOT Cloud Thing to Thing communication

IntermediateProtip554
On Air Sign with Oplà kit #CloudGames2022

Things used in this project

Hardware components

LED (generic)
LED (generic)
×1
Through Hole Resistor, 220 kohm
Through Hole Resistor, 220 kohm
×1
Wire, Hook Up
Wire, Hook Up
×1
ESP8266 ESP-01
Espressif ESP8266 ESP-01
×1
Arduino Oplà IoT Kit
Arduino Oplà IoT Kit
×1

Software apps and online services

Arduino IoT Cloud
Arduino IoT Cloud
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

On Air sign

On Air sign box

Schematics

On Air sign

Code

thingProperties.h

Arduino
ESP8266 code for on Air sign
// Code generated by Arduino IoT Cloud, DO NOT EDIT.

#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>

const char DEVICE_LOGIN_NAME[]  = "71c3db28-b3d0-4576-8d60-083190dc4e6b";

const char SSID[]               = "<Your SSID>";    // Network SSID (name)
const char PASS[]               = "<Your PASS>";    // Network password (use for WPA, or use as key for WEP)
const char DEVICE_KEY[]  = "<DEVICE KEY>";    // Secret device password

void onOnAirSignChange();

bool onAirSign;

void initProperties(){

  ArduinoCloud.setBoardId(DEVICE_LOGIN_NAME);
  ArduinoCloud.setSecretDeviceKey(DEVICE_KEY);

  ArduinoCloud.addProperty(onAirSign, READWRITE, ON_CHANGE, onOnAirSignChange);

}

WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);

esp-onair.ino

Arduino
ESP8266 code for on Air sign
#include <ESP8266WiFi.h>
#include "thingProperties.h"

int pin = 2;

// in my case my router had DHCP issues for this particular ESP01 so I decided to assign ip statically, in your case you may not need this block with call WiFi.config
IPAddress local_IP(192, 168, 150, 5);
IPAddress gateway(192, 168, 150, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);   //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional

void setup() {
  Serial.begin(115200);
  if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
    Serial.println("STA Failed to configure");
  }

  
  pinMode(pin, OUTPUT);

  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
 */
  setDebugMessageLevel(4);
  ArduinoCloud.printDebugInfo();
}


void loop() {
  ArduinoCloud.update();
  // Your code here 
  delay(1000);
}



/*
  Since OnAirSign is READ_WRITE variable, onOnAirSignChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onOnAirSignChange()  {
  digitalWrite(pin, onAirSign?HIGH:LOW);
}

Opla.ino

Arduino
#include <SPI.h>
#include <WiFiNINA.h>
#include <ArduinoHttpClient.h>
#include "thingProperties.h"
#include <Arduino_MKRIoTCarrier.h>
MKRIoTCarrier carrier;

struct Event {
  String from;
  String to;
  String title;
};

char server[] = "calendar.google.com";
String feed;
WiFiClient client;
int port = 443;

bool firstFetch = true;
int tick = 0;

Event events[20];
int event_index = -1;
int event_length = event_index + 1;

CloudTime currentTime;

uint32_t colorRed = carrier.leds.Color(0, 200, 0);
uint32_t colorGreen = carrier.leds.Color(200, 0, 0);
uint32_t colorBlue = carrier.leds.Color(0, 0, 200);

bool buttonsPressed = true;
int actual = 0;

Event nomore {
  "00:00",
  "23:59",
  "No more meeting"
};


void loading() {
  carrier.display.fillScreen(0x0000);
  carrier.display.setRotation(0);
  carrier.display.setTextWrap(true);
  carrier.display.setTextColor(0xFFFF);
  carrier.display.setTextSize(3);
  carrier.display.setCursor(35, 100);
  carrier.display.print("Loading");
  for (int i = 0; i < 3; i++)
  {
    carrier.display.print(".");
    delay(1000);
  }
}

void setup() {
  Serial.begin(9600);
  delay(1500); 

  initProperties();

  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();
  
   while (ArduinoCloud.connected() != 1) {
    ArduinoCloud.update();
    delay(500);
  }
 
  delay(500);
  CARRIER_CASE = true;
  carrier.begin();
  carrier.display.setRotation(0);
  events[0] = nomore;
  event_length = 1;
  loading();
}


void sendRequestICalendarFeed() {
  if (client.connectSSL(server, port)) {
    String get("GET /calendar/ical/");
    get += SECRET_ICALENDAR;
    get += "/basic.ics HTTP/1.1";
    client.println(get);
    client.println("Host: calendar.google.com");
    client.println("Connection: close");
    client.println();
    Serial.println("Request sent.");
  }
}
time_t parseTime(char const * time)
{
  int month, day, year;
  struct tm t =
  {
    0 /* tm_sec   */,
    0 /* tm_min   */,
    0 /* tm_hour  */,
    0 /* tm_mday  */,
    0 /* tm_mon   */,
    0 /* tm_year  */,
    0 /* tm_wday  */,
    0 /* tm_yday  */,
    0 /* tm_isdst */
  };
  sscanf(time, "%4d%2d%2dT*", &year, &month, &day);
  t.tm_mon = month - 1;
  t.tm_mday = day;
  t.tm_year = year - 1900;
  t.tm_isdst = -1;
  t.tm_hour = 12;

  return mktime(&t);
}

struct tm extractTime(char const * time)
{
  int month, day, year, hour, minutes;
  struct tm t =
  {
    0 /* tm_sec   */,
    0 /* tm_min   */,
    0 /* tm_hour  */,
    0 /* tm_mday  */,
    0 /* tm_mon   */,
    0 /* tm_year  */,
    0 /* tm_wday  */,
    0 /* tm_yday  */,
    0 /* tm_isdst */
  };
  sscanf(time, "%4d%2d%2dT%2d%2d*", &year, &month, &day, &hour, &minutes);
  t.tm_mon = month - 1;
  t.tm_mday = day;
  t.tm_year = year - 1900;
  t.tm_isdst = -1;
  t.tm_hour = hour;
  t.tm_min = minutes;

  return t;
}

bool is_today(char const* timeStr, unsigned long localTime) {
  unsigned long t = parseTime(timeStr);
  bool same = abs((long)(t - localTime)) < 43200;
  return same;
}

void load(WiFiClient client) {
  String line;
  String buffer = "";
  String key;
  String value;
  bool insideEvent = false;
  bool today = false;
  int idx = -1;
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "0\r") {
      break;
    }
    if (line.charAt(0) == ' ') {
        // collecting multiline prop
        if (buffer.length() < 100)
          buffer += line.substring(1);
    } else {
      if (buffer != "") {
          // process previous prop
          auto m = buffer.indexOf(':');
          if (m == -1)
            m = buffer.indexOf(';');
          key = buffer.substring(0, m);
          value = buffer.substring(m + 1);
          if (key.compareTo("BEGIN") == 0 && value.compareTo("VEVENT\r") == 0) {
            if (idx < 19){
              idx++;
            }
            insideEvent = true;
          } else if (key.compareTo("END") == 0 && value.compareTo("VEVENT\r") == 0) {
            insideEvent = false;
            if (!is_today(events[idx].from.c_str(), ArduinoCloud.getLocalTime())) {
              idx--;
            }
          } else if (key.compareTo("DTSTART") == 0 && insideEvent) {
            events[idx].from = value;
          } else if (key.compareTo("DTEND") == 0 && insideEvent) {
            events[idx].to = value;
          } else if (key.compareTo("SUMMARY") == 0 && insideEvent) {
            events[idx].title = value;
          }
          buffer = "";
      }
      buffer = line;
    }
  }
  event_length = idx + 1;
  if (!client.connected()) {
    client.stop();
  }
}

void readICalendarFeed() {
  load(client);
  firstFetch = false;
}


void prev(){
  actual--;
  if (actual<0) {
    actual = event_length - 1;
  }
}

void next(){
  actual++;
  if (actual >= event_length) {
    actual = 0;
  }
}

void toggle(){
  onAirSignOpla = !onAirSignOpla;
}

String pad(int n) {
  return String((n >=0 && n < 10)?"0":"") + n;
}

void meeting(Event events[]){
  if (buttonsPressed) {
    carrier.display.fillScreen(0x0000);
    carrier.display.setRotation(0);
    carrier.display.setTextWrap(true);
    if (onAirSignOpla) {
      carrier.display.setTextColor(ST77XX_RED);
    } else {
      carrier.display.setTextColor(ST77XX_WHITE);
    }
    carrier.display.setTextSize(2);
    carrier.display.setCursor(85, 20);
    carrier.display.print("On Air");
  
    carrier.display.setTextColor(ST77XX_ORANGE);
    carrier.display.setCursor(40, 65);
    carrier.display.setTextSize(2);
    struct tm from = extractTime(events[actual].from.c_str());
    struct tm to = extractTime(events[actual].to.c_str());
    carrier.display.print(pad(from.tm_hour) + ":" + pad(from.tm_min) + " - " + pad(to.tm_hour) + ":" + pad(to.tm_min));
    
    carrier.display.setCursor(20, 90);
    carrier.display.setTextColor(ST77XX_GREEN);
    carrier.display.setTextSize(2);
    carrier.display.print(events[actual].title);
  
    carrier.display.setTextColor(ST77XX_WHITE);
    carrier.display.setCursor(40, 180);
    carrier.display.setTextSize(2);
    carrier.display.print("Prev");
    
    
    carrier.display.setCursor(150, 180);
    carrier.display.setTextSize(2);
    carrier.display.print("Next");
  
    carrier.leds.setBrightness(65);
    carrier.leds.setPixelColor(0, colorGreen);
    if (onAirSignOpla) {
      carrier.leds.setPixelColor(2, colorRed);
    } else {
      carrier.leds.setPixelColor(2, colorGreen);
    }
    carrier.leds.setPixelColor(4, colorGreen);
    carrier.leds.show();
    buttonsPressed = false;
  }

  carrier.Buttons.update();
  if (carrier.Buttons.onTouchDown(TOUCH0)) {
    prev();
    buttonsPressed = true;
  }
  if (carrier.Buttons.onTouchDown(TOUCH2)) {
    toggle();
    buttonsPressed = true;
  }
  if (carrier.Buttons.onTouchDown(TOUCH4)) {
    next();
    buttonsPressed = true;
  }
}



void loop() {
  if ((currentTime != 0 && firstFetch) ||(tick%5000 == 20 && !firstFetch)){
    Serial.println("Update meeting feed");
    sendRequestICalendarFeed();
    readICalendarFeed();
  }
  ArduinoCloud.update();
  currentTime = ArduinoCloud.getLocalTime();
  if (events[0].title != nomore.title) {
    meeting(events);
  } else {
    loading();
  }
  delay(100);
  tick++;
  if (tick > 5000) {
    tick-=5000;
  }
}

void onOnAirSignOplaChange()  {
  buttonsPressed = true;
}

OplathingProperties.h

Arduino
thingProperties.h for Opla
// Code generated by Arduino IoT Cloud, DO NOT EDIT.

#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>

const char SSID[]     = SECRET_SSID;    // Network SSID (name)
const char PASS[]     = SECRET_PASS;    // Network password (use for WPA, or use as key for WEP)

void onOnAirSignOplaChange();

bool onAirSignOpla;

void initProperties(){

  ArduinoCloud.addProperty(onAirSignOpla, READWRITE, ON_CHANGE, onOnAirSignOplaChange, 1);

}

WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);

OplathingProperties.h

Arduino
// Code generated by Arduino IoT Cloud, DO NOT EDIT.

#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>

const char SSID[]     = SECRET_SSID;    // Network SSID (name)
const char PASS[]     = SECRET_PASS;    // Network password (use for WPA, or use as key for WEP)

void onOnAirSignOplaChange();

bool onAirSignOpla;

void initProperties(){

  ArduinoCloud.addProperty(onAirSignOpla, READWRITE, ON_CHANGE, onOnAirSignOplaChange, 1);

}

WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);

Credits

tszebeni
0 projects • 0 followers

Comments