There are many light controlling devices in the market already. But most of them require you to deal with electricity (meaning that you have to dismount your current light switch and connect the new device there instead). This, apart from being dangerous, may be sometimes impossible, as when you live in a rented place or if you want to put it in your office at work.
To solve these problems, a non-invasive light controller is the best option. Using a servo it can turn on and off your light switch externally (as if it was you). In that way, there is no need to touch the original electrical switch and cabling.
Besides that, this device has the advantage of being portable, you can just take it out from one light switch and put it into another without problems, for example, every morning you can take it with you to work and control your office light with it (from your office Alexa, cellphone or from the internet).
Some of the additional features included are:
- The possibility to monitor the open/close status of a door (usually light switches are located within 20cm of the door so that you can turn the light on as soon as you enter - so that it's a perfect device to gather that information as well).
- A temperature sensor inside so that you can track the temperature of your room at any time.
- And the power consumption tracker, which allows you to know how much electricity are consuming the lights where you have the device connected.
Here are the instructions on how to use it:
Arduino Code and Interaction with AWS IoTFor the Firmware, it's necessary to include the following libraries:
#include <WiFi101.h> // Wifi
#include <PubSubClient.h> // MQTT
#include <Adafruit_BMP085.h> // Temperature-Altitude Sensor
#include <ArduinoJson.h> // JSON parsing-encoding
#include <Servo.h> // Servo
The process is the following:
- The device will connect to your WiFi network and your MQTT Broker (more on this next) - it follows the PubSubClient library mqtt_basic example.
WiFiClient MKR1000Client;
PubSubClient client(MKR1000Client);
WiFi.begin(ssid, password);
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
- During the setup, the digital input pins (where the magnetic door switch and the momentary push button are connected) have to be started as INPUT_PULLUP, because the connections of the sensors were made to ground. This means that if nothing is connected to the pin, it will read a HIGH status, and when the button or the magnetic switch are closed, they will show a LOW value.
- The servo must be initialized as well (attaching the servo pin to the myservo object). Check the Servo Sweep Example for better understanding on how it works.
int magneticSensorPin = 1;
int pushButtonPin = 0;
int servoPin = 2;
pinMode(magneticSensorPin,INPUT_PULLUP); // Magnetic Sensor
pinMode(pushButtonPin,INPUT_PULLUP); // Push Button
pinMode(LED_BUILTIN, OUTPUT); // Initialize the BUILTIN_LED pin as an output
myservo.attach(servoPin);
- In order to report or receive information from the AWS IoT, it's necessary to setup the MQTT Broker. An excellent example on how to do it is: AWS_IOT_BRIDGE_MOSQUITTO - these instructions made by Amazon allows you to create a middle system between Arduino and AWS IoT. They describe how to do it using a Linux server hosted on Amazon EC2. Nevertheless, the instructions apply to any Linux pc/server/device. I have tested it on a Raspberry PI and it works perfectly as well.
- After the MQTT Broker is set up, it will link three topics to the AWS IoT:
- awsiot_to_localgateway: any message received by AWS IoT from this topic will be forwarded to the local gateway.
- localgateway_to_awsiot: any message received by the local gateway will be forwarded to AWS IoT.
- both_directions: any message received on this topic by one broker will be forwarded to the other broker.
- For the project, the localgateway_to_awsiot is where everything will be published. And the awsiot_to_localgateway is where the device is subscribed.
- As these topics are not related to a device shadow (more on that here), it is necessary to create two rules, one to handle information coming from the local device to forward it to its shadow, and another for the opposite process.
- Rule: localgateway_to_awsiot -> Shadow: (learn how to create a rule here)
- Rule: Shadow -> awsiot_to_localgateway:
- On the device, two functions take care of this process
// for publishing:
void publish2Reported(String label, String statusUpdate)
// for the subscription:
void callback(char* topic, byte* payload, unsigned int length)
void payload2Action(byte* payload, unsigned int length)
- The publish2Reported takes the key and attribute that has been read, and publishes it to the "reported" part of the device shadow. The callback is called every time a new message arrives to the subscription topic, then sends the arrived payload to payload2Action, where it's parsed as JSON format in order to act on it.
- Finally on the loop, the system will continuously monitor the status of the push-button and the magnetic switch, in order to publish a message as soon as they change state. While for the temperature it will be read only every 5 mins and sent to the AWS IoT. The altitude will be read every hour.
void loop() {
// MQTT and WiFi:
if (!client.connected()) {
reconnect();
}
client.loop();
// Check at all times if the push button is pressed
turnOnOffByPushButton();
// Check at all times the status of the door
doorStatusChanged();
// Every 5 mins report the temperature
long now = millis();
if (now - lastMsg1 > 300000) {
lastMsg1 = now;
publish2Reported("temperature", String(bmp.readTemperature()));
}
// Every Hour report the altitude
else if (now - lastMsg2 > 3600000) {
lastMsg2 = now;
publish2Reported("altitude", String(bmp.readAltitude(101500)));
}
flag = 0;
}
Lambda Functions and AlexaThere are three lambda functions required for this project: (check the Repository)
- Custom Skill Lambda: Handles the Alexa custom skill intents - connects to AWS IOT to get the status of the sensors, and connects to the SQL Server (hosted on AWS RDS) to get some useful statistics like the power consumption of the light and the amount of times that your door was open during an specific period.
- Smart Home Skill Lambda: Handles the discovery of the device, the power state controller (on / off) and the temperature reading.
- SQL Writer Lambda: When the AWS IOT is notified about a new sensor reading, it redirects this information to the Lambda, that takes the information and stores it in the SQL Database.
The Custom Skill Lambda uses the Alexa-sdk (to handle the Alexa intents), the aws-sdk (for the connection to AWS IOT) and the MSSQL (for the connection to the database) npm libraries for node js. It has the following intents and utterances:
{
"languageModel": {
"intents": [
{
"name": "AltitudeIntent",
"samples": [
"what is the altitude",
"what is my altitude",
"how high are we",
"what is the current altitude",
"tell me the altitude",
"the altitude"
],
"slots": []
},
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": [
"how does this work"
]
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "DoorOpenCountIntent",
"samples": [
"how many times was my door open {date}",
"how many times was my door open on {date}",
"the amount of times my door was open on {date}",
"the times my door was open on {date}",
"the door open count on {date}",
"the door open count",
"the amount of times that my door has been open during {date}",
"the times my door has been opened {date}"
],
"slots": [
{
"name": "date",
"type": "AMAZON.DATE"
}
]
},
{
"name": "DoorStatusIntent",
"samples": [
"What is the status of the door",
"how is my bedroom door",
"how is my office door",
"how is my room door",
"is my door open",
"is my door close",
"how is my door",
"did i leave the door open",
"did i leave the door close",
"how did i leave the door",
"the status of the door"
],
"slots": []
},
{
"name": "LightBulbs",
"samples": [
"{count} light bulbs",
"{count} bulbs",
"{count} lights",
"I have {count}",
"I have {count} light bulbs",
"now I have {count} light bulbs connected",
"{count} light bulbs connected",
"{count} lights connected"
],
"slots": [
{
"name": "count",
"type": "AMAZON.NUMBER"
}
]
},
{
"name": "PowerConsumptionIntent",
"samples": [
"what is my power consumption",
"what has been my power consumption in the {time}",
"what was my power consumption {time}",
"how much electricity is the light consuming",
"what is my power usage",
"what is my power usage in the {time}",
"what was my power usage {time}",
"what was my power usage in the {time}",
"the power consumption",
"the power consumption in the {time}",
"my power usage",
"the power usage",
"the power usage in {time}",
"the power consumption during {time}",
"the power usage {time}",
"my power usage {time}",
"how much electricity has my smart light consumed in the {time}",
"how much electricity has it consumed",
"how much electricity has it consumed in the {time}",
"how much electricity does it consumes",
"my power consumption",
"during the {time}"
],
"slots": [
{
"name": "time",
"type": "AMAZON.DATE"
}
]
},
{
"name": "TellMeMoreIntent",
"samples": [
"Tell me more",
"tell me about it",
"tell me how to use it",
"how does it work",
"to tell me more"
],
"slots": []
},
{
"name": "TemperatureIntent",
"samples": [
"what is the current temperature",
"what is the temperature in the room",
"what is the temperature in my room",
"what is the temperature",
"to tell me the temperature",
"to tell me the current temperature",
"to tell me the current temperature in the room",
"to tell me the current temperature in my room",
"to give me the current temperature",
"to give me the current temperature in the room",
"to give me the current temperature in my room",
"to give me the temperature",
"the temperature"
],
"slots": []
},
{
"name": "Wattage",
"samples": [
"{number} watts each",
"{number} watts",
"each one has {number} watts",
"each one has {number}"
],
"slots": [
{
"name": "number",
"type": "AMAZON.NUMBER"
}
]
}
],
"invocationName": "smart light"
}
}
Most intents are simply getting the status (read value) of the sensor from the Thing Shadow. While the PowerConsumptionIntent and the DoorOpenCountIntent include the connection to the database to retrieve the amount of time that the light has been on, or the amount of times that the door has been opened.
Instead of using DynamoDB as most other tutorials / projects do, I have chosen a Microsoft SQL database (20Gbytes included in the AWS Free Tier - How to Create it). This choice is because an SQL query can provide you in single database read the result that you are expecting, instead of dealing with key-value pairs as the DynamoDB.
To retrieve the Power Consumption the following query is used:
select sum(datediff(second, OnTime, OffTime)) / 3600 as HoursOfUse
from (select a.[Timestamp] as 'OnTime',
(select MIN(b.[Timestamp])
FROM[portableLightController].[dbo].[ThingShadow] as b
where b.[ShadowKey] = 'toggleLight' and b.[ShadowAttribute] = 'off'
and b.[Timestamp] > a.[Timestamp]) as 'OffTime'
FROM[portableLightController].[dbo].[ThingShadow] as a
where a.[ThingName] = 'ArduinoMKR1000'
and a.[ShadowKey] = 'toggleLight'
and a.[ShadowAttribute] = 'on') as HoursOfUse
The above code is first checking the times when the ShadowKey = toggleLight and the status was on. So you get all the on times of the light. Then it checks for each one, when was the light turned off (b.[Timestamp] > a.[Timestamp]) where b is the turn off, and a is turn on. And finally it adds up in seconds the time difference between a and b sum(datediff(second, OnTime, OffTime)) and divides by 3600 to get it in hours.
After you have the total time in hours, you can multiply by the nominal consumption of each light and get the total consumption. In order to get it for an specific period of time, additional filters like MONTH(a.[Timestamp]) = 'your month' can be added at the end. Check the complete code to see how to handle that part.
The count of door opening times is simpler:
SELECT COUNT(*) as DoorOpenCount
FROM [portableLightController].[dbo].[ThingShadow]
WHERE[ShadowKey] = 'Door' AND [ShadowAttribute] = '1'"
Additionally you may add as well the period of time like mentioned before.
For the initial setup of the database, there are many free options available (to avoid using Microsoft SQL Management Studio), for example, you can download Visual Studio 2017 community edition and install the Database package (install the AWS tools and the node js developing as well to have everything in one place). Or you can create everything directly with node js and the mssql npm module:
app.get('/createTable', function (req, res) {
// connect to your database
sql.connect(config, function (err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query('CREATE TABLE [portableLightController].[dbo].[ThingShadow] \
( \
[Timestamp] datetime2(7) NOT NULL, \
[ThingName] varchar(100) NOT NULL, \
[ShadowKey] varchar(100) NOT NULL, \
[ShadowAttribute] varchar(100), \
)', function (err, recordset) {
if (err) console.log(err)
// send records as a response
res.send(recordset);
sql.close();
});
});
});
Using express js you can define a page to create tables, query them and insert or delete values. Just replace the Create Table with Insert Into, delete, etc.
The Smart Home Skill requires setting up more things (administrative stuff) but less code. To complete the requirements, an extremely great resource is Smart Home Skill under 15 minutes. There you will find everything needed to make it work. Once the initial setup is done, you can start creating the code, for that part you can rely on Amazon's API reference.
It basically consists of two parts, the discovery and the controls, for this project, two controls were created, a Power Control (to turn on and off the light) and the Temperature Request (to return the temperature to the user). Similar to what was done in the custom skill, a connection to the AWS IoT was made (using aws-sdk getThingShadow and updateThingShadow functions).
Finally, the SQL Write Lambda function allows you to connect the data arriving from the device to AWS IoT to the SQL Database. Start by creating an AWS IoT rule that passes all the reported states of the device to the lambda function:
In order to assess that it's working correctly you may add an additional action and redirect it to another topic where you can subscribe and check.
The Lambda function will take that JSON as the event and with an Insert Into query, upload the values to the database:
var queryString = "INSERT INTO [portableLightController].[dbo].[ThingShadow] \
(Timestamp, ThingName, ShadowKey, ShadowAttribute) VALUES";
var i = 1;
// Add each new reported key, attribute to the query
for (var key in reportedShadow) {
var ShadowKey = key;
var ShadowAttribute = reportedShadow[key];
// The first set of values doesn't include comma
if (i == 1) {
queryString += "('" + timestamp.toMysqlFormat() + "' \
,'" + ThingName + "' \
,'" + ShadowKey + "' \
,'" + ShadowAttribute + "')";
}
else {
queryString += ", ('" + timestamp.toMysqlFormat() + "' \
,'" + ThingName + "' \
,'" + ShadowKey + "' \
,'" + ShadowAttribute + "')";
}
i++;
}
// query to the database and set the records
request.query(queryString, function (err, recordset) ...
As the update may include multiple values at once (for example, temperature, altitude, door status, light on/off, etc) it's necessary to loop through the key-value pairs arrived in the JSON and add them to the query string. The query simple uploads the values passed based on the database, table and columns specified in the first line of code above.
Voice User Interface DiagramThe VUI diagram is divided into two parts, one for the Alexa Smart Home Skill, and one for the Alexa Custom Skill:
The first part of the Custom skill, where the most important intents are:
And finally, to the right side of part 1 (mainly the power consumption intent):
Diagram of connections that represents the paths that data has to go through in order to provide the requested information to the user from the device.
Information originated on the Arduino, pass through the MQTT Broker and is delivered to the AWS IoT portal, from there a Lambda functions saves the information to the MS SQL Database.
If a user makes a request, alexa depending on which skill has been activated, passes the request to a Lambda function that acquires the information either from the AWS IoT or the MS SQL DB.
Finally, if the users ask Alexa to change the state of the light (on/off), the information is passed from the Alexa Home Skill to the Lambda Function, then to the AWS IoT and finally to the MQTT Broker and the Arduino.
Comments