Haier Air Conditioner UART driver added (initial release)

some warnings removed
This commit is contained in:
2019-09-15 00:44:07 +03:00
parent 206355b3ec
commit 3fae70bd61
10 changed files with 535 additions and 46 deletions

View File

@@ -8,21 +8,21 @@
extern lan_status lanStatus;
extern PubSubClient mqttClient;
int abstractCh::publishTopic(char* topic, long value, char* subtopic)
int abstractCh::publishTopic(const char* topic, long value, const char* subtopic)
{
char valstr[16];
printUlongValueToStr(valstr, value);
return publishTopic(topic, valstr,subtopic);
};
int abstractCh::publishTopic(char* topic, float value, char* subtopic)
int abstractCh::publishTopic(const char* topic, float value, const char* subtopic)
{
char valstr[16];
printFloatValueToStr(value, valstr);
return publishTopic(topic, valstr,subtopic);
};
int abstractCh::publishTopic(char* topic, char * value, char* subtopic)
int abstractCh::publishTopic(const char* topic, const char * value, const char* subtopic)
{
char addrstr[MQTT_TOPIC_LENGTH];

View File

@@ -10,14 +10,14 @@ public:
virtual ~abstractCh(){};
virtual int Poll() = 0;
virtual int Setup() =0; //Should initialize hardware and reserve resources
virtual int Anounce () {};
virtual int Stop() {}; //Should free resources
virtual int Anounce () {return 0;};
virtual int Stop() {return 0;}; //Should free resources
virtual int Status() {return CST_UNKNOWN;}
protected:
virtual int publishTopic(char* topic, long value, char* subtopic = NULL);
virtual int publishTopic(char* topic, float value, char* subtopic = NULL );
virtual int publishTopic(char* topic, char * value, char* subtopic = NULL);
virtual int publishTopic(const char* topic, long value, const char* subtopic = NULL);
virtual int publishTopic(const char* topic, float value, const char* subtopic = NULL );
virtual int publishTopic(const char* topic, const char * value, const char* subtopic = NULL);
//friend Input;
};

View File

@@ -10,21 +10,21 @@
extern lan_status lanStatus;
extern PubSubClient mqttClient;
int abstractIn::publish(long value, char* subtopic)
int abstractIn::publish(long value, const char* subtopic)
{
char valstr[16];
printUlongValueToStr(valstr, value);
return publish(valstr,subtopic);
};
int abstractIn::publish(float value, char* subtopic)
int abstractIn::publish(float value, const char* subtopic)
{
char valstr[16];
printFloatValueToStr(value, valstr);
return publish(valstr,subtopic);
};
int abstractIn::publish(char * value, char* subtopic)
int abstractIn::publish(char * value, const char* subtopic)
{
char addrstr[MQTT_TOPIC_LENGTH];
if (in)

View File

@@ -9,8 +9,8 @@ public:
protected:
Input * in;
int publish(long value, char* subtopic = NULL);
int publish(float value, char* subtopic = NULL );
int publish(char * value, char* subtopic = NULL);
int publish(long value, const char* subtopic = NULL);
int publish(float value, const char* subtopic = NULL );
int publish(char * value, const char* subtopic = NULL);
friend Input;
};

View File

@@ -188,6 +188,7 @@ switch (cause) {
#endif
}
}
return 0;
}
#ifndef COUNTER_DISABLE
@@ -226,7 +227,7 @@ void Input::counterPoll() {
char addrstr[MQTT_TOPIC_LENGTH];
strncpy(addrstr,emit->valuestring,sizeof(addrstr));
if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,emit->valuestring);
sprintf(valstr, "%d", counterValue);
sprintf(valstr, "%ld", counterValue);
if (mqttClient.connected() && !ethernetIdleCount)
mqttClient.publish(addrstr, valstr);
setNextPollTime(millis() + DHT_POLL_DELAY_DEFAULT);

View File

@@ -34,6 +34,7 @@ e-mail anklimov@gmail.com
#include <PubSubClient.h>
#include "modules/out_spiled.h"
#include "modules/out_ac.h"
//Commands
const char ON_P[] PROGMEM = "ON";
@@ -48,11 +49,11 @@ const char DECREASE_P[] PROGMEM = "DECREASE";
const char TRUE_P[] PROGMEM = "TRUE";
const char FALSE_P[] PROGMEM = "FALSE";
const char HEAT_P[] PROGMEM = "HEAT";
const char COOL_P[] PROGMEM = "COOL";
const char AUTO_P[] PROGMEM = "AUTO";
const char FAN_ONLY_P[] PROGMEM = "FAN_ONLY";
const char DRY_P[] PROGMEM = "DRY";
char HEAT_P[] PROGMEM = "HEAT";
char COOL_P[] PROGMEM = "COOL";
char AUTO_P[] PROGMEM = "AUTO";
char FAN_ONLY_P[] PROGMEM = "FAN_ONLY";
char DRY_P[] PROGMEM = "DRY";
// SubTopics
const char SET_P[] PROGMEM = "set";
@@ -81,7 +82,7 @@ static unsigned long lastctrl = 0;
static aJsonObject *lastobj = NULL;
int txt2cmd(char *payload) {
int cmd = -1;
int cmd = CMD_UNKNOWN;
// Check for command
if (*payload == '-' || (*payload >= '0' && *payload <= '9')) cmd = CMD_NUM;
@@ -115,7 +116,7 @@ int txt2subItem(char *payload) {
// Check for command
if (strcmp_P(payload, SET_P) == 0) cmd = S_SET;
else if (strcmp_P(payload, CMD_P) == 0) cmd = S_CMD;
// else if (strcmp_P(payload, MODE_P) == 0) cmd = S_MODE;
else if (strcmp_P(payload, MODE_P) == 0) cmd = S_MODE;
else if (strcmp_P(payload, HSV_P) == 0) cmd = S_HSV;
else if (strcmp_P(payload, RGB_P) == 0) cmd = S_RGB;
else if (strcmp_P(payload, FAN_P) == 0) cmd = S_FAN;
@@ -124,7 +125,7 @@ int txt2subItem(char *payload) {
else if (strcmp_P(payload, TEMP_P) == 0) cmd = S_TEMP;
else if (strcmp_P(payload, POWER_P) == 0) cmd = S_POWER;
else if (strcmp_P(payload, VOL_P) == 0) cmd = S_VOL;
else if (strcmp_P(payload, HEAT_P) == 0) cmd = S_HEAT; */
*/
return cmd;
}
@@ -152,7 +153,14 @@ void Item::Parse() {
#ifndef SPILED_DISABLE
case CH_SPILED:
driver = new out_SPILed (this);
// debugSerial<<F("SPILED driver created")<<endl;
debugSerial<<F("SPILED driver created")<<endl;
break;
#endif
#ifndef AC_DISABLE
case CH_AC:
driver = new out_AC (this);
debugSerial<<F("AC driver created")<<endl;
break;
#endif
default: ;
@@ -178,7 +186,7 @@ Item::~Item()
if (driver)
{
delete driver;
// debugSerial<<F("Driver destroyed")<<endl;
debugSerial<<F("Driver destroyed")<<endl;
}
}
@@ -443,7 +451,7 @@ int Item::Ctrl(short cmd, short n, int *Parameters, boolean send, int suffixCode
int iaddr = getArg();
int chActive =isActive();
HSVstore st;
CHstore st;
switch (cmd) {
int t;
case CMD_TOGGLE:
@@ -870,7 +878,7 @@ int Item::Ctrl(short cmd, short n, int *Parameters, boolean send, int suffixCode
}
int Item::isActive() {
HSVstore st;
CHstore st;
int val = 0;
if (!isValid()) return -1;
@@ -1422,7 +1430,7 @@ int Item::SendStatus(int sendFlags) {
if (sendFlags & SEND_PARAMETERS)
{
// Preparing parameters payload //////////
HSVstore st;
CHstore st;
//retrive stored values
st.aslong = getVal();
switch (itemType) {
@@ -1554,3 +1562,7 @@ int Item::SendStatus(int sendFlags) {
return 1;
}
}
/////////////////////////////////////////

View File

@@ -27,8 +27,10 @@ e-mail anklimov@gmail.com
#define S_HSV 3
#define S_RGB 4
#define S_FAN 5
#define S_MODE 6
#define S_ADDITIONAL 64
/*
#define S_MODE 4
#define S_RPM 11
#define S_TEMP 3
#define S_SETPOINT 5
@@ -46,7 +48,7 @@ e-mail anklimov@gmail.com
#define CH_GROUP 7 //Group pseudochannel
#define CH_VCTEMP 8 //Vacom PID regulator
#define CH_VC 9 //Vacom modbus motor regulator
#define CH_AC_HAIER 10 //AC Haier
#define CH_AC 10 //AC Haier
#define CH_SPILED 11
#define CH_WHITE 127//
@@ -112,27 +114,31 @@ int txt2cmd (char * payload);
typedef union
{
long int aslong;
float asfloat;
struct
{ uint8_t v;
uint8_t s;
uint16_t h:15;
uint16_t hsv_flag:1;
};
struct
{
int16_t hsv_flag:1;
int16_t h:15;
int8_t s;
int8_t v;
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t w:7;
uint8_t rgb_flag:1;
};
} HSVstore;
} CHstore;
/*
typedef union
{
long int aslong;
struct
{
int8_t r;
int8_t g;
int8_t b;
int8_t w;
};
} RGBWstore;
*/
#pragma pack(pop)
class Item

View File

@@ -1142,7 +1142,7 @@ void cmdFunctionPwd(int arg_cnt, char **args)
}
void cmdFunctionSetMac(int arg_cnt, char **args) {
if (sscanf(args[1], "%x:%x:%x:%x:%x:%x%с", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) < 6) {
if (sscanf(args[1], "%x:%x:%x:%x:%x:%x%c", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) < 6) {
debugSerial<<F("could not parse: ")<<args[1];
return;
}
@@ -1169,7 +1169,7 @@ void saveFlash(short n, char *str) {
// write the data to EEPROM
short res = EEPROM.commitReset();
Serial.println((res) ? "EEPROM Commit OK" : "Commit failed");
#endif.
#endif
}
int loadFlash(short n, char *str, short l) {
@@ -1188,7 +1188,7 @@ void saveFlash(short n, IPAddress& ip) {
// write the data to EEPROM
short res = EEPROM.commitReset();
Serial.println((res) ? "EEPROM Commit OK" : "Commit failed");
#endif.
#endif
}
int ipLoadFromFlash(short n, IPAddress &ip) {

433
lighthub/modules/out_ac.cpp Normal file
View File

@@ -0,0 +1,433 @@
#ifndef AC_DISABLE
#include "modules/out_ac.h"
#include "Arduino.h"
#include "options.h"
#include "Streaming.h"
#include "item.h"
#define AC_Serial Serial3
#define INTERVAL_AC_POLLING 5000L
static int driverStatus = CST_UNKNOWN;
static int fresh =0;
static int power = 0;
static int swing =0;
static int lock_rem =0;
static int cur_tmp = 0;
static int set_tmp = 0;
static int fan_spd = 0;
static int mode = 0;
long prevPolling = 0;
byte inCheck = 0;
byte qstn[] = {255,255,10,0,0,0,0,0,1,1,77,1,90}; // Команда опроса
byte data[37] = {}; //Массив данных
byte on[] = {255,255,10,0,0,0,0,0,1,1,77,2,91}; // Включение кондиционера
byte off[] = {255,255,10,0,0,0,0,0,1,1,77,3,92}; // Выключение кондиционера
// byte lock[] = {255,255,10,0,0,0,0,0,1,3,0,0,14}; // Блокировка пульта
//Extended subItem set
const char LOCK_P[] PROGMEM = "lock";
const char QUIET_P[] PROGMEM = "queit";
const char SWING_P[] PROGMEM = "swing";
const char RAW_P[] PROGMEM = "raw";
extern char HEAT_P[] PROGMEM;
extern char COOL_P[] PROGMEM;
extern char AUTO_P[] PROGMEM;
extern char FAN_ONLY_P[] PROGMEM;
extern char DRY_P[] PROGMEM;
void out_AC::InsertData(byte data[], size_t size){
char s_mode[10];
set_tmp = data[B_SET_TMP]+16;
cur_tmp = data[B_CUR_TMP];
mode = data[B_MODE];
fan_spd = data[B_FAN_SPD];
swing = data[B_SWING];
power = data[B_POWER];
lock_rem = data[B_LOCK_REM];
fresh = data[B_FRESH];
/////////////////////////////////
if (fresh & 0x01)
publishTopic(item->itemArr->name, "ON","/fresh");
else publishTopic(item->itemArr->name, "OFF","/fresh");
/////////////////////////////////
if (lock_rem == 0x80){
publishTopic(item->itemArr->name, "ON","/lock");
}
if (lock_rem == 0x00){
publishTopic(item->itemArr->name, "OFF","/lock");
}
/////////////////////////////////
/*
if (power == 0x01 || power == 0x11){
publishTopic(item->itemArr->name,"Power", "on");
}
if (power == 0x00 || power == 0x10){
publishTopic(item->itemArr->name,"Power", "off");
}
*/
Serial.print ("Power=");
Serial.println(power);
if (power & 0x08)
publishTopic(item->itemArr->name, "ON", "/quiet");
else publishTopic(item->itemArr->name, "OFF" , "/quiet");
if (power == 3 || power == 2)
publishTopic(item->itemArr->name, "on","/compressor");
else
publishTopic(item->itemArr->name, "off","/compressor");
publishTopic(item->itemArr->name, (long) swing,"/swing");
publishTopic(item->itemArr->name, (long) fan_spd,"/fan");
/////////////////////////////////
/*
if (swing == 0x00){
publishTopic(item->itemArr->name, "off","swing");
}
if (swing == 0x01){
publishTopic(item->itemArr->name, "ud","swing");
}
if (swing == 0x02){
publishTopic(item->itemArr->name, "lr","swing");
}
if (swing == 0x03){
publishTopic(item->itemArr->name, "all","swing");
}
/////////////////////////////////
if (fan_spd == 0x00){
publishTopic(item->itemArr->name, "max","fan");
}
if (fan_spd == 0x01){
publishTopic(item->itemArr->name, "mid","fan");
}
if (fan_spd == 0x02){
publishTopic(item->itemArr->name, "min","fan");
}
if (fan_spd == 0x03){
publishTopic(item->itemArr->name, "auto","fan");
}
*/
/////////////////////////////////
publishTopic(item->itemArr->name,(long)set_tmp,"/set");
publishTopic(item->itemArr->name, (long)cur_tmp, "/temp");
////////////////////////////////////
s_mode[0]='\0';
if (mode == 0x00){
strcpy_P(s_mode,AUTO_P);
}
else if (mode == 0x01){
strcpy_P(s_mode,COOL_P);
}
else if (mode == 0x02){
strcpy_P(s_mode,HEAT_P);
}
else if (mode == 0x03){
strcpy_P(s_mode,FAN_ONLY_P);
}
else if (mode == 0x04){
strcpy_P(s_mode,DRY_P);
}
publishTopic(item->itemArr->name, (long) mode, "/mode");
if (power & 0x01)
publishTopic(item->itemArr->name, s_mode,"/cmd");
else publishTopic(item->itemArr->name, "OFF","/cmd");
String raw_str;
char raw[75];
for (int i=0; i < 37; i++){
if (data[i] < 10){
raw_str += "0";
raw_str += String(data[i], HEX);
} else {
raw_str += String(data[i], HEX);
}
}
raw_str.toUpperCase();
raw_str.toCharArray(raw,75);
publishTopic(item->itemArr->name, raw,"/raw");
Serial.println(raw);
///////////////////////////////////
}
byte getCRC(byte req[], size_t size){
byte crc = 0;
for (int i=2; i < size; i++){
crc += req[i];
}
return crc;
}
void SendData(byte req[], size_t size){
AC_Serial.write(req, size - 1);
AC_Serial.write(getCRC(req, size-1));
AC_Serial.flush();
}
inline unsigned char toHex( char ch ){
return ( ( ch >= 'A' ) ? ( ch - 'A' + 0xA ) : ( ch - '0' ) ) & 0x0F;
}
int out_AC::Setup()
{
Serial.println("AC Init");
AC_Serial.begin(9600);
driverStatus = CST_INITIALIZED;
return 1;
}
int out_AC::Stop()
{
Serial.println("AC De-Init");
driverStatus = CST_UNKNOWN;
return 1;
}
int out_AC::Status()
{
return driverStatus;
}
int out_AC::isActive()
{
return (power & 1);
}
int out_AC::Poll()
{
//debugSerial<<".";
long now = millis();
if (now - prevPolling > INTERVAL_AC_POLLING) {
prevPolling = now;
Serial.println ("Polling");
SendData(qstn, sizeof(qstn)/sizeof(byte)); //Опрос кондиционера
}
delay(100);
if(AC_Serial.available() > 0){
AC_Serial.readBytes(data, 37);
while(AC_Serial.available()){
delay(2);
AC_Serial.read();
}
if (data[36] != inCheck){
inCheck = data[36];
InsertData(data, 37);
}
}
return 1;
};
int out_AC::Ctrl(short cmd, short n, int * Parameters, boolean send, int suffixCode, char* subItem)
{
// Some additional Subitems
if (strcmp_P(subItem, LOCK_P) == 0) suffixCode = S_LOCK;
else if (strcmp_P(subItem, SWING_P) == 0) suffixCode = S_SWING;
else if (strcmp_P(subItem, QUIET_P) == 0) suffixCode = S_QUIET;
else if (strcmp_P(subItem, RAW_P) == 0) suffixCode = S_RAW;
data[B_POWER] = power;
// debugSerial<<F(".");
switch(suffixCode)
{
case S_SET:
set_tmp = Parameters[0]-16;
if (set_tmp >= 0 && set_tmp <= 30)
{
data[B_SET_TMP] = set_tmp;
}
break;
case S_CMD:
switch (cmd)
{
case CMD_ON:
data[B_POWER] |= 1;
SendData(on, sizeof(on)/sizeof(byte));
return 1;
break;
case CMD_OFF:
case CMD_HALT:
data[B_POWER] = 0;
SendData(off, sizeof(off)/sizeof(byte));
return 1;
break;
case CMD_AUTO:
data[B_MODE] = 0;
data[B_POWER] |= 1;
break;
case CMD_COOL:
data[B_MODE] = 1;
data[B_POWER] |= 1;
break;
case CMD_HEAT:
data[B_MODE] = 2;
data[B_POWER] |= 1;
break;
case CMD_DRY:
data[B_MODE] = 4;
data[B_POWER] |= 1;
break;
case CMD_FAN:
data[B_MODE] = 3;
data[B_POWER] |= 1;
break;
case CMD_UNKNOWN:
return -1;
break;
}
break;
case S_FAN:
data[B_FAN_SPD] = Parameters[0];
break;
case S_MODE:
data[B_MODE] = Parameters[0];
break;
case S_LOCK:
switch (cmd)
{
case CMD_ON:
data[B_LOCK_REM] = 80;
break;
case CMD_OFF:
data[B_LOCK_REM] = 0;
break;
}
break;
case S_SWING:
data[B_SWING] = Parameters[0];
break;
case S_QUIET:
switch (cmd)
{
case CMD_ON:
data[B_POWER] |= 8;
break;
case CMD_OFF:
data[B_POWER] &= ~8;
break;
}
break;
case S_RAW:
/*
{
char buf[75];
char hexbyte[3] = {0};
strPayload.toCharArray(buf, 75);
int octets[sizeof(buf) / 2] ;
for (int i=0; i < 76; i += 2){
hexbyte[0] = buf[i] ;
hexbyte[1] = buf[i+1] ;
data[i/2] = (toHex(hexbyte[0]) << 4) | toHex(hexbyte[1]);
AC_Serial.write(data, 37);
AC_Serial.flush();
publishTopic("RAW", buf);
}
*/
break;
case S_NOTFOUND:
return -1;
}
/*
//////////
if (strTopic == "/myhome/in/Conditioner/Fan_Speed"){
if (strPayload == "max"){
data[B_FAN_SPD] = 0;
}
if (strPayload == "mid"){
data[B_FAN_SPD] = 1;
}
if (strPayload == "min"){
data[B_FAN_SPD] = 2;
}
if (strPayload == "auto"){
data[B_FAN_SPD] = 3;
}
}
////////
if (strTopic == "/myhome/in/Conditioner/Swing"){
if (strPayload == "off"){
data[B_SWING] = 0;
}
if (strPayload == "ud"){
data[B_SWING] = 1;
}
if (strPayload == "lr"){
data[B_SWING] = 2;
}
if (strPayload == "all"){
data[B_SWING] = 3;
}
}
////////
if (strTopic == "/myhome/in/Conditioner/Lock_Remote"){
if (strPayload == "true"){
data[B_LOCK_REM] = 80;
}
if (strPayload == "false"){
data[B_LOCK_REM] = 0;
}
}
////////
if (strTopic == "/myhome/in/Conditioner/Power"){
if (strPayload == "off" || strPayload == "false" || strPayload == "0"){
SendData(off, sizeof(off)/sizeof(byte));
return;
}
if (strPayload == "on" || strPayload == "true" || strPayload == "1"){
SendData(on, sizeof(on)/sizeof(byte));
return;
}
if (strPayload == "quiet"){
data[B_POWER] = 9;
}
}
*/
////////
//if (strTopic == "/myhome/in/Conditioner/RAW")
data[B_CMD] = 0;
data[9] = 1;
data[10] = 77;
data[11] = 95;
SendData(data, sizeof(data)/sizeof(byte));
return 1;
}
#endif

37
lighthub/modules/out_ac.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#ifndef AC_DISABLE
#include <abstractout.h>
#define LEN_B 37
#define B_CUR_TMP 13 //Текущая температура
#define B_CMD 17 // 00-команда 7F-ответ ???
#define B_MODE 23 //04 - DRY, 01 - cool, 02 - heat, 00 - smart 03 - вентиляция
#define B_FAN_SPD 25 //Скорость 02 - min, 01 - mid, 00 - max, 03 - auto
#define B_SWING 27 //01 - верхний и нижний предел вкл. 00 - выкл. 02 - левый/правый вкл. 03 - оба вкл
#define B_LOCK_REM 28 //80 блокировка вкл. 00 - выкл
#define B_POWER 29 //on/off 01 - on, 00 - off (10, 11)-Компрессор??? 09 - QUIET
#define B_FRESH 31 //fresh 00 - off, 01 - on
#define B_SET_TMP 35 //Установленная температура
#define S_LOCK S_ADDITIONAL+1
#define S_QUIET S_ADDITIONAL+2
#define S_SWING S_ADDITIONAL+3
#define S_RAW S_ADDITIONAL+4
extern void modbusIdle(void) ;
class out_AC : public abstractOut {
public:
out_AC(Item * _item):abstractOut(_item){};
int Setup() override;
int Poll() override;
int Stop() override;
int Status() override;
int isActive() override;
int Ctrl(short cmd, short n=0, int * Parameters=NULL, boolean send=true, int suffixCode=0, char* subItem=NULL) override;
protected:
void InsertData(byte data[], size_t size);
};
#endif