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);