RCOutlets
RCOutlets Documentation
- Version
-
0.3.0
- Author
-
Alexander Keil
- Contact
- Date
-
2014-01-04
- Abstract
-
Control radio controlled outlets from a computer with Arduino.
Contents
Hardware
Required Parts
Radio Controlled Outlets
Arduino
RF Transmitter
2 LED's, 2 Resistors, wires
RC Outlets
The radio controlled outlet comes as a set with three outlet adapters and a handset remote control. The adapters are numbers (1, 2, 3) and the remote has three corresponding pairs of "on"/"off" buttons, one for each outlet (6 buttons total).
There is a rocker switch on the remote and a small dial on the adapter that lets you set one of for "groups" (A-D). This allows to use more than one set at the same time. The adapter "1" will respond to the "1 on" button on the remote only if the remote is set to the same channel as the adapter.
Group |
Numbers |
||
---|---|---|---|
1 |
2 |
3 |
|
A |
A1 |
A2 |
A3 |
B |
B1 |
B2 |
B3 |
C |
C1 |
C2 |
C3 |
D |
D1 |
D2 |
D3 |
Thus, up to 12 outlets can be controlled with a total of 24 commands (12x "on", 12x "off").
RF Transmitter
Aside from the Arduino, a 433.92 MHz RF Transmitter is required. It does not really matter, which exact RF module is used, as long as it send on 433.92 MHz and runs on 5V. It would even be possible (and much cheaper) to use the RF module from the handheld remote.
In this case an Aurel TX-SAW Mid/5V is used.
The Aurel Transmitter is (for example) available at Conrad (German site), the manufacturer has a user manual [PDF] and a datasheet can also be found.
The Aurel TX-SAW Mid/5V transmitter module has 6 pins which are used as follows:
Pin |
Name |
Description |
---|---|---|
1 |
Tx Data |
Data input, connect to |
2 |
GND |
Connect to ground. |
7 |
GND |
Connect to ground. |
8 |
RF out |
RF output connected to antenna. |
9 |
GND |
Connect to ground. |
10 |
V+ |
Connection to 5V supply of Arduino. |
You can use a single GND connection for Pins 2,7 and 9. A piece of wire constitutes a sufficient antenna.
Status LED's
We will also connect two status LED's:
- Green
-
Lights when a command is received over serial and while the command is transmitted over wireless.
- Red
-
Lights for some time if there was an error (e.g. a bad command was received).
Arduino Sketch
Protocol
We use plain text messages to be sent as commands to the Arduino. the messages are fixed length, each terminated by a newline. Each message consists of four characters:
Index |
Meaning |
Values |
---|---|---|
0 |
Group |
|
1 |
Device |
|
2 |
Command |
|
3 |
Terminate |
|
Example:
> A10 # group A, switch 1, "off" > A21 # group A, switch 2, "on" > B11 # group B, switch 1, "on"
Additionally, a simple handshake mechanism is required. When the serial connection to the Arduino is opened, the Arduino will restart and not be ready for a few seconds. All data that is sent before the board is ready will be lost.
The handshake scheme is that the "client" (our Python script)
sends an ASCII ENQ
character and the "server" replies with
a single ASCII ACK
to signal that it is ready.
When initializing the connection, we will keep sending ENQ
until we receive an ACK
(or until a timeout runs out).
After the ACK
is received, we can start sending commands
to control the power outlets.
In pseudo code:
while True: serial.write(ENQ) if serial.read(1) == ACK: break
Code
Luckily, a project called RCSwitch provides us with a library that can handle the interaction with the RF module and also knows how to encode the command which are sent to the power outlets.
Thus, the only thing left is interpreting input received over serial and deciding which commands to send.
Python Client
The Python module uses pySerial to communicate with the Arduino board.
The script can be used as a library module in another Python script or it can be run as a standalone command line app.
Usage
The following interface is supported:
with RCOutlets(options) as outlets: # by group and number outlets.get('A1').on() outlets.get('A0').off() # with aliases/pattern matching for switch in outlets.mget('kitchen B*'): switch.off()
Sample command line usage:
$ outlet A1 on $ outlet A0 off $ outlet desk on $ outlet 'kitchen B*' off
Configuration
If the file ~/.config/rcoutlets.conf
exists,
values from that file are used in the command-line program.
This is how a sample config could look like:
[rcoutlets] port = /dev/ttyACM0 timeout = 2 groups = A B handshake_timeout = 4 [alias] desk = A1 kitchen = B1 B2 C1
The configuration file can map alias names to switch-identifiers. An alias can refer to several switches and it is possible to define groups of switches. If a command is issued to a group, all switches in that group receive that command.
The Daemon
When the serial connection to the Arduino is initialized, the board is reset and it takes some time for it to come up again. For this reason, the Python script connects to the Arduino via a handshake. It keeps sending a Handshake Request until it receives the proper Handshake Response. Only after the handshake completed successfully, we can start sending commands.
This works well but since the handshake takes about two seconds, a simple run-once script will be unresponsive. It has to initialize the connection each time it is invoked, so every command is delayed by the time it takes to build up the collection.
For this reason, we need a daemon which runs constantly and keeps the connection open. Our command line script will then not connect to the Arduino but instead send its command to the daemon process which relays it over serial.
Requirements:
- Reconnect
-
The daemon process might lose its connection to the Arduino, for example if the board is disconnected. The daemon should be able to detect a lost connection and try to rebuild it.
- Start on request
-
Start the daemon only when its (first) required. This is a task for the OS. Connect lazily, i.e. when the first command is sent. This is implemented in the daemon.
The same script is used for:
run the daemon
run as a client for the daemon
run standalone with a new connection for each command
The python script will support a command line option to run as a daemon. If called with this option, the script starts the daemon and then exits.
$ rcoutlets --daemon
Here is an example implementation of running a daemon in PYthon.
Pidfile
By default, it is /var/run/rcoutlets/rcoutlets.pid
.
It is located in a separate directory to allow non-root users to write it
without granting write permissions on /var/run/
.
Prepare like so (as root):
# mkdir /var/run/rcoutlets # chown username:group /var/run/rcoutlets
Where username and group are the user and group that will start the rcoutlets service.
Com
The controller script used on the console to issue individual commands must of course be able to communicate its commands to the daemon.
For this, a small TCP server is implemented, which receives command-strings and sends them through the serial connection. The daemon process simply starts the server.
The command-script will either use a direct serial connection or a TCP client to send its commands.
Android App
Create an Android app to control the lights.
Functions
Have two buttons for each switch, one for on and one for off.
Also, have named groups that combine several switches. would be nice if user could define these groups.
GUI-Layout
Have several tabs, one for each group of switches. That is four tabs. Each tab has 5x2 buttons: "on" and "off" buttons for all four switches plus additional buttons for controlling all switches of the group.
Add an additional tab with favorites. Would require to disable tabs if a group is not defined.
Note
Question
How do dynamically add components to the UI?
How do we add/remove (or hide/show) tabs?
+--------+--------+--------+-------+ | Tab A | Tab B | Tab C | Tab D | +--------+ +--------+-------+ | | | A* <on> <off> | | - - - - - - - - - - - - - - - - -| | | | A1 <on> <off> | | | | A2 <on> <off> | | | | A3 <on> <off> | | | | A4 <on> <off> | | | +----------------------------------+
TCP Client
A TCP Client is required to communicate with the Python server.
The TCP client only has a single business method sendCommand
which is used
to send commands to the server.
The TCP client takes the following configuration options:
- host
-
Hostname or IP address of the server.
- port
-
Port number of the server.
- timeout
-
Connection timeout for talking to the server.
Note
Question
Do we have to tear down the TCP connection when the application is moved to the "background" Or when the application quits?
Does it make sense to keep the connection open? Or is it better to open a new connection for each call?
Project Setup
Install the SDK. Apparently the Eclipse Plugin is not strictly required, there is a command line tool.
Manifest File
A manifest file lists all components of the app?
<!-- AndroidManifest.xml --> <manifest> <uses-sdk android:minSdkVersion="8" ... /> </manifest>
Directory Structure:
src/ res/ drawable-hdpi/ layout/ values/ ...
Activity
The main Activity
goes into the src/
folder.
GUI
Android GUI's are defined statically in an XML file like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:id="id" android:text="text" android:onClick="handleClick" /> </LinearLayout>
GUI elements can be connected to controller code in the layout definition.
In the Activity
implement a method handleClick(View view)
,
in the layout XML assign onClick="handleClick"
on a button.
Code
Arduino-Sketch
The complete Arduino sketch looks like this:
Python Script
The complete script looks like this: