Skip to content

Instantly share code, notes, and snippets.

@oguime
Created January 9, 2014 23:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oguime/8344321 to your computer and use it in GitHub Desktop.
Save oguime/8344321 to your computer and use it in GitHub Desktop.
Philips Hue wireless physical switch with brightness and hue control Uses HT6P20B based remote controls, 433.92MHz receiver and Arduino + W5100 ethernet shield
/*
Hue_W5100_HT6P20B
http://youtu.be/BRcG0C0mo08
Philips Hue wireless physical switch with brightness and hue control
Uses HT6P20B based remote controls, 433.92MHz receiver and Arduino + W5100 ethernet shield
Gilson Oguime
oguime (at) gmail.com
HT6P20B decoder created by Afonso Celso Turcato
acturcato (at) gmail.com
http://acturcato.wordpress.com/2014/01/04/decoder-for-ht6p20b-encoder-on-arduino-board-english/
*/
#include <SPI.h>
#include <Ethernet.h>
/*
Remote Address Light
0 Intelbras 37492 Hue 1
1 Intelbras 2321684 Hue 2
2 Intelbras 2144096 Hue 3
3 Intelbras 605044 Hue 4
4 ECP 2911907 Empty
*/
const int deviceMax = 4;
const long devices[] = {37492,2321684,2144096,605044,2911907};
// Hue constants
const char hueHubIP[] = "192.168.1.2"; // Hue hub IP
const char hueUsername[] = "newdeveloper"; // Hue username
const int hueHubPort = 80;
const int hueBriMax = 255; // max brightness value
const int hueBriStep = 64; // brightness step size when button 1 is pressed (256/64 = 4 steps)
const long hueHueMax = 65535; // max hue value
const long hueHueStep = 10922; // hue step size when button 2 is pressed (65536/10922 = 6 steps)
// Hue variables
int hueBriDir[deviceMax]; // holds brightness step direction for each remote/light (up=1,down=-1)
long hueHueDir[deviceMax]; // holds hue step direction for each remote/light (up=1,down=-1)
int hueButton; // button pressed (0 to 3)
int hueLight; // target light
boolean hueOn; // on/off
int hueBri; // brightness value
long hueHue; // hue value
String hueCmd; // Hue command
// ACT_HT6P20B variables
byte pinRF; // RF Module pin
boolean startbit;
boolean dataok;
int counter; //received bits counter: 22 of Address + 2 of Data + 4 of EndCode (Anti-Code)
int lambda; // on pulse clock width (if fosc = 2KHz than lambda = 500 us)
int dur0, dur1; // pulses durations (auxiliary)
unsigned long buffer=0; //buffer for received data storage
unsigned long addr;
int button;
// Ethernet
byte mac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; // W5100 MAC address
IPAddress ip(192,168,1,251); // Arduino IP
EthernetClient client;
/*
Setup
*/
void setup()
{
Serial.begin(9600);
Serial.println("[Hue_W5100_HT6P20B]");
Ethernet.begin(mac,ip);
pinRF = 2;
pinMode(pinRF, INPUT);
delay(2000);
Serial.println("Ready.");
}
/*
ACT_HT6P20B intercepts remote control button press.
If the remote is recognized, GetHue captures the corresponding light state (on, bri and hue).
Button 0 toggles light On/Off.
Button 1 steps brightness value up or down, depending on brightness direction.
Button 2 steps hue value up or down, depending on hue direction.
SetHue sends hueCmd command.
*/
void loop()
{
if (ACT_HT6P20B())
{
for (hueLight = 0; hueLight <= deviceMax; hueLight++) // scan devices
{
if (addr == devices[hueLight]) // compare to device list
{
hueButton = button;
if (GetHue()) // get light state
{
hueCmd = ""; // initialize command
if ((hueButton == 0) && hueOn) // toggle light Off
hueCmd = "{\"on\":false}";
else if ((hueButton == 0) && !hueOn) // toggle light On
hueCmd = "{\"on\":true}";
else if ((hueButton == 1) && hueOn) // brightness button
{
if (hueBriDir[hueLight] == 0) // initialize brightness direction
{
if (hueBri >= hueBriMax)
hueBriDir[hueLight] = -1; // decrease if already max
else
hueBriDir[hueLight] = 1; // increase otherwise
}
hueBri += (hueBriStep * hueBriDir[hueLight]); // brightness = brightness + step
if (hueBri >= hueBriMax) // brightness higher or equal to max brightness
{
hueBri = hueBriMax; // set brightness to max
hueBriDir[hueLight] = -1; // set brightness direction down
}
if (hueBri <= 0) // brightness lower or equal to 0
{
hueBri = 0; // set brightness to 0
hueBriDir[hueLight] = 1; // set brightness direction up
}
hueCmd = "{\"bri\": " + String(hueBri) +"}"; // light brightness command
}
else if ((hueButton == 2) && hueOn) // hue button
{
if (hueHueDir[hueLight] == 0) // initialize hue direction
{
if (hueHue >= hueHueMax)
hueHueDir[hueLight] = -1; // decrease if already max
else
hueHueDir[hueLight] = 1; // increase otherwise
}
hueHue += (hueHueStep * hueHueDir[hueLight]); // hue = hue + step
if (hueHue >= hueHueMax) // hue higher than max hue
{
hueHue = hueHueMax; // set hue to max
hueHueDir[hueLight] = -1; // set hue direction down
}
if (hueHue <= 0) // hue lower or equal to 0
{
hueHue = 0; // set hue to 0
hueHueDir[hueLight] = 1; // set hue direction up
}
hueCmd = "{\"hue\": " + String(hueHue) +"}"; // hue hue command
}
if (hueCmd.length()>0) // command constructed
{
if (SetHue()) // set hue light state
{
Serial.print("Button : "); Serial.println(hueButton);
Serial.print("Light : "); Serial.println(hueLight + 1);
Serial.print("Cmd : "); Serial.println(hueCmd);
}
else // SetHue failed
{
Serial.print("Could not execute ");Serial.print(hueCmd);
Serial.print(" on Light ");Serial.print(hueLight + 1);Serial.print(" !");
}
}
else // command not defined
{
Serial.print("Could not build command for light ");
Serial.print(hueLight + 1);Serial.println(" !");
}
}
else // GetHue failed
{
Serial.print("Could not get Light ");Serial.print(hueLight + 1);Serial.println(" state!");
}
break; // corresponding light found, no reason to keep looking
}
}
if (hueLight > deviceMax) // recognized device not in list
{
Serial.print("Addr ");Serial.print(addr);Serial.println(" not in list!");
}
Serial.println();
}
}
/*
GetHue
Get light state (on,bri,hue)
*/
boolean GetHue()
{
if (client.connect(hueHubIP, hueHubPort))
{
client.print("GET /api/");
client.print(hueUsername);
client.print("/lights/");
client.print(hueLight + 1); // hueLight zero based, add 1
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(hueHubIP);
client.println("Content-type: application/json");
client.println("keep-alive");
client.println();
while (client.connected())
{
if (client.available())
{
client.findUntil("\"on\":", "\0");
hueOn = (client.readStringUntil(',') == "true"); // if light is on, set variable to true
client.findUntil("\"bri\":", "\0");
hueBri = client.readStringUntil(',').toInt(); // set variable to brightness value
client.findUntil("\"hue\":", "\0");
hueHue = client.readStringUntil(',').toInt(); // set variable to hue value
break; // not capturing other light attributes yet
}
}
client.stop();
return true; // captured on,bri,hue
}
else
return false; // error reading on,bri,hue
}
/*
SetHue
Set light state using hueCmd command
*/
boolean SetHue()
{
if (client.connect(hueHubIP, hueHubPort))
{
while (client.connected())
{
client.print("PUT /api/");
client.print(hueUsername);
client.print("/lights/");
client.print(hueLight + 1); // hueLight zero based, add 1
client.println("/state HTTP/1.1");
client.println("keep-alive");
client.print("Host: ");
client.println(hueHubIP);
client.print("Content-Length: ");
client.println(hueCmd.length());
client.println("Content-Type: text/plain;charset=UTF-8");
client.println(); // blank line before body
client.println(hueCmd); // Hue command
}
client.stop();
return true; // command executed
}
else
return false; // command failed
}
/*
ACT_HT6P20B
Based on ACT_HT6P20B_RX.ino from AFONSO CELSO TURCATO
Modified to return boolean indicating successful button press captured
*/
boolean ACT_HT6P20B()
{
digitalWrite(13, digitalRead(pinRF)); //blink de onboard LED when receive something
if (!startbit)
{// Check the PILOT CODE until START BIT;
dur0 = pulseIn(pinRF, LOW); //Check how long DOUT was "0" (ZERO) (refers to PILOT CODE)
// If Start Bit is OK, then starts measure os how long the signal is level "1" and check is value is into acceptable range.
if((dur0 > 9200) && (dur0 < 13800) && !startbit)
{
lambda = dur0 / 23; //calculate wave length - lambda
dur0 = 0;
buffer = 0;
counter = 0;
dataok = false;
startbit = true;
}
}
// If Start Bit is OK, then starts measure os how long the signal is level "1" and check is value is into acceptable range.
if (startbit && !dataok && counter < 28)
{
++counter;
dur1 = pulseIn(pinRF, HIGH);
if((dur1 > 0.5 * lambda) && (dur1 < (1.5 * lambda))) //If pulse width at "1" is between "0.5 and 1.5 lambda", means that pulse is only one lambda, so the data é "1".
{
buffer = (buffer << 1) + 1; // add "1" on data buffer
}
else if((dur1 > 1.5 * lambda) && (dur1 < (2.5 * lambda))) //If pulse width at "1" is between "1.5 and 2.5 lambda", means that pulse is two lambdas, so the data é "0".
{
buffer = (buffer << 1); // add "0" on data buffer
}
else
{
//Reset the loop
startbit = false;
}
}
//Check if all 28 bits were received (22 of Address + 2 of Data + 4 of Anti-Code)
if (counter==28)
{
// Check if Anti-Code is OK (last 4 bits of buffer equal "0101")
if ((bitRead(buffer, 0) == 1) && (bitRead(buffer, 1) == 0) && (bitRead(buffer, 2) == 1) && (bitRead(buffer, 3) == 0))
{
dataok = true;
counter = 0;
startbit = false;
addr = buffer >> 6;
button = (2*!bitRead(buffer,5)+!bitRead(buffer,4));
Serial.print("Address: "); Serial.println(addr);
return true;
}
else
{
//Reset the loop
startbit = false;
}
}
return false;
}
@acturcato
Copy link

Gilson, very nice code!

I revised my code based on the changes you made.
For more details, access the link:
http://acturcato.wordpress.com/2014/01/04/decoder-for-ht6p20b-encoder-on-arduino-board-english/

See you!

@oguime
Copy link
Author

oguime commented Jan 10, 2014

Hi Afonso, thanks!

And I noticed you've changed the function to pass address and button by reference. Fixed that and posted to github at https://github.com/oguime/Hue_W5100_HT6P20B

Regards,
Gilson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment