Skip to content

Instantly share code, notes, and snippets.

@mattiarossi
Created June 1, 2016 22:34
Show Gist options
  • Select an option

  • Save mattiarossi/da69884f160cfa7c46c71d9603f44bee to your computer and use it in GitHub Desktop.

Select an option

Save mattiarossi/da69884f160cfa7c46c71d9603f44bee to your computer and use it in GitHub Desktop.
#include <ESP8266WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <PubSubClient.h>
//Def
#define HVAC_TOSHIBA_DEBUG; // Un comment to access DEBUG information through Serial Interface
#define ONE_WIRE_BUS 10 // DS18B20 on arduino pin10 corresponds to SD3 on ESP8266 Dev Board
#define NUMROOMS 5
#define DELAY 60000 //Check temp every 10 sec
int ledPin = 2; // GPIO2
WiFiClient espClient;
PubSubClient client(espClient);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
float prevTemp = 0;
const char* MY_SSID = "WIFISSID";
const char* MY_PWD = "WIFIPWD";
int sent = 0;
char mqtt_user[] = "mqttuser";
char mqtt_password[] = "mqttpasswd";
char mqtt_id[] = "esp8266-01";
const char* mqtt_server = "ipoftheserver";
const char * outTopic = "esp8266/01/out";
const char * pingTopic = "esp8266/01/ping";
char * statusTopic = "esp8266/01/status/00/00";
char * inTopic = "esp8266/01/in/#";
typedef struct roomHvac RoomHvac;
unsigned long nextPingOn = 0;
unsigned long pingDelay = 60000;
byte probes[][8] = {
{ 0x28, 0x82, 0xEB, 0x35, 0x05, 0x00, 0x00, 0xCC },
{ 0x28, 0xDD, 0x1F, 0x35, 0x05, 0x00, 0x00, 0x7F },
{ 0x28, 0xB7, 0x8B, 0x34, 0x05, 0x00, 0x00, 0x54 },
{ 0x28, 0x2B, 0x4C, 0x35, 0x05, 0x00, 0x00, 0x64 },
{ 0x28, 0x4F, 0xC3, 0xA2, 0x04, 0x00, 0x00, 0x13 }
};
float drift[5] = {0, 0, 0, 0, 0};
// Reconnect to the MQTT server in case of disconnect
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
#ifdef HVAC_TOSHIBA_DEBUG
Serial.println("Attempting MQTT reconnection...");
#endif
if (WiFi.status() != WL_CONNECTED)
{
connectWifi();
}
// Attempt to connect
if (client.connect(mqtt_id, mqtt_user, mqtt_password)) {
#ifdef HVAC_TOSHIBA_DEBUG
Serial.println("reconnected");
#endif
// Once connected, publish an announcement...
client.publish(pingTopic, "2");
// ... and resubscribe
client.subscribe(inTopic);
} else {
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
#endif
delay(15000);
}
}
}
int halfPeriodicTime;
int IRpin;
int khz;
typedef enum HvacMode {
HVAC_HOT,
HVAC_COLD,
HVAC_DRY,
HVAC_FAN, // used for Panasonic only
HVAC_AUTO
} HvacMode_t; // HVAC MODE
typedef enum HvacFanMode {
FAN_SPEED_1,
FAN_SPEED_2,
FAN_SPEED_3,
FAN_SPEED_4,
FAN_SPEED_5,
FAN_SPEED_AUTO,
FAN_SPEED_SILENT
} HvacFanMode_; // HVAC FAN MODE
typedef enum HvacVanneMode {
VANNE_AUTO,
VANNE_H1,
VANNE_H2,
VANNE_H3,
VANNE_H4,
VANNE_H5,
VANNE_AUTO_MOVE
} HvacVanneMode_; // HVAC VANNE MODE
typedef enum HvacWideVanneMode {
WIDE_LEFT_END,
WIDE_LEFT,
WIDE_MIDDLE,
WIDE_RIGHT,
WIDE_RIGHT_END
} HvacWideVanneMode_t; // HVAC WIDE VANNE MODE
typedef enum HvacAreaMode {
AREA_SWING,
AREA_LEFT,
AREA_AUTO,
AREA_RIGHT
} HvacAreaMode_t; // HVAC WIDE VANNE MODE
typedef enum HvacProfileMode {
NORMAL,
QUIET,
BOOST
} HvacProfileMode_t; // HVAC PANASONIC OPTION MODE
struct roomHvac
{
int temp;
int state; // 0 - OFF 1 - ON
HvacFanMode fanHvac;
HvacMode modeHvac;
int pin;
float probeTemp;
float oldTemp;
};
void sendHvacToshiba(
HvacMode ,
int ,
HvacFanMode ,
int ,
int
);
typedef struct roomHvac RoomHvac;
RoomHvac rooms[NUMROOMS];
void publishRoomStatus(int roomNo) {
char b[10];
statusTopic[19] = '0' + roomNo;
statusTopic[22] = '0';
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Sending update for room ");
Serial.println(roomNo);
Serial.println();
#endif
sprintf(b, "%d", rooms[roomNo].state);
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Publishing value ");
Serial.print(b);
Serial.print(" to Topic: ");
Serial.println(statusTopic);
#endif
client.publish(statusTopic, b);
statusTopic[22] = '1';
sprintf(b, "%d", rooms[roomNo].temp);
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Publishing value ");
Serial.print(b);
Serial.print(" to Topic: ");
Serial.println(statusTopic);
#endif
client.publish(statusTopic, b);
statusTopic[22] = '2';
sprintf(b, "%d", (int)rooms[roomNo].modeHvac);
client.publish(statusTopic, b);
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Publishing value ");
Serial.print(b);
Serial.print(" to Topic: ");
Serial.println(statusTopic);
#endif
statusTopic[22] = '3';
sprintf(b, "%d", (int)rooms[roomNo].fanHvac);
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Publishing value ");
Serial.print(b);
Serial.print(" to Topic: ");
Serial.println(statusTopic);
#endif
client.publish(statusTopic, b);
statusTopic[22] = '4';
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Publishing value ");
Serial.print(rooms[roomNo].probeTemp);
Serial.print(" to Topic: ");
Serial.println(statusTopic);
#endif
client.publish(statusTopic, f2s(rooms[roomNo].probeTemp, 2));
}
void callback(char* topic, byte* payload, unsigned int length) {
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
#endif
payload[length] = '\0';
int val = atoi((char *)payload);
client.publish(outTopic, "received");
int roomNo = (int)topic[15] - 48;
char commandRoom = topic[18];
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Received command for room:");
Serial.println(roomNo);
Serial.print("Received command:");
Serial.println(commandRoom);
Serial.print("Received val:");
Serial.println(val);
#endif
/*
Rooms:
0: Sala
1: Studio
2: Camera PT
3: Camera NORD PP
4: Camera SUD PP
Commands:
0: 0 = OFF 1 = ON
1: TEMP SETPOINT (17-30)
2: AC MODE: 0: Hot 1: Cold 2: Dry 4: Auto
3: FAN Mode: 0: 1 1: 2 2:3 3: 4 4: 5 5: AUTO
*/
if (roomNo >= 0 && roomNo <= NUMROOMS - 1 ) {
switch (commandRoom) {
case '0':
if (val == 0 || val == 1) {
rooms[roomNo].state = val;
}
break;
case '1':
if (val < 17) {
val = 17;
}
if (val > 30) {
val = 30;
}
rooms[roomNo].temp = val;
break;
case '2':
if (val >= 0 && val <= 4) {
rooms[roomNo].modeHvac = (HvacMode) val;
}
break;
case '3':
if (val >= 0 && val <= 5) {
rooms[roomNo].fanHvac = (HvacFanMode)val;
}
break;
default:
break;
}
#ifdef HVAC_TOSHIBA_DEBUG
Serial.println("Room Data:");
Serial.print("State: ");
Serial.println(rooms[roomNo].state);
Serial.print("Temp: ");
Serial.println(rooms[roomNo].temp);
Serial.print("Mode: ");
Serial.println(rooms[roomNo].modeHvac);
Serial.print("Fan: ");
Serial.println(rooms[roomNo].fanHvac);
#endif
int offState = 0;
if ( rooms[roomNo].state == 0) {
offState = 1;
}
sendHvacToshiba(rooms[roomNo].modeHvac, rooms[roomNo].temp, rooms[roomNo].fanHvac, offState, rooms[roomNo].pin);
//const char * statusTopic="esp8266/01/status/00/00";
publishRoomStatus(roomNo);
}
}
// HVAC TOSHIBA_
#define HVAC_TOSHIBA_HDR_MARK 4400
#define HVAC_TOSHIBA_HDR_SPACE 4300
#define HVAC_TOSHIBA_BIT_MARK 560
#define HVAC_TOSHIBA_ONE_SPACE 1590
#define HVAC_MISTUBISHI_ZERO_SPACE 472
#define HVAC_TOSHIBA_RPT_MARK 440
#define HVAC_TOSHIBA_RPT_SPACE 7048 // Above original iremote limit
/****************************************************************************
/* Send IR command to Toshiba HVAC - sendHvacToshiba
/***************************************************************************/
void sendHvacToshiba(
HvacMode HVAC_Mode, // Example HVAC_HOT
int HVAC_Temp, // Example 21 (°c)
HvacFanMode HVAC_FanMode, // Example FAN_SPEED_AUTO
int OFF, // Example false
int irPin
)
{
#define HVAC_TOSHIBA_DATALEN 9
IRpin = irPin;
byte mask = 1; //our bitmask
//F20D03FC0150000051
byte data[HVAC_TOSHIBA_DATALEN] = { 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00 };
// data array is a valid trame, only byte to be chnaged will be updated.
byte i;
#ifdef HVAC_TOSHIBA_DEBUG
Serial.println("Packet to send: ");
for (i = 0; i < HVAC_TOSHIBA_DATALEN; i++) {
Serial.print("_");
Serial.print(data[i], HEX);
}
Serial.println(".");
#endif
data[6] = 0x00;
// Byte 7 - Mode
switch (HVAC_Mode)
{
case HVAC_HOT: data[6] = (byte) B00000011; break;
case HVAC_COLD: data[6] = (byte) B00000001; break;
case HVAC_DRY: data[6] = (byte) B00000010; break;
case HVAC_AUTO: data[6] = (byte) B00000000; break;
default: break;
}
// Byte 7 - On / Off
if (OFF) {
data[6] = (byte) 0x07; // Turn OFF HVAC
} else {
// Turn ON HVAC (default)
}
// Byte 6 - Temperature
// Check Min Max For Hot Mode
byte Temp;
if (HVAC_Temp > 30) {
Temp = 30;
}
else if (HVAC_Temp < 17) {
Temp = 17;
}
else {
Temp = HVAC_Temp;
};
data[5] = (byte) Temp - 17 << 4;
// Byte 10 - FAN / VANNE
switch (HVAC_FanMode)
{
case FAN_SPEED_1: data[6] = data[6] | (byte) B01000000; break;
case FAN_SPEED_2: data[6] = data[6] | (byte) B01100000; break;
case FAN_SPEED_3: data[6] = data[6] | (byte) B10000000; break;
case FAN_SPEED_4: data[6] = data[6] | (byte) B10100000; break;
case FAN_SPEED_5: data[6] = data[6] | (byte) B11000000; break;
case FAN_SPEED_AUTO: data[6] = data[6] | (byte) B00000000; break;
case FAN_SPEED_SILENT: data[6] = data[6] | (byte) B00000000; break;//No FAN speed SILENT for TOSHIBA so it is consider as Speed AUTO
default: break;
}
// Byte 9 - CRC
data[8] = 0;
for (i = 0; i < HVAC_TOSHIBA_DATALEN - 1; i++) {
data[HVAC_TOSHIBA_DATALEN - 1] = (byte) data[i] ^ data[HVAC_TOSHIBA_DATALEN - 1]; // CRC is a simple bits addition
}
#ifdef HVAC_TOSHIBA_DEBUG
Serial.println("Packet to send: ");
for (i = 0; i < HVAC_TOSHIBA_DATALEN; i++) {
Serial.print("_"); Serial.print(data[i], HEX);
}
Serial.println(".");
for (i = 0; i < HVAC_TOSHIBA_DATALEN ; i++) {
Serial.print(data[i], BIN); Serial.print(" ");
}
Serial.println(".");
#endif
enableIROut(38); // 38khz
space(0);
for (int j = 0; j < 2; j++) { // For Toshiba IR protocol we have to send two time the packet data
// Header for the Packet
mark(HVAC_TOSHIBA_HDR_MARK);
space(HVAC_TOSHIBA_HDR_SPACE);
for (i = 0; i < HVAC_TOSHIBA_DATALEN; i++) {
// Send all Bits from Byte Data in Forward Order (MSB)
for (mask = 10000000; mask > 0; mask >>= 1) { //iterate through bit mask
if (data[i] & mask) { // Bit ONE
mark(HVAC_TOSHIBA_BIT_MARK);
space(HVAC_TOSHIBA_ONE_SPACE);
}
else { // Bit ZERO
mark(HVAC_TOSHIBA_BIT_MARK);
space(HVAC_MISTUBISHI_ZERO_SPACE);
}
//Next bits
}
}
// End of Packet and retransmission of the Packet
mark(HVAC_TOSHIBA_RPT_MARK);
space(HVAC_TOSHIBA_RPT_SPACE);
space(0); // Just to be sure
}
}
/****************************************************************************
/* enableIROut : Set global Variable for Frequency IR Emission
/***************************************************************************/
void enableIROut(int khz) {
// Enables IR output. The khz value controls the modulation frequency in kilohertz.
halfPeriodicTime = 500 / khz; // T = 1/f but we need T/2 in microsecond and f is in kHz
}
/****************************************************************************
/* mark ( int time)
/***************************************************************************/
void mark(int time) {
// Sends an IR mark for the specified number of microseconds.
// The mark output is modulated at the PWM frequency.
long beginning = micros();
while (micros() - beginning < time) {
digitalWrite(IRpin, HIGH);
delayMicroseconds(halfPeriodicTime);
digitalWrite(IRpin, LOW);
delayMicroseconds(halfPeriodicTime); //38 kHz -> T = 26.31 microsec (periodic time), half of it is 13
}
}
/****************************************************************************
/* space ( int time)
/***************************************************************************/
/* Leave pin off for time (given in microseconds) */
void space(int time) {
// Sends an IR space for the specified number of microseconds.
// A space is no output, so the PWM output is disabled.
digitalWrite(IRpin, LOW);
if (time > 0) delayMicroseconds(time);
}
/****************************************************************************
/* sendRaw (unsigned int buf[], int len, int hz)
/***************************************************************************/
void sendRaw (unsigned int buf[], int len, int hz)
{
enableIROut(hz);
for (int i = 0; i < len; i++) {
if (i & 1) {
space(buf[i]);
}
else {
mark(buf[i]);
}
}
space(0); // Just to be sure
}
/* Check the temperature data */
void tempTimer() {
//Getting the temperature
float temp = 0;
for (int r = 0; r < NUMROOMS; r++) {
rooms[r].oldTemp = rooms[r].probeTemp;
rooms[r].probeTemp = readTemp(probes[r]) - drift[r];
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Room: ");
Serial.print(r);
Serial.print(" - Temperature: ");
Serial.println(rooms[r].probeTemp);
#endif
if (rooms[r].probeTemp != rooms[r].oldTemp) {
publishRoomStatus(r);
}
}
}
/* float to string
f is the float to turn into a string
p is the precision (number of decimals)
return a string representation of the float.
*/
char *f2s(float f, int p) {
char * pBuff; // use to remember which part of the buffer to use for dtostrf
const int iSize = 10; // number of bufffers, one for each float before wrapping around
static char sBuff[iSize][20]; // space for 20 characters including NULL terminator for each float
static int iCount = 0; // keep a tab of next place in sBuff to use
pBuff = sBuff[iCount]; // use this buffer
if (iCount >= iSize - 1) { // check for wrap
iCount = 0; // if wrapping start again and reset
}
else {
iCount++; // advance the counter
}
return dtostrf(f, 0, p, pBuff); // call the library function
}
void setup() {
#ifdef HVAC_TOSHIBA_DEBUG
Serial.begin(115200);
Serial.println("Sketch Started.");
#endif
IRpin = D0;
khz = 38;
halfPeriodicTime = 500 / khz;
pinMode(IRpin, OUTPUT);
for (int r = 0; r < NUMROOMS; r++) {
/*
struct roomHvac
{
int temp;
int state; // 0 - OFF 1 - ON
HvacFanMode fanHvac;
HvacMode modeHvac;
};
*/
rooms[r].temp = 22;
rooms[r].state = 0;
rooms[r].fanHvac = FAN_SPEED_AUTO;
rooms[r].modeHvac = HVAC_AUTO;
}
rooms[0].pin = D0;
pinMode(D0, OUTPUT);
rooms[1].pin = D1;
pinMode(D1, OUTPUT);
rooms[2].pin = D2;
pinMode(D2, OUTPUT);
rooms[3].pin = D3;
pinMode(D3, OUTPUT);
rooms[4].pin = D4;
pinMode(D4, OUTPUT);
connectWifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
if (client.connect(mqtt_id, mqtt_user, mqtt_password)) {
client.publish(outTopic, "hello world");
client.subscribe(inTopic);
}
for (int r = 0; r < 5; r++) {
publishRoomStatus(r);
}
}
void loop() {
float temp;
if (WiFi.status() != WL_CONNECTED)
{
connectWifi();
}
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long time = millis();
if (time > nextPingOn) {
tempTimer();
client.publish(pingTopic, "1");
nextPingOn = time + pingDelay;
}
}
void connectWifi()
{
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print("Connecting to " + *MY_SSID);
#endif
WiFi.begin(MY_SSID, MY_PWD);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
#ifdef HVAC_TOSHIBA_DEBUG
Serial.print(".");
#endif
}
#ifdef HVAC_TOSHIBA_DEBUG
Serial.println("");
Serial.print("Connected to ");
Serial.println(MY_SSID);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
#endif
}//end connect
float readTemp(byte *addressDs1820) {
byte i;
byte present = 0;
byte type_s;
byte data[12];
byte *addr;
float celsius;
/*
if ( !ds.search(addr)) {
ds.reset_search();
delay(250);
return;
}
*/
addr = addressDs1820;
if (OneWire::crc8(addr, 7) != addr[7]) {
return -1;
}
// the first ROM byte indicates which chip
switch (addr[0]) {
case 0x10:
type_s = 1;
break;
case 0x28:
type_s = 0;
break;
case 0x22:
type_s = 0;
break;
default:
return -1;
}
oneWire.reset();
oneWire.select(addr);
oneWire.write(0x44, 1); // start conversion, with parasite power on at the end
delay(1000); // maybe 750ms is enough, maybe not
// we might do a ds.depower() here, but the reset will take care of it.
present = oneWire.reset();
oneWire.select(addr);
oneWire.write(0xBE); // Read Scratchpad
for ( i = 0; i < 9; i++) { // we need 9 bytes
data[i] = oneWire.read();
}
// Convert the data to actual temperature
// because the result is a 16 bit signed integer, it should
// be stored to an "int16_t" type, which is always 16 bits
// even when compiled on a 32 bit processor.
int16_t raw = (data[1] << 8) | data[0];
if (type_s) {
raw = raw << 3; // 9 bit resolution default
if (data[7] == 0x10) {
// "count remain" gives full 12 bit resolution
raw = (raw & 0xFFF0) + 12 - data[6];
}
} else {
byte cfg = (data[4] & 0x60);
// at lower res, the low bits are undefined, so let's zero them
if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms
else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
//// default is 12 bit resolution, 750 ms conversion time
}
celsius = (float)raw / 16.0;
// fahrenheit = celsius * 1.8 + 32.0;
return celsius;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment