Page 258

PWM colours breadboard layout

Page 259

const int PWMA = 4; const int AIN2 = 3; const int AIN1 = 2; const int STBY = 1; const int INTPIN = 0; int motorASpeed = 255; //volatile bool stopA = false; void setup() { Serial.begin(115200); delay(1000); pinMode(PWMA, OUTPUT); pinMode(AIN2, OUTPUT); pinMode(AIN1, OUTPUT); pinMode(STBY, OUTPUT); pinMode(INTPIN, INPUT_PULLUP); //attachInterrupt(digitalPinToInterrupt(INTPIN), setStopA, LOW); test1(); } void motorAForward() { Serial.println("Forward"); analogWrite(PWMA, motorASpeed); digitalWrite(AIN1, HIGH); digitalWrite(AIN2, LOW); digitalWrite(STBY, HIGH); } void motorABrake() { Serial.println("Brake"); digitalWrite(AIN1, HIGH); digitalWrite(AIN2, HIGH); analogWrite(PWMA, 0); } void motorAStop() { Serial.println("Stop"); digitalWrite(AIN1, LOW); digitalWrite(AIN2, LOW); analogWrite(PWMA, 0); digitalWrite(STBY, LOW); // would also stop motor B } void motorABack() { Serial.println("Backward"); analogWrite(PWMA, motorASpeed); digitalWrite(AIN1, LOW); digitalWrite(AIN2, HIGH); digitalWrite(STBY, HIGH); }

Page 260

void test1() { motorAForward(); delay(2000); motorABrake(); delay(500); motorAStop(); delay(1000); motorABack(); delay(2000); motorABrake(); delay(500); motorAStop(); } void setStopA() { stopA = true; } void loop() { if(stopA) {     motorAStop();     Serial.println("Limiter Stop");     stopA = false; } }

Page 261

#include <WiFiNINA.h> template<class T> inline Print &operator<<(Print &obj, T arg) { obj.print(arg); return obj;} void setup() { Serial.begin(115200); while (!Serial) {} Serial.println("Scanning available WiFi access..."); listWiFi(); } void listWiFi() { int wifiCount = WiFi.scanNetworks(); for(int i = 0; i < wifiCount; i++) {     Serial << i << ' ' << "SSID: " << WiFi.SSID(i) << '\n'; } if(wifiCount < 1) {     Serial.println("No WiFi services found"); } } void loop() { }

Page 262

#include <WiFiNINA.h> #include "Secret.h" const char* mySSID = SECRET_SSID; const char* myPass = SECRET_PASSWORD; template<class T> inline Print &operator<<(Print &obj, T arg) { obj.print(arg); return obj;} void setup() { Serial.begin(115200); while (!Serial) {} Serial.println("Connecting to WiFi..."); connectToWiFi(); } void connectToWiFi() { while (WiFi.begin(mySSID, myPass) != WL_CONNECTED) {     delay(500); } Serial.println("WiFi connected"); Serial << "Local IP: " << WiFi.localIP() << '\n'; }
Serial << "Signal strength: " << WiFi.RSSI() << '\n';      byte macAdd[6];      WiFi.macAddress(macAdd);      Serial.print("MAC Address: ");      showMACAddress(macAdd);

void showMACAddress(byte add[]) { for(int i = 5; i >= 0; i--) {     if(add[i] < 16) {Serial.print('0');} // padd hex     Serial.print(add[i], HEX);     if(i > 0) {      Serial.print(':');     } } Serial.println(); }

Page 263

unsigned int localPort = 2390;        // local port to listen for packets IPAddress timeServer(143,210,16,201); // 0.uk.pool.ntp.org note commas const int NTP_PACKET_SIZE = 48; // NTP time stamp in the first 48 bytes byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold packets const int GMT_ADJUST = 1 * 60 * 60; //time zone adjustment (BST) unsigned long epoch; void getCurrentTime() { int pingCount = 0, maxPings = 6; do {     epoch = getLinuxEpoch();     pingCount++; } while ((epoch == 0) && (pingCount <= maxPings)); if (pingCount > maxPings) {     Serial.println("NTP did not respond"); } else {     rtc.setEpoch(epoch + GMT_ADJUST);     Serial << "Time value: " << rtc.getEpoch() << '\n'; } }

Page 264

RTCZero rtc; WiFiUDP Udp; void setup() { Serial.begin(115200); while (!Serial) {} Serial.println("Connecting to WiFi..."); connectToWiFi(); rtc.begin(); // start the clock getCurrentTime(); showTime(); }

unsigned long getLinuxEpoch() { Udp.begin(localPort); sendNTPPacket(timeServer); // send an NTP packet to NTP server // wait a bit delay(1000); if ( Udp.parsePacket() ) {     Serial.println("NTP time received");     // read data into buffer     Udp.read(packetBuffer, NTP_PACKET_SIZE); // read 48 bytes          unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);     unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);     // combine the four bytes into a long integer     // should be seconds since Jan 1 1900     unsigned long ntpTime = highWord << 16 | lowWord;     Udp.stop();     // now adjust NTP time to Linux time which starts 70 years later     const unsigned long seventyYears = 2208988800UL;     return (ntpTime - seventyYears); } else {     Udp.stop();     return 0; } }

Page 265

void showTime() { Serial << rtc.getDay() << '/' << rtc.getMonth() << "/20" << rtc.getYear() << "    " << rtc.getHours() << ':' << rtc.getMinutes() << ':' << rtc.getSeconds() << '\n'; }

volatile unsigned long rtcEpoch = 0; unsigned long lastEpoch = 0;

void setAlarm(){ rtc.setAlarmSeconds(5); rtc.attachInterrupt(alarmCall); rtc.enableAlarm(rtc.MATCH_SS); // match the alarm time every minute } void alarmCall() { rtcEpoch = rtc.getEpoch(); }

void loop() { if(rtcEpoch != lastEpoch) {     lastEpoch = rtcEpoch;     showTime(); } }

Page 266

#define BUFF_SIZE 81 #include <WiFiNINA.h> #include "Secret.h" const char* mySSID = SECRET_SSID; const char* myPass = SECRET_PASSWORD; const char pageRequest[] = "GET / HTTP/1.1"; WiFiServer server(80); // port 80 char buffer[BUFF_SIZE]; const long timeOut = 1000; unsigned long waitStart; int pageCount = 0; WiFiClient client; template<class T> inline Print &operator<<(Print &obj, T arg) { obj.print(arg); return obj;} void setup() { Serial.begin(115200); while (!Serial) {} connectToWiFi(); server.begin(); // start the server }

Page 267

void connectToWiFi() { while (WiFi.begin(mySSID, myPass) != WL_CONNECTED) {     delay(500); } Serial << "WiFi connected\n"; Serial << "Local IP: " << WiFi.localIP() << '\n'; } void loop() { client = server.available(); if(client) {     int charCount = clearBuffer();     Serial << "Browser connected\n";     waitStart = millis();     while (client.connected()) {      if(client.available()) {         char c = client.read();         Serial << c;         if(c == '\n' || c == '\r'){          if(strncmp(pageRequest, buffer, 14) == 0) {             sendHTML();             break;          } else {             charCount = clearBuffer();             waitStart = millis();          }         } else {          if(charCount < 79) {             buffer[charCount] = c;             charCount++;          }         }     } else {      // trigger timeout when browser silent      if((unsigned long)millis() - waitStart >= timeOut) {         break;      }     } } client.flush(); client.stop(); Serial << "Browser disconnected\n"; } }

Page 268

int clearBuffer() { memset(buffer, 0, BUFF_SIZE); return 0; } void sendHTML() { Serial << "\nSending HTML\n"; // HTTP preamble first client << "HTTP/1.1 200 OK\n"; client << "Content-Type: text/html\n"; client << "Connection: close\n\n"; // extra linefeed important // now the HTML forming the web page client << "<!DOCTYPE HTML>\n"; client << "<html>\n"; client << "Hello from Arduino MKR 1010 Server<br />\n"; pageCount++; client << "Page served Counter: " << pageCount << '\n'; client << "</html>\n"; }

Page 269

client << "<input type=\"button\" onclick=\"location.href=\'/A\';\" value=\"Click Me\" />\n";

Page 270

const int PWMA = 4; const int AIN2 = 3; const int AIN1 = 2; const int STBY = 1; byte motorSpeed = 180; void setMotorPins() { pinMode(PWMA, OUTPUT); pinMode(AIN2, OUTPUT); pinMode(AIN1, OUTPUT); pinMode(STBY, OUTPUT); }

Page 271

void motorOpen() { analogWrite(PWMA, motorSpeed); digitalWrite(AIN1, HIGH); digitalWrite(AIN2, LOW); digitalWrite(STBY, HIGH); } void motorBrake() { digitalWrite(AIN1, HIGH); digitalWrite(AIN2, HIGH); analogWrite(PWMA, 0); } void motorStop() { digitalWrite(AIN1, LOW); digitalWrite(AIN2, LOW); analogWrite(PWMA, 0); digitalWrite(STBY, LOW); } void motorClose() { analogWrite(PWMA, motorSpeed); digitalWrite(AIN1, LOW); digitalWrite(AIN2, HIGH); digitalWrite(STBY, HIGH); }

const int LIMIT_CLOSED = 0; const int LIMIT_OPEN = 16; // Pin A1 enum SwitchStates: int8_t {OPEN, CLOSED}; // switch open or closed byte closedLimitState = OPEN; volatile byte newClosedState = OPEN; byte openLimitState = OPEN; volatile byte newOpenState = OPEN;

Page 272

void setupLimitSwitches() { pinMode(LIMIT_CLOSED, INPUT_PULLUP); pinMode(LIMIT_OPEN, INPUT_PULLUP); attachInterrupt(LIMIT_CLOSED, setClosedLimit, CHANGE); attachInterrupt(LIMIT_OPEN, setOpenLimit, CHANGE); initLimitStates(); } void setClosedLimit() { newClosedState = (digitalRead(LIMIT_CLOSED) == LOW) ? CLOSED : OPEN; } void setOpenLimit() { newOpenState = (digitalRead(LIMIT_OPEN) == LOW) ? CLOSED : OPEN; } void initLimitStates() { newClosedState = closedLimitState =     (digitalRead(LIMIT_CLOSED) == LOW) ? CLOSED : OPEN; newOpenState = openLimitState =     (digitalRead(LIMIT_OPEN) == LOW) ? CLOSED : OPEN; if(closedLimitState == CLOSED) {     skylightState = FULLY_CLOSED; } else if(openLimitState == CLOSED) {     skylightState = FULLY_OPEN; } else {     skylightState = PART_OPEN; // as it can't be moving yet } }

Page 273

void checkLimitStates() {     if(newOpenState != openLimitState) {     if(newOpenState == CLOSED) {      motorStop();      skylightState = FULLY_OPEN;      if(Serial) {         Serial << "Stopped in fully open position\n";      }     }     openLimitState = newOpenState; } if(newClosedState != closedLimitState) {     if(newClosedState == CLOSED) {      motorStop();      skylightState = FULLY_CLOSED;      if(Serial) {         Serial << "Stopped in fully closed position\n";      }     }     closedLimitState = newClosedState; } }

void clearBuffer() { memset(buffer, 0, BUFF_SIZE); charCount = 0; } void sendHTML() { client << "HTTP/1.1 200 OK\n"; client << "Content-Type: text/html\n"; if(skylightState == OPENING || skylightState == CLOSING) {     client << "Refresh: 3\n"; // ask for refresh at 3 second intervals } client << "Connection: close\n\n"; // extra linefeed important

Page 274

client << "<!DOCTYPE HTML>\n"; client << "<html>\n<head>\n"; client << "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"; client << "<title>Skylight Automation</title>\n</head>\n"; client << "<body><div style=\"width:100%; height: 100%; "; client << "background-color: antiquewhite;\">\n"; client << "<div style=\"text-align: center; border: 3px solid blue;\">\n"; client << "<h1>Automated Skylight</h1><br />\n"; client << "Your skylight is currently: "; client << stateText[skylightState]; client << "<br /><br />\n";

client << "<input type=\"button\" value=\"Open\" onclick=\"location.href=\'/O';\" "; if(skylightState != FULLY_CLOSED && skylightState != PART_OPEN) { client << " disabled "; } client << " />\n"; client << "<input type=\"button\" value=\"Close\" onclick=\"location.href=\'/C';\" "; if(skylightState != FULLY_OPEN && skylightState != PART_OPEN) { client << " disabled "; } client << " />\n"; client << "<input type=\"button\" value=\"Stop\" onclick=\"location.href=\'/S';\" "; if(skylightState != OPENING && skylightState != CLOSING) { client << " disabled "; } client << " />\n"; client << "<br /><br /></div></div></body></html>\n"; }

Page 275

#define BUFF_SIZE 81 #define BUFF_LIMIT 80 #include <WiFiNINA.h> #include "Secret.h" char buffer[BUFF_SIZE]; const long timeOut = 500; unsigned long waitStart; int charCount; enum SkylightStates: int8_t {FULLY_OPEN, FULLY_CLOSED, PART_OPEN, OPENING, CLOSING}; char stateText[][13] = {"fully open", "fully closed", "part open", "opening", "closing"}; byte skylightState; const char* mySSID = SECRET_SSID; const char* myPass = SECRET_PASSWORD; const char getRequest[] = "GET /"; WiFiServer server(80); // port 80 is standard WiFiClient client; template<class T> inline Print &operator<<(Print &obj, T arg) { obj.print(arg); return obj;} void setup() { Serial.begin(115200); while (!Serial) {} setMotorPins(); setupLimitSwitches(); connectToWiFi(); server.begin(); // start the server } void connectToWiFi() { while (WiFi.begin(mySSID, myPass) != WL_CONNECTED) {     delay(500); } Serial << "WiFi connected\n"; Serial << "Local IP: " << WiFi.localIP() << '\n'; }

Page 276

void loop() { if(skylightState == OPENING || skylightState == CLOSING) {     checkLimitStates(); // check for interrupts } client = server.available(); if(client) {     waitStart = millis();     clearBuffer();     while (client.connected()) {      if(client.available()) {         char c = client.read();         if(c == '\n' || c == '\r'){          if(strncmp(getRequest, buffer, 5) == 0) {             switch(buffer[5]) {              case ' ': // initial get request                 sendHTML();                 break;              case 'O': // open button clicked              if(skylightState != OPENING && skylightState != FULLY_OPEN){                  skylightState = OPENING;                  motorOpen();                 }                 sendHTML();                 break;              case 'C': // close button clicked                 if(skylightState != CLOSING && skylightState !=                  FULLY_CLOSED) {                  skylightState = CLOSING;                  motorClose();                 }                 sendHTML();                 break;              case 'S': // stop button                 motorStop();                 skylightState = PART_OPEN; // probably                 checkLimitStates(); //to make sure                 sendHTML();                 break;             }          }          break; // escape while loop to close connection         } else {          if(charCount <= BUFF_LIMIT) {             buffer[charCount] = c;             charCount++;          }         }      } else {         if((unsigned long)millis() - waitStart >= timeOut) {break;}      }      if(skylightState == OPENING || skylightState == CLOSING) {         checkLimitStates();      }     }     client.flush();     client.stop(); } }

Page 277

PWM colours breadboard layout

Page 279

#include <WiFiNINA.h> #include "Secret.h" const char* mySSID = SECRET_SSID; const char* myPass = SECRET_PASSWORD; template<class T> inline Print &operator<<(Print &obj, T arg) { obj.print(arg); return obj;} void setup() { Serial.begin(115200); while (!Serial) {} connectToWiFi(); connectToResource(); } #include <WiFiNINA.h> #include "Secret.h" const char* mySSID = SECRET_SSID; const char* myPass = SECRET_PASSWORD; template<class T> inline Print &operator<<(Print &obj, T arg) { obj.print(arg); return obj;} void setup() { Serial.begin(115200); while (!Serial) {} connectToWiFi(); connectToResource(); } void connectToResource() { WiFiClient client; const char* host = "www.arduino.cc"; const int httpPort = 443; char* uri = "/reference/en/language/variables/data-types/stringobject/"; if (!client.connectSSL(host, httpPort)) {      Serial << "connection failed\n";      return; } client << "GET " << uri << " HTTP/1.1\r\n"; client << "Host: " << host << "\r\n"; client << "Connection: close\r\n\r\n"; // two line terminators unsigned long timeOut = millis(); while(!client.available()) {     if((unsigned long) millis() - timeOut > 5000) {      Serial << "Client timeout\n";     } } while(client.available() ) {     char c = client.read();     Serial << c; } Serial << "\nThats All Folks\n"; }

Page 280

PWM colours breadboard layout

My final program

If you are interested in my final program and the detail of my chosen service (warts and all) then you can click here for a download.
You would have to sign up for your own API key and (of course) add your own Secret.h.