The PowerHome Generic Serial / Arduino controller is intended for DIY home automation hardware primarily based upon the Arduino platform but can be utilized with any serial port controlled device provided that it can be programmed to support the protocol. The protocol that this controller uses is a simple, lightweight, two-way query and control protocol detailed below.
Configuration of the controller within the Setup section of the PH Explorer is quite simple. Pressing the "Settings" button for the controller opens a window requiring only 2 fields. The COM port that the hardware is connected to on the PowerHome machine and the COM settings for the hardware, typically "19200,N,8,1" for 19200 BPS, No parity, 8 data bits, and 1 stop bit.
The controller supports a number of devices within PowerHome. On the Digital IO screen: Input, Output, IOT Input, and IOT Output device types are all supported. On the Analog IO screen: Input, Output, IOT Input, and IOT Output device types are also supported. For both the Digital IO and Analog IO screens, the Unit column must be set to 0 and the Point column can be set to any number between 0 and 512 inclusive.
The controller protocol consists of 4 basic command types depending upon the Digital or Analog IO device type that you're controlling or querying. These basic command types are:
The commands sent from PowerHome to the connected controller use the 4 basic commands above with either an "R" or a "W" appended to them to specify whether the command is a "Read" (query the controller for current status) or a "Write" (command the controller to change the status). The controller will respond as appropriate with an echo of the command without the "R" or "W". Any command that is initiated by PowerHome is expected to be acknowledged by the controller. It is this acknowledgement that updates the actual device status within PowerHome. Every command (in both directions) starts with an asterisk "*" character and ends with a carriage return / linefeed combination. After the "*" character comes the basic command. If PowerHome is sending the command, then either an "R" or "W" will also be appended. After the command comes a colon ":" followed by the "Point" number that you defined within PowerHome. If the command is a read command (DIR, DOR, AIR, or AOR) from PowerHome, nothing more is needed and the command is terminated with cr/lf. If the command is a write command from PowerHome (DIW, DOW, AIW, or AOW), then the point value is followed by a comma followed by the actual data you wish to write. Commands sent from the controller to PowerHome start with an "*" followed by the command (DI, DO, AI, and AO), followed by a ":", followed by the point value, followed by a comma, followed by the status of the device and ending with the cr/lf pair. Sample commands can be seen below:
PowerHome also provides a controller command (9990) that allows you to send any string you like to the connected controller through the serial port. This string can be an actual command adhering to the standard protocol or can be something else entirely if you wished to extend the protocol and have added support for it in your external device. Use the ph_ctlrcmd ( ) function to send the controller command. The string to send to the serial port should be in Parm3. An example of sending an infrared command using the Pronto format might look like this: ph_ctlrcmd(5,"ARDUINO",9990,0,0,"*IR:0000 0071 0022 0002 0149 00A7 0014 003F 0014 0014 0014 003F 0014 0014 0014 0014 0014 0014 0014 0014 0014 003F 0014 003F 0014 0014 0014 003F 0014 003F 0014 0014 0014 003F 0014 003F 0014 003F 0014 0014 0014 003F 0014 003F 0014 0014 0014 0014 0014 0014 0014 0014 0014 0014 0014 003F 0014 0014 0014 0014 0014 003F 0014 003F 0014 003F 0014 003F 0014 003F 0014 05A4 0149 0053 0014 05A4~r~n","")
A sample of actual Arduino code for control and query of several points can be seen below and can be modified to suit your own creations:
//CDS AI 1
//Temp AI 2
//Humd AI 3
//Ultrasonic
AI 4
//PIR DI 1
//Pink Jack DI 2
//Yellow Jack DO 3
#include <SimpleDHT.h>
#define DHTPIN 5
#define CDSPIN 4
#define PIRPIN
2
#define UTrigPin 7
#define UEchoPin 8
#define PINKPIN 3
#define
YELLOWPIN 4
#undef abs
SimpleDHT22 dht22(DHTPIN);
int CDSval = 0;
int Temp = 0;
int Humd = 0;
int
PIRval = -1;
int Udistance = 0;
int Pinkval = -1;
int Yellowval =
-1;
unsigned long DHTmilli = 0;
char serialBuf[64];
void setup()
{
pinMode(PIRPIN,INPUT);
pinMode(UTrigPin,OUTPUT);
pinMode(UEchoPin,INPUT);
pinMode(PINKPIN,INPUT_PULLUP);
pinMode(YELLOWPIN,OUTPUT);
digitalWrite(YELLOWPIN,LOW);
Serial.begin(19200);
}
void loop()
{
ReadDigital("I:1",false,PIRPIN,&PIRval);
ReadDigital("I:2",true,PINKPIN,&Pinkval);
ReadDigital("O:3",false,YELLOWPIN,&Yellowval);
ReadAnalog("I:1",0.05,CDSPIN,&CDSval);
ReadDHT22(2,3,&Temp,&Humd,2,10,&DHTmilli);
ReadDistance(4,UTrigPin,UEchoPin,&Udistance);
ReadSerial();
}
void ReadDigital(const char *type,boolean invert,int
pin,int *value)
{
int temp;
temp = digitalRead(pin);
if(*value != temp)
{
*value = temp;
Serial.print("*D");
Serial.print(type);
Serial.print(",");
if(invert)
{
Serial.println((*value == HIGH ? 0 :
1));
}
else
{
Serial.println(*value);
}
}
}
void ReadAnalog(const char *type,float percentchg,int
pin,int *value)
{
int temp;
temp = analogRead(pin);
if(abs(*value - temp) > (int)round(*value *
percentchg))
{
*value =
temp;
Serial.print("*A");
Serial.print(type);
Serial.print(",");
Serial.println(*value);
}
}
void ReadDHT22(int tempvpin,int humdvpin,int
*tempvar,int *humdvar,int tempchg,int humdchg,unsigned long
*millisec)
{
float TempRaw;
float HumdRaw;
int
Temptemp;
int Humdtemp;
unsigned long millitemp =
millis();
if(abs(*millisec - millitemp) >
2000)
{
dht22.read2(&TempRaw, &HumdRaw,
NULL);
Temptemp = round((TempRaw * 9 / 5 + 32) *
10);
Humdtemp = round(HumdRaw * 10);
if(abs(*tempvar - Temptemp) >
tempchg)
{
*tempvar =
Temptemp;
Serial.print("*AI:");
Serial.print(tempvpin);
Serial.print(",");
Serial.println(*tempvar);
}
if(abs(*humdvar - Humdtemp) >
10)
{
*humdvar =
Humdtemp;
Serial.print("*AI:");
Serial.print(humdvpin);
Serial.print(",");
Serial.println(*humdvar);
}
*millisec = millis();
}
}
void ReadDistance(int vpin,int trigpin,int echopin,int
*distance)
{
int temp;
unsigned long duration = 0;
digitalWrite(trigpin,LOW);
delayMicroseconds(2);
digitalWrite(trigpin,HIGH);
delayMicroseconds(10);
digitalWrite(trigpin,LOW);
duration =
pulseIn(echopin,HIGH);
temp = duration / 74 / 2;
temp =
*distance - (*distance / 3) + (temp / 3);
if(abs(*distance - temp)
> (int)round(*distance * 0.10))
{
*distance =
temp;
Serial.print("*AI:");
Serial.print(vpin);
Serial.print(",");
Serial.println(*distance);
}
}
void ReadSerial()
{
static int ndx =
0;
static int cmd = 0;
static int point = 0;
static int value = 0;
char rc;
while (Serial.available() > 0)
{
rc = Serial.read();
switch(rc)
{
case
'*':
ndx =
1;
cmd =
0;
point =
0;
value =
0;
break;
case
'A':
case
'D':
if(ndx ==
1)
{
cmd = rc -
64;
ndx
++;
}
else
ndx =
0;
break;
case
'I':
case
'O':
if(ndx ==
2)
{
cmd = cmd * 10 + rc
- 72;
ndx
++;
}
else
ndx =
0;
break;
case
'R':
case
'W':
if(ndx ==
3)
{
cmd = cmd * 10 + rc
- 81;
ndx
++;
}
else
ndx =
0;
break;
case
':':
if(ndx ==
4)
{
point =
0;
value =
0;
ndx
++;
}
else
ndx =
0;
break;
case
'0':
case
'1':
case
'2':
case
'3':
case
'4':
case
'5':
case
'6':
case
'7':
case
'8':
case
'9':
if(ndx ==
5)
{
point = point * 10 +
rc - 48;
}
else if(ndx ==
6)
{
value = value * 10 +
rc - 48;
}
else
ndx =
0;
break;
case
',':
if(ndx == 5 && point
> 0)
ndx
++;
else
ndx =
0;
break;
case
'\r':
break;
case
'\n':
if(ndx == 5 || ndx ==
6)
ndx =
7;
else
ndx =
0;
break;
}
if(ndx == 7)
{
switch(cmd)
{
case 111:
//AIR
switch(point)
{
case
1:
CDSval =
0;
break;
case
2:
Temp =
0;
break;
case
3:
Humd =
0;
break;
case
4:
Udistance =
0;
break;
}
break;
case 411:
//DIR
switch(point)
{
case
1:
PIRval =
-1;
break;
case
2:
Pinkval =
-1;
break;
}
break;
case 471:
//DOR
switch(point)
{
case
3:
Yellowval =
-1;
break;
}
break;
case 476:
//DOW
switch(point)
{
case
3:
if(value ==
0)
digitalWrite(YELLOWPIN,LOW);
else
digitalWrite(YELLOWPIN,HIGH);
Yellowval =
-1;
break;
}
break;
}
ndx =
0;
cmd = 0;
point = 0;
value = 0;
}
}
}