The home automation systems allow to control electronic devices remotely and to perform automatic routines based on the user's programming preferences, making domestic environments more convenient, comfortable, and efficient, also optimizing energy costs.
Home automation applications allow, for example, smart lights management and HVAC control. Thanks to its industrial IoT capabilities, Opta™️ is an excellent product for home automation applications.
This application note shows a home automation system based on an Opta™️, capable of controlling a roller window shade, also known as roller blind, based on a programmed scenario. The application note's goals are to:
Implement the roller window shade control (opening and closing) upon a programmed scenario
Get inputs from limit switches included in the roller window shade's control system, for feedback and security reinforcement
Control and monitor the roller window shade via Arduino IoT Cloud
Opta™️ PLC with wireless connectivity support (x1)
USB-C® cable (x1)
12V/30 RPM DC motor (x1)
Full-bridge motor driver (x1)
A 3D printed case for the DC motor and its driver (.stl files of the case can be downloaded here)
Normally Open (NO) Single Pole Double Throw (SPDT) limit switch (x2)
24AWG twisted-pair cable (for electrical connections)
12V/2A DC power supply (x1)
WiFi
and NTPClient
The following diagram shows the electrical connections of the intended application:
The Opta™️ controls a DC motor via a driver using the digital output terminals
D0
and D1
; two NO SPDT limit switches are connected to digital input terminals I3
and I4
of the Opta™️. A 12V DC/2A power supply is used to power the Opta™️, the DC motor, and the rest of the application.Note: The maximum voltage of Opta™️ digital input pins is 24 VDC.
The first task of the program is to create the connection between the Opta™️ and the predefined Wi-Fi® network by the user in the Arduino IoT Cloud; when the Wi-Fi® connection is established, the internal real-time clock (RTC) of the Opta™️ will be synced using a Network Time Protocol (NTP) server. Opta™️ RTC will be synced with the NTP server periodically (in this example, every 30 minutes) for security and reliability reasons.
The NTP is a protocol used to synchronize the clocks of servers and clients across the Internet; it is intended to synchronize computers participating in the network within a few milliseconds of Coordinated Universal Time (UTC). You can read more about the NTP protocol here.
After the RTC is synced, Opta™️ will be programmed for opening and closing the roller window shades using the DC motor, at specific hours every day:
Hereafter the sketch explained. Note that this code must be updated considering the proper parameters of your roller window shade and DC motor characteristics.
The first part of the sketch includes some definitions needed to:
1// Libraries used in the sketch2#include <WiFi.h>3#include <NTPClient.h>4#include <mbed_mktime.h>5
6// Wi-Fi network credentials (replace with your network credentials)7char ssid[] = "YOUR_WIFI_SSID"; 8char pass[] = "YOUR_WIFI_PASSWORD";9int status = WL_IDLE_STATUS;10
11// NTP client configuration and RTC update interval12WiFiUDP ntpUDP;13NTPClient timeClient(ntpUDP, "pool.ntp.org", -6*3600, 0);14unsigned long interval = 60*30*1000UL;15unsigned long lastTime = 0;16
17// Programmed scenarios18int local_hour;19int local_minutes;20int programmed_hour_1 = 6;21int programmed_hour_2 = 18;22int programmed_minutes_1 = 0;23int programmed_minutes_2 = 0;
For Wi-Fi® connectivity, we will use
library.
For the connection to an NTP server and to retrieve time information from it, we will use WiFi
.
We are going to use specific RTC management methods from Mbed™️ to handle the internal RTC of the Opta™️ microcontroller. More information regarding those methods can be found here.NTPClient
From the code shown above, there are two important lines:
1WiFiUDP ntpUDP;2NTPClient timeClient(ntpUDP, "pool.ntp.org", -6*3600, 0);
The parameters of the
timeClient
object are as follows:pool.ntp.org
.These are some time offset examples to use for different timezones:
3600
28800
-3600
0
The variables
programmed_hour_1
, programmed_hour_2
, programmed_minutes_1
, and programmed_minutes_2
are used to open or close the roller window shade at two specific times of the day. They can be changed according to your needs. The Opta™️ setup is shown as follows:
1// Opta initialization2void setup() {3 Serial.begin(9600);4 while (!Serial) {5 ;6 }7 delay(5000);8
9 // Attempt Wi-Fi connection10 while (status != WL_CONNECTED) {11 Serial.print("- Attempting to connect to WPA SSID: ");12 Serial.println(ssid);13 status = WiFi.begin(ssid, pass);14 delay(500);15 }16 17 // Display Wi-Fi network information18 Serial.println();19 Serial.println("- NETWORK INFORMATION");20 Serial.print("- You're now connected to the network ");21 printCurrentNet();22 printWifiData();23 delay(5000);24
25 // NTP client object initialization and time update, display updated time on the Serial Monitor26 timeClient.begin();27 updateTime();28 29 // Digital inputs, digital outputs, built-in LEDs initialization30 pinMode(LED_D0, OUTPUT);31 pinMode(LED_D1, OUTPUT);32 pinMode(DO, OUTPUT);33 pinMode(D1, OUTPUT);34 pinMode(A2, INPUT);35 pinMode(A3, INPUT);36
37 // Stop the roller window shade from moving38 digitalWrite(LED_D0, LOW);39 digitalWrite(LED_D1, LOW);40 stop_shade();41}
There are five essential steps in the setup function:
The serial port is initialized to
9600
bauds; it will be used in the application for debugging purposes.A Wi-Fi® connection is attempted; the application will not start unless the Wi-Fi® connection is established successfully.
After establishing a Wi-Fi® connection, the connected network information is displayed using the
printCurrentNet()
and printWifiData()
functions in the Arduino IDE Serial Monitor. This information will be used for debugging purposes.The NTP client is initialized, and Opta™️ PLC's RTC is aligned with the updated time retrieved from the NTP server.
Two digital inputs, two digital outputs, and two built-in LEDs of Opta™️ are initialized, the roller window shade is stopped using the
stop_shade()
function. The built-in LEDs of Opta™️ are used in the application as visual feedback for user feedback and debugging purposes.The main loop shown below:
1void loop() {2 // Check and verify if it's time to move up or down the roller window shade3
4 local_hour = getLocalHour();5 local_minutes = getLocalMinutes();6
7 if (programmed_hour_1 == local_hour && programmed_minutes_1 == local_minutes) {8 Serial.println("- Rolling down shade!");9 roll_down_shade();10 } else if (programmed_hour_2 == local_hour && programmed_minutes_2 == local_minutes) {11 Serial.println("- Rolling up shade!");12 roll_up_shade();13 } else {14 stop_shade();15 }16
17 // Update time client periodically18 unsigned long currentTime = millis();19 if (currentTime - lastTime >= interval) {20 updateTime();21 lastTime = currentTime;22 }23}
The main loop can be divided into two essential steps:
getLocalHour()
and getLocalMinutes()
functions are used to acquire Opta™'s local time (from its RTC); this is then compared to the programmed scenarios times (in this example, 6:00 AM and 6:00 PM). The roller window shade must be opened or closed if Optas™'s RTC time matches the programmed scenarios.updateTime()
function.The complete example sketch explained before is shown below; pay attention to the
updateTime()
, getLocalTime()
, getLocalHour()
, roll_down_shade()
, roll_up_shade()
, stop_shade()
, printWifiData()
, and printCurrentNet()
functions:1/**2 Home Automation with Opta (Application Note)3 Name: window_shade_roller_opta.ino4 Purpose: Open or close a roller window shade based on a programmed scenario5
6 @author: José Bagur and Taddy Chung7 @version: 1.3 (15/02/23)8*/9
10// Libraries used in the sketch11#include <WiFi.h>12#include <NTPClient.h>13#include <mbed_mktime.h>14
15// Wi-Fi network credentials16char ssid[] = "YOUR_WIFI_SSID"; 17char pass[] = "YOUR_WIFI_PASSWORD";18int status = WL_IDLE_STATUS;19
20// NTP client configuration and RTC update interval21WiFiUDP ntpUDP;22NTPClient timeClient(ntpUDP, "pool.ntp.org", -6*3600, 0);23unsigned long interval = 60*30*1000UL;24unsigned long lastTime = 0;25
26// Programmed scenarios for opening or closing the shade27int local_hour;28int local_minutes;29int programmed_hour_1 = 6;30int programmed_hour_2 = 18;31int programmed_minutes_1 = 0;32int programmed_minutes_2 = 0;33
34bool shade_state = false; 35
36
37// Opta initialization38void setup() {39 Serial.begin(9600);40 while (!Serial) {41 ;42 }43 delay(5000);44
45 // Attempt Wi-Fi connection46 while (status != WL_CONNECTED) {47 Serial.print("- Attempting to connect to WPA SSID: ");48 Serial.println(ssid);49 status = WiFi.begin(ssid, pass);50 delay(500);51 }52 53 // Display Wi-Fi network information54 Serial.println();55 Serial.println("- NETWORK INFORMATION");56 Serial.print("- You're now connected to the network ");57 printCurrentNet();58 printWifiData();59 delay(5000);60
61 // NTP client object initialization and time update, display updated time on the Serial Monitor62 timeClient.begin();63 updateTime();64 65 // Digital inputs, digital outputs, built-in LEDs initialization66 pinMode(LED_D0, OUTPUT);67 pinMode(LED_D1, OUTPUT);68 pinMode(D0, OUTPUT);69 pinMode(D1, OUTPUT);70 pinMode(A2, INPUT);71 pinMode(A3, INPUT);72
73 // Stop the roller window shade from moving74 digitalWrite(LED_D0, LOW);75 digitalWrite(LED_D1, LOW);76 stop_shade();77}78
79void loop() {80 // Check and verify if it's time to move up or down the roller window shade81
82 local_hour = getLocalHour();83 local_minutes = getLocalMinutes();84
85 if (programmed_hour_1 == local_hour && programmed_minutes_1 == local_minutes) {86 Serial.println("- Rolling down shade!");87 roll_down_shade();88 } else if (programmed_hour_2 == local_hour && programmed_minutes_2 == local_minutes) {89 Serial.println("- Rolling up shade!");90 roll_up_shade();91 } else {92 stop_shade();93 }94
95 // Update time client periodically96 unsigned long currentTime = millis();97 if (currentTime - lastTime >= interval) {98 updateTime();99 lastTime = currentTime;100 }101}102
103/**104 Updates Opta's internal RTC using a NTP server105
106 @param none107 @return none108*/109void updateTime() {110 Serial.println();111 Serial.println("- TIME INFORMATION:");112 timeClient.update();113 const unsigned long epoch = timeClient.getEpochTime();114 set_time(epoch);115 Serial.print("- UTC time: ");116 Serial.println(getLocalTime());117 Serial.print("- Unix time: ");118 Serial.println(epoch);119}120
121/**122 Retrieves Opta's RTC time123
124 @param none125 @return Opta's RTC time in hh:mm:ss format126*/127String getLocalTime() {128 char buffer[32];129 tm t;130 _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);131 strftime(buffer, 32, "%k:%M:%S", &t);132 return String(buffer);133}134
135/**136 Retrieves Opta's RTC hour137
138 @param none139 @return Opta's RTC hour (int)140*/141int getLocalHour() {142 int hour;143 tm t;144 _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);145 hour = t.tm_hour;146 return hour;147}148
149/**150 Retrieves Opta's RTC minutes151
152 @param none153 @return Opta's RTC minutes (int)154*/155int getLocalMinutes() {156 int minutes;157 tm t;158 _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);159 minutes = t.tm_min;160 return minutes;161}162
163/**164 Rolls down the shade165
166 @param none167 @return none168*/169void roll_down_shade() {170 while(digitalRead(A2) == LOW) { 171 Serial.println("- ROLLING DOWN SHADE!");172 digitalWrite(D0, HIGH);173 digitalWrite(D1, LOW);174 digitalWrite(LED_D0, HIGH);175 digitalWrite(LED_D1, LOW);176 } 177 178 Serial.println("- STOP SHADE!");179 digitalWrite(LED_D0, LOW);180 digitalWrite(LED_D1, LOW);181 stop_shade();182 shade_state = false; 183}184
185/**186 Rolls up the shade187
188 @param none189 @return none190*/191void roll_up_shade() {192 while(digitalRead(A3) == LOW) { 193 Serial.println("- ROLLING UP SHADE!");194 digitalWrite(D0, LOW);195 digitalWrite(D1, HIGH);196 digitalWrite(LED_D0, LOW);197 digitalWrite(LED_D1, HIGH);198 }199 200 Serial.println("- STOP SHADE!");201 digitalWrite(LED_D0, LOW);202 digitalWrite(LED_D1, LOW);203 stop_shade();204 shade_state = true; 205}206
207/**208 Stops the shade from moving up or down209
210 @param none211 @return none212*/213void stop_shade() {214 digitalWrite(D0, LOW);215 digitalWrite(D1, LOW);216}217
218/**219 Prints local Wi-Fi network IP and MAC address220
221 @param none222 @return none223*/224void printWifiData() {225 // Display network IP226 IPAddress ip = WiFi.localIP();227 Serial.print("- IP address: ");228 Serial.println(ip);229
230 // Display MAC address231 byte mac[6];232 WiFi.macAddress(mac);233 Serial.print("- MAC address: ");234 Serial.print(mac[5], HEX);235 Serial.print(":");236 Serial.print(mac[4], HEX);237 Serial.print(":");238 Serial.print(mac[3], HEX);239 Serial.print(":");240 Serial.print(mac[2], HEX);241 Serial.print(":");242 Serial.print(mac[1], HEX);243 Serial.print(":");244 Serial.println(mac[0], HEX);245}246
247/**248 Prints connected Wi-Fi network SSID, BSSID, RSSI and encryption type249
250 @param none251 @return none252*/253void printCurrentNet() {254 // Display network SSID255 Serial.println(WiFi.SSID());256 257 // Display network BSSID258 byte bssid[6];259 WiFi.BSSID(bssid);260 Serial.print("- BSSID: ");261 Serial.print(bssid[5], HEX);262 Serial.print(":");263 Serial.print(bssid[4], HEX);264 Serial.print(":");265 Serial.print(bssid[3], HEX);266 Serial.print(":");267 Serial.print(bssid[2], HEX);268 Serial.print(":");269 Serial.print(bssid[1], HEX);270 Serial.print(":");271 Serial.println(bssid[0], HEX);272
273 // Display network RSSI274 long rssi = WiFi.RSSI();275 Serial.print("- Signal strength (RSSI): ");276 Serial.println(rssi);277
278 // Display network encryption 279 byte encryption = WiFi.encryptionType();280 Serial.print("- Encryption type: ");281 Serial.println(encryption, HEX);282}
The example code explained before can be modified to connect the roller window shade to the Arduino IoT Cloud, leveraging the wireless connectivity features of the Opta™️; remember that you must have an Arduino IoT Cloud account. With the Arduino IoT Cloud integration, we are going to add more advanced functionalities such as remote programming, actuation and monitoring to the application:
If you are new to the Arduino IoT Cloud, check out this getting started guide.
After associating your Opta™️ device to your Arduino IoT Cloud account, create a thing; in this example, we called the thing "Shade Controller." In the "Shade Controller" thing, create the cloud variables explained below:
Variable:
programmed_hour_1
int
Read & Write
On change
Variable:
programmed_hour_2
int
Read & Write
On change
Variable:
programmed_minutes_1
int
Read & Write
On change
Variable:
programmed_minutes_2
int
Read & Write
On change
Variable:
open_shade
bool
Read & Write
On change
true
.Variable:
close_shade
bool
Read & Write
On change
true
.Variable:
shade_shade
bool
Read & Write
Periodically
true
for open and false
for closed.
We will use a dashboard and widgets to modify the variables explained before. An example dashboard is shown below:
In the dashboard shown above:
Value
-type widgets are used to show the current value of the programmed_hour_1
, programmed_hour_2
, programmed_minutes_1
, and programmed_minutes_2
variables. These widgets can also be used to modify the value of the linked variables.LED
-type widget shows the current status of the shade_state
variable, green when the shade is open and red when the shade is closed.Push button
widgets are used to open or close the shade anytime.The complete example sketch with the Arduino IoT Cloud integration is shown below:
1#include <NTPClient.h>2#include <mbed_mktime.h>3#include "thingProperties.h"4
5// NTP client configuration and RTC update interval6WiFiUDP ntpUDP;7NTPClient timeClient(ntpUDP, "pool.ntp.org", -6 * 3600, 0);8unsigned long interval = 60 * 30 * 1000UL;9unsigned long lastTime = 0;10
11int local_hour;12int local_minutes;13
14void setup() {15 Serial.begin(9600);16 delay(1500);17
18 // Arduino Cloud initialization19 initProperties();20 ArduinoCloud.begin(ArduinoIoTPreferredConnection);21 setDebugMessageLevel(2);22 ArduinoCloud.printDebugInfo();23 24 while (ArduinoCloud.connected() == 0) {25 ArduinoCloud.update();26 Serial.println("- Waiting for connection to Arduino IoT Cloud");27 delay(1000);28 }29
30 // NTP client object initialization and time update, display updated time on the Serial Monitor31 timeClient.begin();32 updateTime();33
34 // Digital inputs, digital outputs, built-in LEDs initialization35 pinMode(LED_D0, OUTPUT);36 pinMode(LED_D1, OUTPUT);37 pinMode(D0, OUTPUT);38 pinMode(D1, OUTPUT);39 pinMode(A0, INPUT);40 pinMode(A1, INPUT);41
42 // Stop the roller window shade from moving43 stop_shade();44 open_shade = false;45 close_shade = false;46 programmed_hour_1 = 6;47 programmed_hour_1 = 18;48 programmed_minutes_1 = 0;49 programmed_minutes_2 = 0;50 51}52
53void loop() {54 // Update Arduino IoT Cloud55 ArduinoCloud.update();56 delay(1000);57 58 // Check and verify if it's time to move up or down the roller window shade59
60 local_hour = getLocalHour();61 local_minutes = getLocalMinutes();62
63 if (programmed_hour_1 == local_hour && programmed_minutes_1 == local_minutes) {64 Serial.println("- Rolling down shade!");65 roll_down_shade();66 } else if (programmed_hour_2 == local_hour && programmed_minutes_2 == local_minutes) {67 Serial.println("- Rolling up shade!");68 roll_up_shade();69 } else {70 stop_shade();71 }72
73 // Update time client periodically74 unsigned long currentTime = millis();75 if (currentTime - lastTime >= interval) {76 updateTime();77 lastTime = currentTime;78 }79}80
81/**82 Updates Opta's internal RTC using a NTP server83
84 @param none85 @return none86*/87void updateTime() {88 Serial.println();89 Serial.println("- TIME INFORMATION:");90 timeClient.update();91 const unsigned long epoch = timeClient.getEpochTime();92 set_time(epoch);93 Serial.print("- UTC time: ");94 Serial.println(getLocalTime());95 Serial.print("- Unix time: ");96 Serial.println(epoch);97}98
99/**100 Retrieves Opta's RTC time101
102 @param none103 @return Opta's RTC time in hh:mm:ss format104*/105String getLocalTime() {106 char buffer[32];107 tm t;108 _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);109 strftime(buffer, 32, "%k:%M:%S", &t);110 return String(buffer);111}112
113/**114 Retrieves Opta's RTC hour115
116 @param none117 @return Opta's RTC hour (int)118*/119int getLocalHour() {120 int hour;121 tm t;122 _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);123 hour = t.tm_hour;124 return hour;125}126
127/**128 Retrieves Opta's RTC minutes129
130 @param none131 @return Opta's RTC minutes (int)132*/133int getLocalMinutes() {134 int minutes;135 tm t;136 _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);137 minutes = t.tm_min;138 return minutes;139}140
141/**142 Rolls down the shade143
144 @param none145 @return none146*/147void roll_down_shade() {148 while(digitalRead(A0) == LOW) { 149 Serial.println("- ROLLING DOWN SHADE!");150 digitalWrite(D0, HIGH);151 digitalWrite(D1, LOW);152 digitalWrite(LED_D0, HIGH);153 digitalWrite(LED_D1, LOW);154 ArduinoCloud.update();155 delay(1000);156 } 157 158 Serial.println("- STOP SHADE!");159 digitalWrite(LED_D0, LOW);160 digitalWrite(LED_D1, LOW);161 stop_shade();162 shade_state = false; 163}164
165/**166 Rolls up the shade167
168 @param none169 @return none170*/171void roll_up_shade() {172 while(digitalRead(A1) == LOW) { 173 Serial.println("- ROLLING UP SHADE!");174 digitalWrite(D0, LOW);175 digitalWrite(D1, HIGH);176 digitalWrite(LED_D0, LOW);177 digitalWrite(LED_D1, HIGH);178 ArduinoCloud.update();179 delay(1000);180 }181 182 Serial.println("- STOP SHADE!");183 digitalWrite(LED_D0, LOW);184 digitalWrite(LED_D1, LOW);185 stop_shade();186 shade_state = true; 187}188
189/**190 Stops the shade from moving up or down191
192 @param none193 @return none194*/195void stop_shade() {196 digitalWrite(LED_D0, LOW);197 digitalWrite(LED_D1, LOW);198 digitalWrite(D0, LOW);199 digitalWrite(D1, LOW);200}201
202/*203 Since ProgrammedHour1 is READ_WRITE variable, onProgrammedHour1Change() is204 executed every time a new value is received from IoT Cloud.205*/206void onProgrammedHour1Change() {207 Serial.println(programmed_hour_1);208}209
210/*211 Since ProgrammedHour2 is READ_WRITE variable, onProgrammedHour2Change() is212 executed every time a new value is received from IoT Cloud.213*/214void onProgrammedHour2Change() {215 Serial.println(programmed_hour_2);216}217
218
219/*220 Since ProgrammedMinutes1 is READ_WRITE variable, onProgrammedMinutes1Change() is221 executed every time a new value is received from IoT Cloud.222*/223void onProgrammedMinutes1Change() {224 Serial.println(programmed_minutes_1);225}226
227/*228 Since ProgrammedMinutes2 is READ_WRITE variable, onProgrammedMinutes2Change() is229 executed every time a new value is received from IoT Cloud.230*/231void onProgrammedMinutes2Change() {232 Serial.println(programmed_minutes_2);233}234/*235 Since CloseShade is READ_WRITE variable, onCloseShadeChange() is236 executed every time a new value is received from IoT Cloud.237*/238void onCloseShadeChange() {239 if (close_shade == true) {240 Serial.println("CAMBIO CLOSE SHADE TRUE");241 close_shade = false;242 roll_down_shade();243 }244}245/*246 Since OpenShade is READ_WRITE variable, onOpenShadeChange() is247 executed every time a new value is received from IoT Cloud.248*/249void onOpenShadeChange() {250 if (open_shade == true) {251 Serial.println("CAMBIO OPEN SHADE TRUE");252 open_shade = false;253 roll_up_shade();254 }255}256/*257 Since ShadeState is READ_WRITE variable, onShadeStateChange() is258 executed every time a new value is received from IoT Cloud.259*/260void onShadeStateChange() {261 Serial.println(shade_state);262}
This application note shows how to implement an effective home automation system that can control roller window shades with Opta™️. By leveraging the internal RTC of the Opta™️ and NTP server time data, the proposed system can accurately and automatically control the shades based on pre-programmed scenarios. Additionally, the proposed system can be easily monitored and controlled through the Arduino IoT Cloud, providing users with remote management capabilities.