From 6b628d6795e1984399de5e5e47ae0a271770c4bb Mon Sep 17 00:00:00 2001 From: Andrey Klimov Date: Mon, 2 Oct 2017 14:13:45 +0300 Subject: [PATCH] Initial commit --- dmx.cpp | 126 ++++++ dmx.h | 25 ++ inputs.cpp | 127 ++++++ inputs.h | 77 ++++ item.cpp | 553 ++++++++++++++++++++++++++ item.h | 102 +++++ lighthub.ino | 1061 ++++++++++++++++++++++++++++++++++++++++++++++++++ owSwitch.cpp | 438 +++++++++++++++++++++ owSwitch.h | 12 + owTerm.cpp | 177 +++++++++ owTerm.h | 70 ++++ utils.cpp | 41 ++ utils.h | 5 + 13 files changed, 2814 insertions(+) create mode 100644 dmx.cpp create mode 100644 dmx.h create mode 100644 inputs.cpp create mode 100644 inputs.h create mode 100644 item.cpp create mode 100644 item.h create mode 100644 lighthub.ino create mode 100644 owSwitch.cpp create mode 100644 owSwitch.h create mode 100644 owTerm.cpp create mode 100644 owTerm.h create mode 100644 utils.cpp create mode 100644 utils.h diff --git a/dmx.cpp b/dmx.cpp new file mode 100644 index 0000000..76e0f1c --- /dev/null +++ b/dmx.cpp @@ -0,0 +1,126 @@ +#include "dmx.h" +//#include +//#include +//#include + +uint8_t * DMXin = NULL; +int D_State=0; +unsigned long D_checkT=0; + +Artnet *artnet = NULL; +aJsonObject *dmxArr = NULL; + +void DMXImmediateUpdate(short tch,short r, short g, short b, short w) { +//Here only safe re-interable code for quick passthrow between DMX IN and DMX OUT + if (dmxArr && (dmxArr->type==aJson_Array)) + + { + char* itemname = aJson.getArrayItem(dmxArr,tch)->valuestring; + itemCtrl2(itemname,r,g,b,w); + } +} + +void DMXSemiImmediateUpdate(short tch,short trh, int val) +{ +//Here any code for passthrow between DMX IN and DMX OUT in idle state +} + +void DMXput(void) +{ +int t; +for (short tch=0; tch<=3 ; tch++) + { + short base = tch*4; + DMXImmediateUpdate(tch,DMXin[base],DMXin[base+1],DMXin[base+2],DMXin[base+3]); + } +}; + + +void DMXUpdate(void) +{ +int t; +for (short tch=0; tch<=3 ; tch++) + { + short base = tch*4; + bool updated = 0; + + for (short trh=0; trh<4 ; trh++) + if ((t=DMXSerial.read(base+trh+1)) != DMXin[base+trh]) + { + D_State |= (1< "); Serial.print(t);Serial.println(); + DMXin[base+trh]=t; + //DMXImmediateUpdate(tch,trh,t); + //break; + } + + if (updated) + { + DMXImmediateUpdate(tch,DMXin[base],DMXin[base+1],DMXin[base+2],DMXin[base+3]); + D_checkT=millis()+D_CHECKT; + } + } + //Serial.print(D_State,BIN);Serial.println(); +} + + + void DMXCheck(void) +{ +// CHSV hsv; +// CRGB rgb; + +short t,tch; +//Here code for semi-immediate update + for (t=1,tch=0; t<=8 ; t<<=1,tch++) + if (D_State & t) + { + // Serial.print(D_State,BIN);Serial.print(":"); + D_State &= ~t; + for (short trh=0; trh<4 ; trh++) + DMXSemiImmediateUpdate(tch,trh,DMXin[tch*4+trh]); + + } + +if ((millis()setArtDmxCallback(onDmxFrame); +} + +void ArtnetSetup() +{ + if (!artnet) artnet = new Artnet; + // this will be called for each packet received + if (artnet) artnet->setArtDmxCallback(onDmxFrame); +} diff --git a/dmx.h b/dmx.h new file mode 100644 index 0000000..391d5d2 --- /dev/null +++ b/dmx.h @@ -0,0 +1,25 @@ +#define D_UPDATED1 1 +#define D_UPDATED2 2 +#define D_UPDATED3 4 +#define D_UPDATED4 8 +#define D_CHECKT 300 + +#define MAX_CHANNELS 60 +//define MAX_IN_CHANNELS 16 + +//#define DMX_OUT_PIN 3 + +#include +#include +#include +#include "aJSON.h" + +extern aJsonObject *dmxArr; +extern Artnet *artnet; + + +void DMXput(void); +void DMXinSetup(int channels); +void ArtnetSetup(); +void DMXCheck(void); +int itemCtrl2(char* name,int r,int g, int b, int w); diff --git a/inputs.cpp b/inputs.cpp new file mode 100644 index 0000000..312f40f --- /dev/null +++ b/inputs.cpp @@ -0,0 +1,127 @@ +#include "inputs.h" +#include "aJSON.h" +#include "item.h" +#include + +extern PubSubClient client; + +Input::Input(char * name) //Constructor +{ + if (name) + inputObj= aJson.getObjectItem(inputs, name); + else inputObj=NULL; + + Parse(); +} + + +Input::Input(int pin) //Constructor +{ + // TODO +} + + + Input::Input(aJsonObject * obj) //Constructor +{ + inputObj= obj; + Parse(); + +} + + +boolean Input::isValid () +{ + return (pin && store); +} + +void Input::Parse() +{ + store = NULL; + inType = 0; + pin = 0; + + if (inputObj && (inputObj->type==aJson_Object)) + { + aJsonObject * s; + + s = aJson.getObjectItem(inputObj,"T"); + if (s) inType = s->valueint; + + pin = atoi(inputObj->name); + + + s = aJson.getObjectItem(inputObj,"S"); + if (!s) { Serial.print("In: ");Serial.print(pin);Serial.print("/");Serial.println(inType); + aJson.addNumberToObject(inputObj,"S", 0); + s = aJson.getObjectItem(inputObj,"S"); + } + + if (s) store= (inStore *) &s->valueint; + } +} + +int Input::Pool () +{ + boolean v; + if (!isValid()) return -1; + + + if (inType & IN_ACTIVE_HIGH) + { pinMode(pin, INPUT); + v = (digitalRead(pin)==HIGH); + } + else + { pinMode(pin, INPUT_PULLUP); + v = (digitalRead(pin)==LOW); + } + if (v!=store->cur) // value changed + { + if (store->bounce) store->bounce--; + else //confirmed change + { + Changed(v); + store->cur=v; + } + } + else // no change + store->bounce=3; + return 0; +} + +void Input::Changed (int val) +{ + Serial.print(pin);Serial.print("=");Serial.println(val); + aJsonObject * item = aJson.getObjectItem(inputObj,"item"); + aJsonObject * scmd = aJson.getObjectItem(inputObj,"scmd"); + aJsonObject * rcmd = aJson.getObjectItem(inputObj,"rcmd"); + aJsonObject * emit = aJson.getObjectItem(inputObj,"emit"); + + if (emit) + { + + if (val) + { //send set command + if (scmd) client.publish(emit->valuestring,scmd->valuestring); else client.publish(emit->valuestring,"ON"); + } + else + { //send reset command + if (rcmd) client.publish(emit->valuestring,rcmd->valuestring); else client.publish(emit->valuestring,"OFF"); + } + } + + if (item) + { + Item it(item->valuestring); + if (it.isValid()) + { + if (val) + { //send set command + if (scmd) it.Ctrl(txt2cmd(scmd->valuestring),0,NULL,true); else it.Ctrl(CMD_ON,0,NULL,true); + } + else + { //send reset command + if (rcmd) it.Ctrl(txt2cmd(scmd->valuestring),0,NULL,true); else it.Ctrl(CMD_OFF,0,NULL,true); + } + } + } +} diff --git a/inputs.h b/inputs.h new file mode 100644 index 0000000..6500017 --- /dev/null +++ b/inputs.h @@ -0,0 +1,77 @@ +#include "aJSON.h" + +#define IN_ACTIVE_HIGH 128 // High level = PUSHED/ CLOSED/ ON othervise :Low Level +#define IN_ANALOG 64 // Analog input +#define IN_RE 32 // Rotary Encoder (for further use) + +#define IN_PUSH_ON 0 // PUSH - ON, Release - OFF (ovverrided by pcmd/rcmd) - DEFAULT +#define IN_PUSH_TOGGLE 1 // Every physicall push toggle logical switch on/off + + +// in syntaxis +// "pin": { "T":"N", "emit":"out_emit", item:"out_item", "scmd": "ON,OFF,TOGGLE,INCREASE,DECREASE", "rcmd": "ON,OFF,TOGGLE,INCREASE,DECREASE", "rcmd":"repeat_command" } + +// +//Switch/Restore all +//"pin": { "T":"1", "emit":"/all", item:"local_all", "scmd": "OFF", "rcmd": "RESTORE"} + +// +//Normal (not button) Switch (toggled mode) +//"pin": { "T":"1", "emit":"/light1", item:"light1", "scmd": "TOGGLE", "rcmd": "TOGGLE"} +// or +// "pin": { "T":"xx", "emit":"/light1", item:"light1"} + +//Normal (not button) Switch +//"pin": { "T":"0", "emit":"/light1", item:"light1", "scmd": "ON", "rcmd": "OFF"} +// or +// "pin": { "T":"0", "emit":"/light1", item:"light1"} +//or +// "pin": { "emit":"/light1", item:"light1"} + +//1-Button dimmer +//"pin": { "T":"1", "emit":"/light1", item:"light1", "scmd": "ON", srcmd:"INCREASE",rrcmd:"DECREASE", "rcmd": "OFF"} +// or +// "pin": { "T":"xx", "emit":"/light1", item:"light1"} + +//2-Buttons dimmer +//"pin1": { "T":"0", "emit":"/light1", item:"light1", "scmd": "ON", repcmd:"INCREASE"} +//"pin2": { "T":"0", "emit":"/light1", item:"light1", "scmd": "OFF", repcmd:"INCREASE"} + +extern aJsonObject *inputs; + + +typedef union +{ + long int aslong; + struct + { + int8_t reserve; + int8_t logicState; + int8_t bounce; + int8_t cur; + }; +} inStore; + +class Input +{ + public: + aJsonObject *inputObj; + uint8_t inType; + uint8_t pin; + inStore * store; + + Input(int pin); + Input(aJsonObject * obj); + Input(char * name); + + boolean isValid (); + void Changed (int val); + + int Pool (); + protected: + void Parse(); +}; + + + + diff --git a/item.cpp b/item.cpp new file mode 100644 index 0000000..600fedf --- /dev/null +++ b/item.cpp @@ -0,0 +1,553 @@ +#include "item.h" +#include "aJSON.h" +#include +#include "FastLED.h" +#include +#include + + + +extern int modbusSet(int addr, uint16_t _reg, int _mask, uint16_t value); +extern PubSubClient client; +//extern const char* outprefix; +const char outprefix[] PROGMEM = "/myhome/s_out/"; + +static unsigned long lastctrl = 0; +static aJsonObject * lastobj = NULL; + +int txt2cmd (char * payload) +{ + int cmd=0; + + // Check for command + if (strcmp(payload,"ON")==0) cmd=CMD_ON; + else if (strcmp(payload,"OFF")==0) cmd=CMD_OFF; + else if (strcmp(payload,"REST")==0) cmd=CMD_RESTORE; + else if (strcmp(payload,"TOGGLE")==0) cmd=CMD_TOGGLE; + else if (strcmp(payload,"HALT")==0) cmd=CMD_HALT; + return cmd; +} + +Item::Item(char * name) //Constructor +{ + if (name) + itemArr= aJson.getObjectItem(items, name); + else itemArr=NULL; + + if (isValid()) + { + // Todo - avoid static enlarge for every types + for (int i=aJson.getArraySize(itemArr);i<4;i++) aJson.addItemToArray(itemArr,aJson.createItem(int(0))); //Enlarge item to 4 elements. VAL=int if no other definition in conf + + + itemType = aJson.getArrayItem(itemArr,I_TYPE)->valueint; + itemArg = aJson.getArrayItem(itemArr,I_ARG); + itemVal = aJson.getArrayItem(itemArr,I_VAL); + + + Serial.print(F(" Item:")); + Serial.println(name); + Serial.print(itemType);Serial.print(":");Serial.println(getArg()); + } +} + +uint8_t Item::getCmd() +{ + aJsonObject *t = aJson.getArrayItem(itemArr,I_CMD); + if (t) + return t->valueint; + else return -1; +} + + +void Item::setCmd(uint8_t cmd) +{ + aJsonObject *t = aJson.getArrayItem(itemArr,I_CMD); + if (t) + t->valueint=cmd; +} + +int Item::getArg() //Return arg int or first array element if Arg is array +{ if (!itemArg) return -1; + if (itemArg->type==aJson_Int) return itemArg->valueint; + else if (itemArg->type==aJson_Array) return aJson.getArrayItem(itemArg,0)->valueint; + else return -2; +} +/* +int Item::getVal(short n) //Return Val from Value array +{ if (!itemVal) return -1; + else if (itemVal->type==aJson_Array) + { + aJsonObject *t = aJson.getArrayItem(itemVal,n); + if (t) return t->valueint; + else return -3; + } + else return -2; +} +*/ + +long int Item::getVal() //Return Val if val is int or first elem of Value array +{ if (!itemVal) return -1; + if (itemVal->type==aJson_Int) return itemVal->valueint; + else if (itemVal->type==aJson_Array) + { + aJsonObject *t = aJson.getArrayItem(itemVal,0); + if (t) return t->valueint; + else return -3; + } + else return -2; +} +/* +void Item::setVal(short n, int par) // Only store if VAL is array defined in config to avoid waste of RAM +{ + if (!itemVal || itemVal->type!=aJson_Array) return; + Serial.print(F(" Store p="));Serial.print(n);Serial.print(F(" Val="));Serial.println(par); + for (int i=aJson.getArraySize(itemVal);i<=n;i++) aJson.addItemToArray(itemVal,aJson.createItem(int(0))); //Enlarge array of Values + + aJsonObject *t = aJson.getArrayItem(itemVal,n); + if (t) t->valueint=par; +} +*/ + +void Item::setVal(long int par) // Only store if VAL is int (autogenerated or config-defined) +{ + if (!itemVal || itemVal->type!=aJson_Int) return; + Serial.print(F(" Store "));Serial.print(F(" Val="));Serial.println(par); + itemVal->valueint=par; +} + + +boolean Item::isValid () +{ + return (itemArr && (itemArr->type==aJson_Array)); +} + + + +/* +void Item::copyPar (aJsonObject *itemV) +{ int n=aJson.getArraySize(itemV); + //for (int i=aJson.getArraySize(itemVal);ivalueint); +} +*/ + +boolean Item::getEnableCMD(int delta) +{ + return ((millis()-lastctrl>(unsigned long) delta) );//|| (itemArr!=lastobj)); +} + +int Item::Ctrl(short cmd, short n, int * Par, boolean send) +{ + + Serial.print(F("Cmd="));Serial.println(cmd); + //va_list vl; + + int _Par[3]={0,0,0}; + if (Par==NULL) Par=_Par; + + int iaddr=getArg(); + HSVstore st; + + + //Store Parameter(s) into json VAL + + + + switch (cmd) + { + + case CMD_TOGGLE: + switch (getCmd()) + { + case CMD_ON: + case CMD_SET: + cmd=CMD_OFF; + break; + case CMD_OFF: + case CMD_HALT: + cmd=CMD_ON; + break; + }//switch old cmd + + break; + + case CMD_RESTORE: + if (itemType!=CH_GROUP) //individual threating of channels + switch (getCmd()) + { case CMD_HALT: + // Serial.print("LastCmd:");Serial.println(t); + cmd=CMD_ON; + break; + }//switch old cmd + } //switch cmd + + switch (cmd) + { + case 0: //no command + + setCmd(CMD_SET); + + switch (itemType) + { + case CH_GROUP: + case CH_RGBW: //only if configured VAL array + st.h=Par[0]; + st.s=Par[1]; + st.v=Par[2]; + setVal(st.aslong); + break; + + case CH_DIMMER: //Everywhere, in flat VAL + case CH_MODBUS: + case CH_THERMO: + case CH_VC: + case CH_VCTEMP: + setVal(Par[0]); + + }//itemtype + + lastctrl=millis(); //last controlled object ond timestamp update + lastobj =itemArr; + + break; + + case CMD_ON: //retrive stored values + if (getCmd()!=CMD_ON) + { + st.aslong=getVal();//Serial.print("Returned getVal: ");Serial.println(st.aslong); + if (st.aslong>=0) + { + Par[0]=st.h; + Par[1]=st.s; + Par[2]=st.v; + } + + for (short i=0;i<3 ;i++) + { + Serial.print(F("Restored: "));Serial.print(i);Serial.print("=");Serial.println(Par[i]); + } + + + setCmd(cmd); + } + else + { //Double ON - apply special preset + switch (itemType) + { + case CH_RGBW: + Serial.println(F("White: ")); + itemType=CH_WHITE; + // Par[2]=getVal(2); + // if (Par[2]<0) + Par[2]=100; + break; + } + } + + break; + + case CMD_OFF: + + Par[0]=0;Par[1]=0;Par[2]=0; + setCmd(cmd); + break; + + case CMD_HALT: + if (getCmd()!=CMD_OFF) + { + Par[0]=0;Par[1]=0;Par[2]=0; + setCmd(cmd); + Serial.print(itemType);Serial.println(" Halted"); + } + + + + }//switch cmd + +/* + Serial.print("go: "); + for (short i=0;i<3 ;i++) + { + Serial.print(i);Serial.print("=");Serial.println(Par[i]); + } + */ + + if (send) SendCmd(cmd,n,Par); + + switch (itemType) + { + case CH_DIMMER: //Dimmed light + + DmxSimple.write(iaddr, map(Par[0],0,100,0,255)); + + break; + + case CH_RGBW: //Colour RGBW + { + int k; + DmxSimple.write(iaddr+3, k=map((100-Par[1])*Par[2],0,10000,0,255)); + Serial.print(F("W:"));Serial.println(k); + } + case CH_RGB: // RGB + + { + + + CRGB rgb= CHSV(map(Par[0],0,365,0,255),map(Par[1],0,100,0,255),map(Par[2],0,100,0,255)); + + DmxSimple.write(iaddr, rgb.r); + DmxSimple.write(iaddr+1, rgb.g); + DmxSimple.write(iaddr+2, rgb.b); + + + break; } + + case CH_WHITE: + DmxSimple.write(iaddr, 0); + DmxSimple.write(iaddr+1, 0); + DmxSimple.write(iaddr+2, 0); + DmxSimple.write(iaddr+3, map(Par[2],0,100,0,255)); + break; + + case CH_MODBUS: + { + + if ((itemArg->type == aJson_Array) && (aJson.getArraySize(itemArg)==3)) + { + int _addr= aJson.getArrayItem(itemArg,0)->valueint; + int _reg = aJson.getArrayItem(itemArg,1)->valueint; + int _mask= aJson.getArrayItem(itemArg,2)->valueint; + + modbusSet(_addr,_reg,_mask,map(Par[0],0,100,0,0x1f)); + } + break;} + + case CH_GROUP://Group + { + //aJsonObject *groupArr= aJson.getArrayItem(itemArr, 1); + if (itemArg->type==aJson_Array) + { + aJsonObject *i =itemArg->child; + while (i) + { + Item it (i->valuestring); +// it.copyPar(itemVal); + it.Ctrl(cmd,n,Par,true); + i=i->next; + } //while + } //if + } //case + break; + case CH_RELAY: + {int k; + pinMode(iaddr,OUTPUT); + digitalWrite(iaddr,k=((cmd==CMD_ON)?HIGH:LOW)); + Serial.print(F("Pin:"));Serial.print(iaddr);Serial.print(F("="));Serial.println(k); + break; + case CH_THERMO: + ///thermoSet(name,cmd,Par1); all cativities done - update temp & cmd + break; + case CH_PWM: + pinMode(iaddr,OUTPUT); + //timer 0 for pin 13 and 4 + //timer 1 for pin 12 and 11 + //timer 2 for pin 10 and 9 + //timer 3 for pin 5 and 3 and 2 + //timer 4 for pin 8 and 7 and 6 + //prescaler = 1 ---> PWM frequency is 31000 Hz + //prescaler = 2 ---> PWM frequency is 4000 Hz + //prescaler = 3 ---> PWM frequency is 490 Hz (default value) + //prescaler = 4 ---> PWM frequency is 120 Hz + //prescaler = 5 ---> PWM frequency is 30 Hz + //prescaler = 6 ---> PWM frequency is <20 Hz + int tval = 7; // this is 111 in binary and is used as an eraser + TCCR4B &= ~tval; // this operation (AND plus NOT), set the three bits in TCCR2B to 0 + TCCR3B &= ~tval; + tval=2; + TCCR4B|=tval; + TCCR3B|=tval; + + analogWrite(iaddr,k=map(Par[0],0,100,0,255)); + Serial.print(F("Pin:"));Serial.print(iaddr);Serial.print(F("="));Serial.println(k); + break; + } + + case CH_VC: + + VacomSetFan(getArg(),Par[0]); + break; + + + case CH_VCTEMP: + { + Item it (itemArg->valuestring); + if (it.isValid() && it.itemType==CH_VC) + VacomSetHeat(it.getArg(),Par[0],cmd); + break; + } + + + } // switch itemtype + // break; + + + + } +/* + +short thermoSet(char * name, short cmd, short t) +{ + +if (items) + { + aJsonObject *item= aJson.getObjectItem(items, name); + if (item && (item->type==aJson_Array) && (aJson.getArrayItem(item, I_TYPE)->valueint==CH_THERMO)) + { + + for (int i=aJson.getArraySize(item);i<4;i++) aJson.addItemToArray(item,aJson.createItem(int(0))); //Enlarge item to 4 elements + if (!cmd) aJson.getArrayItem(item, I_VAL)->valueint=t; + aJson.getArrayItem(item, I_CMD)->valueint=cmd; + + } + + } +} + +*/ +void PooledItem::Idle() +{ +if (PoolingInterval) + { + Pool(); + next=millis()+PoolingInterval; + } + +}; + + + + + /* +addr 10d +Снять аварию 42001 (2001=7d1) =>4 + +[22:20:33] Write task has completed successfully +[22:20:33] <= Response: 0A 06 07 D0 00 04 89 FF +[22:20:32] => Poll: 0A 06 07 D0 00 04 89 FF + +100% +2003-> 10000 +[22:24:05] Write task has completed successfully +[22:24:05] <= Response: 0A 06 07 D2 27 10 33 C0 +[22:24:05] => Poll: 0A 06 07 D2 27 10 33 C0 + +ON +2001->1 +[22:24:50] Write task has completed successfully +[22:24:50] <= Response: 0A 06 07 D0 00 01 49 FC +[22:24:50] => Poll: 0A 06 07 D0 00 01 49 FC + +OFF +2001->0 +[22:25:35] Write task has completed successfully +[22:25:35] <= Response: 0A 06 07 D0 00 00 88 3C +[22:25:34] => Poll: 0A 06 07 D0 00 00 88 3C + + +POOL 2101x10 +[22:27:29] <= Response: 0A 03 14 00 23 00 00 27 10 13 88 0B 9C 00 32 00 F8 00 F2 06 FA 01 3F AD D0 +[22:27:29] => Poll: 0A 03 08 34 00 0A 87 18 + +*/ + +int Item::Pool() +{ + + } + +extern short modbusBusy; +extern ModbusMaster node; + +int Item::VacomSetFan (int addr, int8_t val) +{ + Serial.print("VC#");Serial.print(addr);Serial.print("=");Serial.println(val); + + + if (modbusBusy) return -1; + modbusBusy=1; + + uint8_t j, result; + uint16_t data[1]; + node.begin(9600,SERIAL_8N1,13); + + node.setSlave(addr); + + if (val) { + node.writeSingleRegister(2001-1,4+1);//delay(500); + //node.writeSingleRegister(2001-1,1); + } + else node.writeSingleRegister(2001-1,0); + delay (500); + node.writeSingleRegister(2003-1,val*100); + + /* + result = node.readHoldingRegisters(2003, 10); + + // do something with data if read is successful + if (result == node.ku8MBSuccess) + { Serial.print(F(" FM Val :")); + for (j = 0; j < 10; j++) + { + data[j] = node.getResponseBuffer(j); + Serial.print(data[j],HEX);Serial.print("-"); + + } + Serial.println(); + } else {Serial.print(F("Modbus pooling error=")); Serial.println(result,HEX); } + */ + modbusBusy=0; + + + + +} + +int Item::VacomSetHeat(int addr, int8_t val, int8_t cmd) + { + Serial.print("VC#");Serial.print(addr);Serial.print("=");Serial.print(val);Serial.print(" cmd=");Serial.println(cmd); + + } +int Item::SendCmd(short cmd,short n, int * Par) + { + char addrstr[32]; + char addrbuf[17]; + char valstr[16]; + + strcpy_P (addrstr,outprefix); + strncat (addrstr,itemArr->name,sizeof(addrstr)); //// + + + switch (cmd) + { + case CMD_ON: + strcpy(valstr,"ON"); + break; + case CMD_OFF: + case CMD_HALT: + strcpy(valstr,"OFF"); + break; + // TODO send Par + //case 0: + //case CMD_SET: + ///////////sprintf(valstr,"%d",Par[0]); + default: + return -1; + } + + Serial.print(addrstr);Serial.print("->");Serial.println(valstr); + client.publish(addrstr, valstr); + return 0; + } + + diff --git a/item.h b/item.h new file mode 100644 index 0000000..bad25f8 --- /dev/null +++ b/item.h @@ -0,0 +1,102 @@ + +#define CH_DIMMER 0 //DMX 1 ch +#define CH_RGBW 1 //DMX 4 ch +#define CH_RGB 2 //DMX 3 ch +#define CH_PWM 3 //PWM output directly to PIN +#define CH_MODBUS 4 //Modbus AC Dimmer +#define CH_THERMO 5 //Simple ON/OFF thermostat +#define CH_RELAY 6 //ON_OFF relay output +#define CH_GROUP 7 //Group pseudochannel +#define CH_VCTEMP 8 //Vacom PID regulator +#define CH_VC 9 //Vacom modbus motor regulator +#define CH_WHITE 127// + +#define CMD_ON 1 +#define CMD_OFF 2 +#define CMD_HALT 5 +#define CMD_RESTORE 3 +#define CMD_TOGGLE 4 +#define CMD_CURTEMP 127 +#define CMD_SET 9 + +#define I_TYPE 0 //Type of item +#define I_ARG 1 //Chanel-type depended argument or array of arguments (pin, address etc) +#define I_VAL 2 //Latest preset (int or array of presets) +#define I_CMD 3 //Latest CMD received +#define I_EXT 4 //Chanell-depended extension - array + +#include "aJSON.h" + +extern aJsonObject *items; + +int txt2cmd (char * payload); + +typedef union +{ + long int aslong; + struct + { + int16_t h; + int8_t s; + int8_t v; + }; +} HSVstore; + +class Item +{ + public: + aJsonObject *itemArr, *itemArg,*itemVal; + uint8_t itemType; + + + Item(char * name); + boolean isValid (); + virtual int Ctrl(short cmd, short n=0, int * Par=NULL, boolean send=false); + int getArg(); + boolean getEnableCMD(int delta); + //int getVal(short n); //From VAL array. Negative if no array + long int getVal(); //From int val OR array + uint8_t getCmd(); + void setCmd(uint8_t cmd); + //void setVal(uint8_t n, int par); + void setVal(long int par); + //void copyPar (aJsonObject *itemV); + inline int On (){Ctrl(CMD_ON);}; + inline int Off(){Ctrl(CMD_OFF);}; + inline int Toggle(){Ctrl(CMD_TOGGLE);}; + int Pool (); + int SendCmd(short cmd,short n=0, int * Par=NULL); + + protected: + int VacomSetFan (int addr, int8_t val); + int VacomSetHeat(int addr, int8_t val, int8_t cmd=0); + +}; + + + +class PooledItem : public Item +{ + public: + virtual int Changed() = 0; + virtual void Idle (); + protected: + int PoolingInterval; + unsigned long next; + virtual int Pool() =0; + +}; + + + +/* + +class Vacon : public Item +{ +public: +int Pool (); +virtual int Ctrl(short cmd, short n=0, int * Par=NULL); +protected: +}; + +*/ diff --git a/lighthub.ino b/lighthub.ino new file mode 100644 index 0000000..8fb2cc1 --- /dev/null +++ b/lighthub.ino @@ -0,0 +1,1061 @@ +/* + * Done: + * MQMT/openhab + * 1-wire + * DMX - out + * DMX IN + * 1809 strip out + Modbus master Out + DHCP +json config +cli +PWM Out 7,8,9 +1-w relay out +Termostat out +Todo +=== +A/C control +rotary encoder local ctrl +analog in local ctrl +Light sensor analog in +Smooth regulation/fading +Phase dimmer board +PID Termostat out +dmx relay out +-IRDA in +-MCS 350 out ? +-HDMI sw out? +Simple bridging (1w <-> dmx,modbus etc) ? + + +*/ + +//define NOETHER + + +//#include +#include +#include +#include + +//#include "owSwitch.h" +#include "utils.h" +#include "owTerm.h" + +//#include "led_sysdefs.h" +//#include "pixeltypes.h" +//#include "hsv2rgb.h" +#include +//#include +#include +#include "aJSON.h" +#include "HTTPClient.h" +//#include "CommandLine.h" +#include +#include "stdarg.h" +#include +//#include +#include +#include "dmx.h" +#include "item.h" +#include "inputs.h" +#include + + + +aJsonObject *root = NULL; +aJsonObject *items = NULL; +aJsonObject *inputs = NULL; + +aJsonObject *mqttArr = NULL; +aJsonObject *modbusArr = NULL; +aJsonObject *owArr = NULL; + + +// CommandLine instance. +//CommandLine commandLine(Serial, "> "); + + +bool owReady = false; +int lanStatus = 0; + +ModbusMaster node(2,0x60); //TODO dynamic alloc + + +IPAddress server(192, 168, 88, 2); +byte mac[6]; + +EthernetClient ethClient; +PubSubClient client(ethClient); + + +//char* inprefix=("/myhome/in/"); +//char* outprefix=("/myhome/s_out/"); +//char* subprefix=("/myhome/in/#"); + +#define inprefix "/myhome/in/" +const char outprefix[] PROGMEM = "/myhome/s_out/"; +#define subprefix "/myhome/in/#" +//#define subprefix "#" + +int modbusSet(int addr, uint16_t _reg, int _mask, uint16_t value); +int freeRam (void) ; + +int getInt(char ** chan) +{ + int ch = atoi(*chan); + *chan=strchr(*chan,','); + + if (*chan) *chan+=1; + //Serial.print(F("Par:")); Serial.println(ch); + return ch; + +} + + + +//// +int itemCtrl2(char* name,int r,int g, int b, int w) +{ + aJsonObject *itemArr= aJson.getObjectItem(items, name); + + if (itemArr && (itemArr->type==aJson_Array)) + { + + short itemtype = aJson.getArrayItem(itemArr,0)->valueint; + short itemaddr = aJson.getArrayItem(itemArr,1)->valueint; + switch (itemtype){ + case 0: //Dimmed light +//// if (is_on) DmxSimple.write(5, 255); //ArduinoDmx2.TxBuffer[itemaddr[ch]]=255;// +//// else DmxSimple.write(itemaddr, map(Par1,0,100,0,255)); //ArduinoDmx2.TxBuffer[itemaddr[ch]]=map(Par1,0,100,0,255);// + break; + + case 1: //Colour RGBW + DmxSimple.write(itemaddr+3, w); + case 2: // RGB + { + + DmxSimple.write(itemaddr, r); + DmxSimple.write(itemaddr+1, g); + DmxSimple.write(itemaddr+2, b); + + break; } + + case 7: //Group + aJsonObject *groupArr= aJson.getArrayItem(itemArr, 1); + if (groupArr && (groupArr->type==aJson_Array)) + { aJsonObject *i =groupArr->child; + while (i) + { //Serial.println(i->valuestring); + itemCtrl2(i->valuestring,r,g,b,w); + i=i->next;} + } + } //itemtype + // break; + } //if have correct array + } + + +void callback(char* topic, byte* payload, unsigned int length) { + payload[length]=0; + Serial.print(F("[")); + Serial.print(topic); + Serial.print("] "); + + int fr = freeRam(); + if (fr<250) {Serial.println(F("OOM!"));return;} + + char subtopic[20]=""; + int cmd = 0; + + for (int i=0;ibegin(); + + break; + case 2: // IP Ready, Connecting & subscribe +//Arming Watchdog + wdt_enable(WDTO_8S); + +if (!client.connected() && mqttArr && (aJson.getArraySize(mqttArr)>1)) { + char *c=aJson.getArrayItem(mqttArr,1)->valuestring; + Serial.print(F("Attempting MQTT connection...")); + if (client.connect(c)) { + Serial.print(F("connected as "));Serial.println(c); + + + // ... and resubscribe + client.subscribe(subprefix); + + + restoreState(); + // if (_once) {DMXput(); _once=0;} + lanStatus=3; + } else { + Serial.print(F("failed, rc=")); + Serial.print(client.state()); + Serial.println(F(" try again in 5 seconds")); + lanCheck=millis()+5000; + lanStatus=-12; + } +} +break; + case 3: //operation + if (!client.connected()) lanStatus=2; + break; + +//Awaiting address +case -10: if (millis()>lanCheck) + lanStatus=0; +break; +//Reconnect +case -12: if (millis()>lanCheck) + + lanStatus=2; +break; + // read or Re-read config + case -11: + if (loadConfig(0,NULL)) lanStatus=2; + else {lanCheck=millis()+5000;lanStatus=-10;} + + +break; + +} +//static long mtnCnt=0; +// Maintain dynamic IP +//if (millis()>mtnCnt) +{ +//mtnCnt=millis()+2; +wdt_disable(); +switch (Ethernet.maintain()) + { + case 1: + //renewed fail + Serial.println(F("Error: renewed fail")); + lanStatus = -10; + break; + + case 2: + //renewed success + Serial.println(F("Renewed success")); + + //print your local IP address: + printIPAddress(); + break; + + case 3: + //rebind fail + Serial.println(F("Error: rebind fail")); + lanStatus = -10; + break; + + case 4: + //rebind success + Serial.println(F("Rebind success")); + + //print your local IP address: + printIPAddress(); + break; + + default: + //nothing happened + break; + + } + wdt_enable(WDTO_8S); + } + +return lanStatus; + +} + + +void Changed (int i, DeviceAddress addr, int val) +{ + char addrstr[32]="NIL"; + char addrbuf[17]; + char valstr[16]="NIL"; + char *owEmit = NULL; + char *owItem = NULL; + + //PrintBytes(addr,8); + // Serial.print("Emit: "); + SetBytes(addr,8,addrbuf);addrbuf[17]=0; + + //Serial.println(addrbuf); + + aJsonObject *owObj = aJson.getObjectItem(owArr, addrbuf); + if (owObj) + { + owEmit = aJson.getObjectItem(owObj, "emit")->valuestring; + if (owEmit) + { + strncpy(addrbuf,owEmit,sizeof(addrbuf)); + Serial.print(owEmit);Serial.print("=");Serial.println(val); + } + owItem = aJson.getObjectItem(owObj, "item")->valuestring; + } else Serial.println(F("Not find")); + + + /* No sw support anymore + switch (addr[0]){ + case 0x29: // DS2408 + snprintf(addrstr,sizeof(addrstr),"%sS0%s",outprefix,addrbuf); + // Serial.println(addrstr); + client.publish(addrstr, (val & SW_STAT0)?"ON":"OFF"); + snprintf(addrstr,sizeof(addrstr),"%sS1%s",outprefix,addrbuf); + // Serial.println(addrstr); + client.publish(addrstr, (val & SW_STAT1)?"ON":"OFF"); + snprintf(addrstr,sizeof(addrstr),"%sS2%s",outprefix,addrbuf); + // Serial.println(addrstr); + client.publish(addrstr, (val & SW_AUX0)?"OFF":"ON"); + snprintf(addrstr,sizeof(addrstr),"%sS3%s",outprefix,addrbuf); + // Serial.println(addrstr); + client.publish(addrstr, (val & SW_AUX1)?"OFF":"ON"); + break; + + case 0x28: // Thermomerer + + snprintf(addrstr,sizeof(addrstr),"%s%s",outprefix,addrbuf); + sprintf(valstr,"%d",val); + //Serial.println(val); + //Serial.println(valstr); + client.publish(addrstr, valstr); + + if (owItem) + { + thermoSetCurTemp(owItem,val); + } + break; + + case 0x01: + case 0x81: + snprintf(addrstr,sizeof(addrstr),"%sDS%s",outprefix,addrbuf); + if (val) sprintf(valstr,"%s","ON"); else sprintf(valstr,"%s","OFF"); + client.publish(addrstr, valstr); + } + */ + + if ((val==-127) || (val==85)) + { +// Serial.print("Temp err ");Serial.println(t); + return; + } + + strcpy_P (addrstr,outprefix); + strncat (addrstr,addrbuf,sizeof(addrstr)); + //snprintf(addrstr,sizeof(addrstr),"%s%s",F(outprefix),addrbuf); + sprintf(valstr,"%d",val); + client.publish(addrstr, valstr); + + if (owItem) + { + thermoSetCurTemp(owItem,val); + } + + + + //client.publish(addrstr, valstr); + + //Serial.print(addrstr); + //Serial.print(" = "); + //Serial.println(valstr); + +} + + +void modbusIdle(void) ; + +void _handleHelp(int arg_cnt, char **args) +//(char* tokens) +{ + Serial.println(F("Use the commands 'help', 'save', 'get' or 'item'.")); +} + +void _kill(int arg_cnt, char **args) +{ + for (short i=9;i>0;i--) {delay(1000);Serial.println(i);}; +} + +#define EEPROM_offset 40 + +void parseConfig() +{ int mc,incnt; + //DMX out is configured + aJsonObject *dmxoutArr = aJson.getObjectItem(root, "dmx"); + if (dmxoutArr && aJson.getArraySize(dmxoutArr)==2) + { + DmxSimple.usePin(aJson.getArrayItem(dmxoutArr,0)->valueint); + DmxSimple.maxChannel(mc=aJson.getArrayItem(dmxoutArr,1)->valueint); + Serial.print(F("DMX out started. Channels: ")); + Serial.println(mc); + } + + //DMX in is configured + dmxArr= aJson.getObjectItem(root, "dmxin"); + if (dmxArr && (incnt=aJson.getArraySize(dmxArr))) + { + DMXinSetup(incnt*4); + Serial.print(F("DMX in started. Channels:")); + Serial.println(incnt*4); + } + + items = aJson.getObjectItem(root,"items"); + inputs = aJson.getObjectItem(root,"in"); + + modbusArr= aJson.getObjectItem(root, "modbus"); + mqttArr= aJson.getObjectItem(root, "mqtt"); + owArr= aJson.getObjectItem(root, "ow"); + + + Serial.println(F("Configured:")); + + Serial.print(F("items ")); printBool(items); + Serial.print(F("inputs ")); printBool(inputs); + Serial.print(F("modbus ")); printBool(modbusArr); + Serial.print(F("mqtt ")); printBool(mqttArr); + Serial.print(F("1-wire ")); printBool(owArr); + + if (owArr && !owReady) + { + aJsonObject * item= owArr->child; + owReady=owSetup(&Changed); + + while (item) + { + if ((item->type==aJson_Object) ) + { + DeviceAddress addr; + //Serial.print(F("Add:")),Serial.println(item->name); + SetAddr(item->name,addr); + owAdd(addr); + } + item=item->next; + } + + } + +} + +void _loadConfig (int arg_cnt, char **args) {loadConfig(arg_cnt,args);restoreState();} +int loadConfig (int arg_cnt, char **args) +//(char* tokens) +{ + Serial.println(F("loading Config")); + aJsonEEPROMStream as=aJsonEEPROMStream(EEPROM_offset); + aJson.deleteItem(root); + root = aJson.parse(&as); + Serial.println(); + if (!root) + { + Serial.println(F("load failed")); + return 0; + } + Serial.println(F("Loaded")); + parseConfig(); + return 1; +} + +void _mqttConfigReq (int arg_cnt, char **args) {mqttConfigReq(arg_cnt,args);restoreState();} + + +int mqttConfigReq (int arg_cnt, char **args) +//(char* tokens) +{ + char buf[25] ="/"; + Serial.println(F("request MQTT Config")); + SetBytes((uint8_t*)mac,6,buf+1); + buf[13]=0; + strncat(buf,"/resp/#",25); + Serial.println(buf); + client.subscribe(buf); + buf[13]=0; + strncat(buf,"/req/conf",25); + Serial.println(buf); + client.publish(buf,"1"); + + } + + +int mqttConfigResp (char * as) +{ + Serial.println(F("got MQTT Config")); + + //aJsonEEPROMStream as=aJsonEEPROMStream(EEPROM_offset); + + //aJson.deleteItem(root); + root = aJson.parse(as); + Serial.println(); + if (!root) + { + Serial.println(F("load failed")); + return 0; + } + Serial.println(F("Loaded")); + parseConfig(); + return 1; +} + +void _saveConfig(int arg_cnt, char **args) +//(char* tokens) +{ + aJsonEEPROMStream es=aJsonEEPROMStream(EEPROM_offset); + Serial.println(F("Saving config..")); + aJson.print(root,&es); + es.putEOF(); + Serial.println(F("Saved")); + +} + + +void _setConfig(int arg_cnt, char **args) +{ + + //Serial.print("Got:"); + //Serial.println(args[1]); + if (sscanf(args[1], "%x:%x:%x:%x:%x:%x%с", + &mac[0], + &mac[1], + &mac[2], + &mac[3], + &mac[4], + &mac[5]) < 6) +{ + Serial.print(F("could not parse: ")); + Serial.println(args[1]); + return; +} + printMACAddress(); + for (short i=0;i<6;i++) { EEPROM.update(i, mac[i]);} + Serial.println(F("Updated")); + +} + +void _getConfig(int arg_cnt, char **args) {getConfig(arg_cnt,args);restoreState();} + +void printBool (bool arg) +{if (arg) Serial.println(F("on")); else Serial.println(F("off"));} + +int getConfig (int arg_cnt, char **args) +//(char *tokens) +{ + FILE* result; + int returnCode ; + char ch; + char URI[32]; + byte hserver[] = { 192,168,88,2 }; + + + snprintf(URI, sizeof(URI), "/%02x-%02x-%02x-%02x-%02x-%02x.config.json",mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + Serial.println(F("Config URI: "));Serial.println(URI); + + HTTPClient hclient("192.168.88.2",hserver,80); + + // FILE is the return STREAM type of the HTTPClient + result = hclient.getURI( URI); + returnCode = hclient.getLastReturnCode(); + + if (result!=NULL) { + if (returnCode==200) { + + Serial.println(F("got Config")); + aJsonFileStream as=aJsonFileStream(result); + aJson.deleteItem(root); + root = aJson.parse(&as); + hclient.closeStream(result); // this is very important -- be sure to close the STREAM + + if (!root) + { + Serial.println(F("Config parsing failed")); + lanCheck=millis()+15000; + return -11; + } + else + { + char * outstr=aJson.print(root); + Serial.println(outstr); + free (outstr); + + parseConfig(); + + + } + + } + else { + Serial.print(F("ERROR: Server returned ")); + Serial.println(returnCode); + lanCheck=millis()+5000; + return -11; + } + + } + else { + Serial.println(F("failed to connect")); + Serial.println(F(" try again in 5 seconds")); + lanCheck=millis()+5000; + return -11; + } + + + + return 2; +} + + +void setup() { + //Serial.begin(115200); + cmdInit(115200); + + Serial.println(F("\nLazyhome.ru LightHub controller v0.8")); + + + for (short i=0;i<6;i++) mac[i]=EEPROM.read(i); + printMACAddress(); + + // initialize Modbus communication baud rate + node.begin(9600,SERIAL_8N1,13); + + owReady=0; + //=owSetup(&Changed); + + if (net) net->idle(&owIdle); + node.idle(&modbusIdle); + + + client.setServer(server, 1883); + client.setCallback(callback); + + + + ArtnetSetup(); + + + cmdAdd("help", _handleHelp); + cmdAdd("save", _saveConfig); + cmdAdd("load", _loadConfig); + cmdAdd("get", _getConfig); + cmdAdd("set", _setConfig); + cmdAdd("kill", _kill); + cmdAdd("req", _mqttConfigReq); + + +} + + + +unsigned long modbus_check=0; +short modbusBusy=0; + + int modbusSet(int addr, uint16_t _reg, int _mask, uint16_t value) + { + + if (modbusBusy) return -1; + modbusBusy=1; + node.begin(9600,SERIAL_8E1,13); + node.setSlave(addr); + /* + uint8_t res = node.readHoldingRegisters(_reg, 1); + if (res != node.ku8MBSuccess) return -1; + uint16_t b0=node.getResponseBuffer(0); + //uint8_t b1=node.getResponseBuffer(1); + //Serial.print(b0,HEX);Serial.println(b1,HEX); + delay(5); + + + if (_mask) + {value <<= 8; value |= (b0 & 0xff);} + else {value &= 0xff; value |= (b0 & 0xff00);} +*/ + +if (_mask) + {value <<= 8; value |= (0xff);} + else {value &= 0xff; value |= (0xff00);} + + Serial.print(addr);Serial.print("=>");Serial.print(_reg,HEX);Serial.print(":");Serial.println(value,HEX); + + node.writeSingleRegister(_reg,value); + modbusBusy=0; + } + + + +int checkFMDev(int dev) + { + if (modbusBusy) return -1; + modbusBusy=1; + + uint8_t j, result; + uint16_t data[1]; + node.begin(9600,SERIAL_8N1,13); + + node.setSlave(dev); + + result = node.readHoldingRegisters(2101-1, 10); + + // do something with data if read is successful + if (result == node.ku8MBSuccess) + { Serial.print(F(" FM Val :")); + for (j = 0; j < 10; j++) + { + data[j] = node.getResponseBuffer(j); + Serial.print(data[j],HEX);Serial.print("-"); + + } + Serial.println(); + } else {Serial.print(F("Modbus pooling error=")); Serial.println(result,HEX); } + +result = node.readHoldingRegisters(20-1, 4); + + // do something with data if read is successful + if (result == node.ku8MBSuccess) + { Serial.print(F(" PI Val :")); + for (j = 0; j < 4; j++) + { + data[j] = node.getResponseBuffer(j); + Serial.print(data[j]);Serial.print("-"); + + } + Serial.println(); + } else {Serial.print(F("Modbus pooling error=")); Serial.println(result,HEX); } + + + + modbusBusy=0; + } + + + int checkDev(int dev) + { + if (modbusBusy) return -1; + modbusBusy=1; + node.begin(9600,SERIAL_8E1,13); + + uint8_t j, result; + uint16_t data[1]; + + node.setSlave(dev); + result = node.readHoldingRegisters(0, 1); + + // do something with data if read is successful + if (result == node.ku8MBSuccess) + { Serial.print(F(" Modbus Val :")); + for (j = 0; j < 1; j++) + { + data[j] = node.getResponseBuffer(j); + Serial.print(data[j],HEX);Serial.print("-"); + + } + Serial.println(); + } else {Serial.print(F("Modbus pooling error=")); Serial.println(result,HEX); } + + modbusBusy=0; + } + + + + +void modbusloop() +{ + + if (millis()>modbus_check) + { + checkDev(0x60); + delay(10); + checkDev(0x61); + delay(10); + checkFMDev(10); + + + modbus_check=millis()+5000; + Serial.println(freeRam()); + } +} + + +void loop(){ + wdt_reset(); + + //commandLine.update(); + cmdPoll(); +if (lanloop() >1) {client.loop(); if (artnet) artnet->read();} +if (owReady && owArr) owLoop(); + + unsigned long lastpacket = DMXSerial.noDataSince(); + // if (lastpacket && (lastpacket%10==0)) Serial.println(lastpacket); + + DMXCheck(); + if (modbusArr) modbusloop(); + if (items) thermoIdle(); + if (inputs) inputIdle(); + +} +//int imodbus=0; +//unsigned short modbuspoll[]={0x60,0x61}; + + +void owIdle(void) +{ if (artnet) artnet->read(); + wdt_reset(); + + return;/// + Serial.print("o"); + +if (lanloop() == 1) client.loop(); +//if (owReady) owLoop(); + DMXCheck(); + //modbusloop(); + } + + +void modbusIdle(void) +{ + //Serial.print("m"); + wdt_reset(); + +if (lanloop() > 1) {client.loop();if (artnet) artnet->read();} +//if (owReady) owLoop(); + DMXCheck(); + //modbusloop(); + } + +#define GIST 2 +unsigned long thermocheck=0; + + +void thermoIdle(void) +{ +#define T_ATTEMPTS 20 +#define IET_TEMP 0 +#define IET_ATTEMPTS 1 + + if (millis()>thermocheck) + { + + aJsonObject * item= items->child; + + while (item) + { + if ((item->type==aJson_Array) && (aJson.getArrayItem(item, 0)->valueint==CH_THERMO) && (aJson.getArraySize(item)>4)) + { + int pin=aJson.getArrayItem(item, I_ARG)->valueint; + int temp=aJson.getArrayItem(item, I_VAL)->valueint; + + int cmd=aJson.getArrayItem(item, I_CMD)->valueint; + + aJsonObject * extArr=aJson.getArrayItem(item, I_EXT); + + if (extArr && (aJson.getArraySize(extArr)>1) ) + { + int curtemp = aJson.getArrayItem(extArr, IET_TEMP)->valueint; + if (!aJson.getArrayItem(extArr, IET_ATTEMPTS)->valueint) {Serial.println(F("Expired"));} else aJson.getArrayItem(extArr, IET_ATTEMPTS)->valueint--; + + Serial.print(item->name);Serial.print(F(" Set:"));Serial.print(temp); Serial.print(F(" Curtemp:"));Serial.print(curtemp); Serial.print(F( " cmd:")); Serial.print(cmd), + + pinMode(pin,OUTPUT); + if (cmd==CMD_OFF || cmd==CMD_HALT) {digitalWrite(pin,LOW);Serial.println(F(" OFF"));} + else + { + if (curtemp+GISTnext; + } + + + thermocheck=millis()+5000; + Serial.println(freeRam()); + } + +} + +long int incheck =0; + +void inputIdle(void) +{ + + if (millis()>incheck) + { + + aJsonObject * input= inputs->child; + + while (input) + { + if ((input->type==aJson_Object) ) + { + Input in(input); + in.Pool(); + } + input=input->next; + } + + + incheck=millis()+50; + + } + +} + + +short thermoSetCurTemp(char * name, short t) +{ +if (items) + { + aJsonObject *item= aJson.getObjectItem(items, name); + if (item && (item->type==aJson_Array) && (aJson.getArrayItem(item, I_TYPE)->valueint==CH_THERMO) && (aJson.getArraySize(item)>=4)) + { + aJsonObject * extArray =NULL; + + if (aJson.getArraySize(item)==4) //No thermo extension yet + { + extArray = aJson.createArray(); //Create Ext Array + + aJsonObject * ocurt=aJson.createItem(t); //Create int + aJsonObject * oattempts=aJson.createItem(T_ATTEMPTS); //Create int + aJson.addItemToArray(extArray,ocurt); + aJson.addItemToArray(extArray,oattempts); + aJson.addItemToArray(item,extArray); //Adding to item + } //if + else if (extArray=aJson.getArrayItem(item,I_EXT)) + { + aJson.getArrayItem(extArray, IET_TEMP)->valueint=t; + aJson.getArrayItem(extArray, IET_ATTEMPTS)->valueint=(int) T_ATTEMPTS; + } //if + + + } //if +} // if items + +} //proc + + + +int freeRam () +{ + extern int __heap_start, *__brkval; + int v; + return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); +} + + diff --git a/owSwitch.cpp b/owSwitch.cpp new file mode 100644 index 0000000..013a685 --- /dev/null +++ b/owSwitch.cpp @@ -0,0 +1,438 @@ +#include "owSwitch.h" +#include "owTerm.h" +#include +#include "utils.h" + +int owRead2408(uint8_t* addr) { + uint8_t buf[13]; +// PrintBytes(buf, 13, true); +if (!net) return -1; + net->reset(); + net->select(addr); + +// uint8_t buf[13]; // Put everything in the buffer so we can compute CRC easily. + buf[0] = 0xF0; // Read PIO Registers + buf[1] = 0x88; // LSB address + buf[2] = 0x00; // MSB address + net->write_bytes(buf, 3,1); + net->read_bytes(buf+3, 10); // 3 cmd bytes, 6 data bytes, 2 0xFF, 2 CRC16 + net->reset(); + + if (!OneWire::check_crc16(buf, 11, &buf[11])) { + Serial.print(F("CRC failure in DS2408 at ")); + PrintBytes(addr, 8, true); + PrintBytes(buf+3,10); + return -1; + } + return (buf[3]); +} + + +/* +int read1W(int i) +{ + + Serial.print("1W requested: "); + Serial.println (i); + + int t=-1; + switch (term[i][0]){ + case 0x29: // DS2408 + t=owRead2408(term[i]); + break; + case 0x28: // Thermomerer + t=sensors.getTempC(term[i]); + } + return t; +} +*/ + + +int ow2408out(DeviceAddress addr,uint8_t cur) +{ + if (!net) return -1; + uint8_t buf[5]; + net->reset(); + net->select(addr); + buf[0] = 0x5A; // Write PIO Registers + buf[1]=cur; + buf[2] = ~buf[1]; + net->write_bytes(buf, 3); + net->read_bytes(buf+3, 2); + //net.reset(); + PrintBytes(buf, 5); + Serial.print(" Out: ");Serial.print(buf[1],BIN); + Serial.print(" In: ");Serial.println(buf[4],BIN); + if (buf[3] != 0xAA) { + Serial.print("Write failure in DS2408 at "); + PrintBytes(addr, 8, true); + return -2; + } + return buf[4]; +} +int cntrl2408(uint8_t* addr, int subchan, int val) { +if (!net) return -1; + + uint8_t buf; + int mask,devnum; + + if ((devnum=owFind(addr))<0) return -1; + buf=regs[devnum]; + Serial.print("Current: ");Serial.println(buf,BIN); + mask=0; + int r,f; + switch (subchan) { + case 0: + if ((buf & SW_STAT0) != ((val)?SW_STAT0:0)) + { + if (wstat[devnum] & (SW_PULSE0|SW_PULSE_P0)) + { + wstat[devnum]|=SW_CHANGED_P0; + Serial.println("Rollback 0"); + } + else { + wstat[devnum]|=SW_PULSE0; + regs[devnum] = (ow2408out(addr,(buf | SW_MASK) & ~SW_OUT0) & SW_INMASK) ^ SW_STAT0; ///? + + } + } + return 0; + case 1: + if ((buf & SW_STAT1) != ((val)?SW_STAT1:0)) + { + if (wstat[devnum] & (SW_PULSE1|SW_PULSE_P1)) + { + wstat[devnum]|=SW_CHANGED_P1; + Serial.println("Rollback 1"); + } + else { + wstat[devnum]|=SW_PULSE1; + regs[devnum] =(ow2408out(addr,(buf | SW_MASK) & ~SW_OUT1) & SW_INMASK) ^ SW_STAT1; /// -? + + } + } + return 0; + /* Assume AUX 0&1 it is INPUTS - no write + case 2: + mask=SW_AUX0; + break; + case 3: + mask=SW_AUX1; */ + } + + /* Assume AUX 0&1 it is INPUTS - no write + switch (val) { + case 0: buf=(buf | SW_MASK | SW_OUT0 | SW_OUT1) | mask; + break; + default: buf= (buf | SW_MASK | SW_OUT0 | SW_OUT1) & ~mask; + } + + + regs[devnum] = ow2408out(addr,buf); */ + return 0; + } + + +int cntrl2890(uint8_t* addr, int val) { + uint8_t buf[13]; + if (!net) return -1; + // case 0x2C: //Dimmer + Serial.print("Update dimmer ");PrintBytes(addr, 8, true);Serial.print(" = "); + Serial.println(val); + + net->reset(); + net->select(addr); + + buf[0] = 0x55; + buf[1] = 0x4c; + net->write_bytes(buf, 2); + net->read_bytes(buf+2, 1); // check if buf[2] == val = ok + buf[3]=0x96; + net->write_bytes(buf+3, 1); + net->read_bytes(buf+4, 1); // 0 = updated ok + PrintBytes(buf, 5, true); + + net->select(addr); + + +if (val==-1) + { + buf[0] = 0xF0; + net->write_bytes(buf, 1); + net->read_bytes(buf+1, 2); // check if buf[2] == val = ok + net->reset(); + return buf[2]; + } +else + { + buf[0] = 0x0F; + buf[1] = val; + net->write_bytes(buf, 2); + net->read_bytes(buf+2, 1); // check if buf[2] == val = ok + buf[3]=0x96; + net->write_bytes(buf+3, 1); + net->read_bytes(buf+4, 1); // 0 = updated ok + net->reset(); + PrintBytes(buf, 5, true); + return buf[2]; + } + + +} + +#define DS2413_FAMILY_ID 0x3A +#define DS2413_ACCESS_READ 0xF5 +#define DS2413_ACCESS_WRITE 0x5A +#define DS2413_ACK_SUCCESS 0xAA +#define DS2413_ACK_ERROR 0xFF + +#define DS2413_IN_PinA 1 +#define DS2413_IN_LatchA 2 +#define DS2413_IN_PinB 4 +#define DS2413_IN_LatchB 8 + +#define DS2413_OUT_PinA 1 +#define DS2413_OUT_PinB 2 + + +/* +byte read(void) +{ + bool ok = false; + uint8_t results; + + oneWire.reset(); + oneWire.select(address); + oneWire.write(DS2413_ACCESS_READ); + + results = oneWire.read(); / Get the register results / + ok = (!results & 0x0F) == (results >> 4); / Compare nibbles / + results &= 0x0F; / Clear inverted values / + + oneWire.reset(); + + // return ok ? results : -1; + return results; +} + +bool write(uint8_t state) +{ + uint8_t ack = 0; + + / Top six bits must '1' / + state |= 0xFC; + + oneWire.reset(); + oneWire.select(address); + oneWire.write(DS2413_ACCESS_WRITE); + oneWire.write(state); + oneWire.write(~state); / Invert data and resend / + ack = oneWire.read(); / 0xAA=success, 0xFF=failure / + if (ack == DS2413_ACK_SUCCESS) + { + oneWire.read(); / Read the status byte / + } + oneWire.reset(); + + return (ack == DS2413_ACK_SUCCESS ? true : false); +} + +*/ + + + +int cntrl2413(uint8_t* addr, int subchan, int val) { + + + bool ok = false; + uint8_t results; + uint8_t cmd; + uint8_t set=0; + uint8_t count =10; + if (!net) return -1; + // case 0x85: //Switch + Serial.print("Update switch ");PrintBytes(addr, 8, false); Serial.print("/");Serial.print(subchan);Serial.print(" = ");Serial.println(val); + while (count--) + { + net->reset(); + net->select(addr); + net->setStrongPullup(); + + cmd = DS2413_ACCESS_READ; + net->write(cmd); + + results = net->read(); + Serial.print("Got: "); Serial.println(results,BIN); + //Serial.println((~results & 0x0F),BIN); Serial.println ((results >> 4),BIN); + + ok = (~results & 0x0F) == (results >> 4); // Compare nibbles + results &= 0x0F; // Clear inverted values + + if (ok) {Serial.println("Read ok");break;} else {Serial.println("read Error");delay(1);} + } //while + + if (ok && (val>=0)) + { + count=10; + while (count--) + { + net->reset(); + net->select(addr); + + if (results & DS2413_IN_LatchA) set|=DS2413_OUT_PinA; + if (results & DS2413_IN_LatchB) set|=DS2413_OUT_PinB; + + switch (subchan) { + case 0: + if (!val) set|=DS2413_OUT_PinA; else set &= ~DS2413_OUT_PinA; + break; + case 1: + if (!val) set|=DS2413_OUT_PinB; else set &= ~DS2413_OUT_PinB; + }; + set |= 0xFC; + Serial.print("New: ");Serial.println(set,BIN); + cmd = DS2413_ACCESS_WRITE; + net->write(cmd); + + net->write(set); + net->write(~set); + + uint8_t ack = net->read(); // 0xAA=success, 0xFF=failure + + if (ack == DS2413_ACK_SUCCESS) + { + results=net->read(); + Serial.print("Updated ok: "); Serial.println(results,BIN); + ok = (~results & 0x0F) == (results >> 4); // Compare nibbles + { + if (ok) + {Serial.println("Readback ok"); + break;} + else {Serial.println("readback Error");delay(1);} + } + results &= 0x0F; // Clear inverted values + } + else Serial.println ("Write failed");; + + } //while + } //if +return ok ? results : -1; + + +} + + +int sensors_ext(void) +{ + + int t; + switch (term[si][0]){ + case 0x29: // DS2408 + //Serial.println(wstat[si],BIN); + + if (wstat[si] & SW_PULSE0) { + wstat[si]&=~SW_PULSE0; + wstat[si]|=SW_PULSE_P0; + Serial.println("Pulse0 in progress"); + + return 500; + } + + if (wstat[si] & SW_PULSE0_R) { + wstat[si]&=~SW_PULSE0_R; + wstat[si]|=SW_PULSE_P0; + regs[si] =(ow2408out(term[si],(regs[si] | SW_MASK) & ~SW_OUT0) & SW_INMASK) ^ SW_STAT0; + Serial.println("Pulse0 in activated"); + + return 500; + } + + if (wstat[si] & SW_PULSE1) { + wstat[si]&=~SW_PULSE1; + wstat[si]|=SW_PULSE_P1; + Serial.println("Pulse1 in progress"); + + return 500; + } + + if (wstat[si] & SW_PULSE1_R) { + wstat[si]&=~SW_PULSE1_R; + wstat[si]|=SW_PULSE_P1; + regs[si] =(ow2408out(term[si],(regs[si] | SW_MASK) & ~SW_OUT1) & SW_INMASK) ^ SW_STAT1; + Serial.println("Pulse0 in activated"); + + return 500; + } + + if (wstat[si] & SW_PULSE_P0) { + wstat[si]&=~SW_PULSE_P0; + Serial.println("Pulse0 clearing"); + ow2408out(term[si],regs[si] | SW_MASK | SW_OUT0); + + if (wstat[si] & SW_CHANGED_P0) { + wstat[si]&=~SW_CHANGED_P0; + wstat[si]|=SW_PULSE0_R; + return 500; + } + } + +if (wstat[si] & SW_PULSE_P1) { + wstat[si]&=~SW_PULSE_P1; + Serial.println("Pulse1 clearing"); + ow2408out(term[si],regs[si] | SW_MASK | SW_OUT1); + + if (wstat[si] & SW_CHANGED_P1) { + wstat[si]&=~SW_CHANGED_P1; + wstat[si]|=SW_PULSE1_R; + return 500; + } + } + + if (wstat[si]) + { + t=owRead2408(term[si]) & SW_INMASK; + + + + if (t!=regs[si]) { + + Serial.print(F("DS2408 data = ")); + Serial.println(t, BIN); + + if (!(wstat[si] & SW_DOUBLECHECK)) + { + wstat[si]|=SW_DOUBLECHECK; //suspected + Serial.println("DOUBLECHECK"); + return recheck_interval; + } + + + Serial.println(F("Really Changed")); + if (owChanged) owChanged(si,term[si],t); + regs[si]=t; + + + } + wstat[si]&=~SW_DOUBLECHECK; + } + break; + + + case 0x01: + case 0x81: + t=wstat[si]; + if (t!=regs[si]) + { Serial.println("Changed"); + if (owChanged) owChanged(si,term[si],t); + regs[si]=t; + } + } + + + si++; + return check_circle; + +} + + + diff --git a/owSwitch.h b/owSwitch.h new file mode 100644 index 0000000..be40357 --- /dev/null +++ b/owSwitch.h @@ -0,0 +1,12 @@ +//define APU_OFF +#include +#include + + + +int owRead2408(uint8_t* addr); +int ow2408out(DeviceAddress addr,uint8_t cur); +//int read1W(int i); +int cntrl2408(uint8_t* addr, int subchan, int val=-1); +int cntrl2413(uint8_t* addr, int subchan, int val=-1); +int cntrl2890(uint8_t* addr, int val); diff --git a/owTerm.cpp b/owTerm.cpp new file mode 100644 index 0000000..deccc3f --- /dev/null +++ b/owTerm.cpp @@ -0,0 +1,177 @@ +#include "owTerm.h" +#include +#include "utils.h" + + +OneWire *net = NULL; +// Pass our oneWire reference to Dallas Temperature. +//DallasTemperature sensors(&net); + +DeviceAddress *term = NULL; +//int *regs = NULL; +uint16_t *wstat = NULL; +DallasTemperature *sensors = NULL; + +short si=0; +int t_count = 0; +unsigned long owTimer=0; + +owChangedType owChanged; + +int owUpdate() +{ + unsigned long finish = millis() + 5000; + short sr; + + //net.setStrongPullup(); + Serial.println(F("Searching")); +if (net) net->reset_search(); +for (short i=0;iwireSearch(term[t_count])>0 && (t_count millis ()) + { short ifind=-1; + if (net->crc8(term[t_count], 7) == term[t_count][7]) + { + for (short i=0;isetResolution(term[t_count], TEMPERATURE_PRECISION); + net->setStrongPullup(); + // sensors.requestTemperaturesByAddress(term[t_count]); + } + t_count++;} + }//if + } //while + + Serial.print(F("1-wire count: ")); + Serial.println(t_count); + +} + + +int owSetup(owChangedType owCh) { + //// todo - move memory allocation to here + +#ifdef _2482 +net = new OneWire; +#else +net = new OneWire (ONE_WIRE_BUS); +#endif + + + +// Pass our oneWire reference to Dallas Temperature. +sensors = new DallasTemperature (net); + +term = new DeviceAddress[t_max]; +//regs = new int [t_max]; +wstat = new uint16_t [t_max]; + + + + #ifdef _2482 + Wire.begin(); + if (net->checkPresence()) + { + Serial.println(F("DS2482-100 present")); + net->deviceReset(); + #ifdef APU_OFF + Serial.println(F("APU off")); + #else + net->setActivePullup(); + #endif + + Serial.println(F("\tChecking for 1-Wire devices...")); + if (net->wireReset()) + Serial.println(F("\tReset done")); + + sensors->begin(); + owChanged=owCh; + //owUpdate(); + //Serial.println(F("\t1-w Updated")); + sensors->setWaitForConversion(false); + + + return true; + } + #endif + + return false; + // IC Default 9 bit. If you have troubles consider upping it 12. Ups the delay giving the IC more time to process the temperature measurement + + + delay(500); + +} + + +int sensors_loop(void) + { + if (!sensors) return -1; + if (si>=t_count) + { + owUpdate(); //every check circle - scan for new devices + si=0; + return 8000; + } + + int t; + switch (term[si][0]){ + + case 0x28: // Thermomerer + t=sensors->getTempC(term[si]);//*10.0; + //Serial.println("o"); + if (owChanged) owChanged(si,term[si],t); + sensors->requestTemperaturesByAddress(term[si]); + si++; + return 2500; + + // default + // return sensors_ext(); + } //switch + + + si++; + return check_circle; + +} + + +void owLoop() + +{ + if (millis() >=owTimer) owTimer=millis()+sensors_loop(); +} + + +int owFind(DeviceAddress addr) +{ + for (short i=0;isetResolution(term[t_count], TEMPERATURE_PRECISION); + net->setStrongPullup(); + // sensors.requestTemperaturesByAddress(term[t_count]); + } + t_count++; +} + + diff --git a/owTerm.h b/owTerm.h new file mode 100644 index 0000000..7f39c5c --- /dev/null +++ b/owTerm.h @@ -0,0 +1,70 @@ +//define APU_OFF + +#define SW_AUX0 0x40 +#define SW_AUX1 0x80 +#define SW_STAT0 0x4 +#define SW_STAT1 0x8 +#define SW_OUT0 0x20 +#define SW_OUT1 0x10 +#define SW_MASK 0xF +#define SW_INMASK 0xFC + +#define recheck_interval 5 +#define check_circle 2000/t_count + +#define SW_FIND 1 +#define SW_DOUBLECHECK 2 //Doublecheck required +#define SW_PULSE0 4 //Pulse Reset started +#define SW_PULSE1 8 //Pulse Reset stsrted +#define SW_PULSE_P0 0x10 //Pulse reset in process +#define SW_PULSE_P1 0x20 //Pulse reset in process +#define SW_CHANGED_P0 0x40 //Changes while pulse in progress +#define SW_CHANGED_P1 0x80 //Changes while pulse in progress +#define SW_PULSE0_R 0x100 //Pulse Reset requested +#define SW_PULSE1_R 0x200 //Pulse Reset requested + + +#define recheck_interval 5 +#define check_circle 2000/t_count + + + +#define t_max 20 //Maximum number of 1w devices +#define TEMPERATURE_PRECISION 9 + +#include +#include +#include "aJSON.h" + +extern aJsonObject *owArr; + +typedef void (*owChangedType) (int , DeviceAddress, int) ; + +#define _2482 // HW driver + +#ifdef _2482 +#include +#else +#define ONE_WIRE_BUS A0 +#endif + +extern OneWire *net; + +extern DallasTemperature *sensors; +extern DeviceAddress *term ; +extern int *regs ; +extern uint16_t *wstat; +extern int t_count; +extern short si; + +extern owChangedType owChanged; + + + +int owUpdate(); +int owSetup(owChangedType owCh); +void owLoop(); +void owIdle(void) ; +int owFind(DeviceAddress addr); +void owAdd (DeviceAddress addr); + diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..dca67ea --- /dev/null +++ b/utils.cpp @@ -0,0 +1,41 @@ +#include "utils.h" + +void PrintBytes(uint8_t* addr, uint8_t count, bool newline) { + for (uint8_t i = 0; i < count; i++) { + Serial.print(addr[i]>>4, HEX); + Serial.print(addr[i]&0x0f, HEX); + } + if (newline) + Serial.println(); +} + +const char HEXSTR[]="0123456789ABCDEF"; + +void SetBytes(uint8_t* addr, uint8_t count, char * out) { + // Serial.println("SB:"); + for (uint8_t i = 0; i < count; i++) { + *(out++)=HEXSTR[(addr[i]>>4)]; + *(out++)=HEXSTR[(addr[i]&0x0f)]; + } + *out=0; + +} + + +byte HEX2DEC(char i) +{ byte v; +if ('a' <= i && i <='f') { v=i-97+10; } + else if ('A' <= i && i <='F') { v=i-65+10; } + else if ('0' <= i && i <='9') { v=i-48; } + return v; + } + +void SetAddr(char * out, uint8_t* addr) { + + for (uint8_t i = 0; i < 8; i++) { + *addr=HEX2DEC(*out++)<<4; + *addr++|=HEX2DEC(*out++); + } +} + + diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..5e10133 --- /dev/null +++ b/utils.h @@ -0,0 +1,5 @@ +#include +void PrintBytes(uint8_t* addr, uint8_t count, bool newline=0); +void SetBytes(uint8_t* addr, uint8_t count, char * out); +void SetAddr(char * out, uint8_t* addr); +uint8_t HEX2DEC(char i);