single, double, tripple click, longpress, repeat logic for contact inputs are added

This commit is contained in:
2020-01-07 00:15:13 +03:00
parent 8a3980272e
commit 951d0d4383
6 changed files with 425 additions and 55 deletions

View File

@@ -113,7 +113,7 @@ void Input::Parse()
void Input::setup()
{
if (!isValid() || (!root)) return;
/*
#ifndef CSSHDC_DISABLE
if (inType == IN_CCS811)
{
@@ -127,6 +127,51 @@ if (!isValid() || (!root)) return;
}
// TODO rest types setup
#endif
*/
store->aslong=0;
uint8_t inputPinMode = INPUT; //if IN_ACTIVE_HIGH
switch (inType)
{
case IN_PUSH_ON:
case IN_PUSH_TOGGLE :
inputPinMode = INPUT_PULLUP;
case IN_PUSH_ON | IN_ACTIVE_HIGH:
case IN_PUSH_TOGGLE | IN_ACTIVE_HIGH:
pinMode(pin, inputPinMode);
store->state=IS_IDLE;
break;
case IN_ANALOG:
inputPinMode = INPUT_PULLUP;
case IN_ANALOG | IN_ACTIVE_HIGH:
pinMode(pin, inputPinMode);
break;
case IN_DHT22:
case IN_COUNTER:
case IN_UPTIME:
break;
#ifndef CSSHDC_DISABLE
case IN_CCS811:
{
in_ccs811 ccs811(this);
ccs811.Setup();
}
break;
case IN_HDC1080:
{
in_hdc1080 hdc1080(this);
hdc1080.Setup();
}
break;
#endif
} //switch
}
@@ -140,17 +185,18 @@ if (!isValid()) return -1;
switch (cause) {
case CHECK_INPUT: //Fast polling
case CHECK_INTERRUPT: //Realtime polling
switch (inType)
{
case IN_PUSH_ON:
case IN_PUSH_ON | IN_ACTIVE_HIGH:
case IN_PUSH_TOGGLE :
case IN_PUSH_TOGGLE | IN_ACTIVE_HIGH:
contactPoll();
contactPoll(cause);
break;
case IN_ANALOG:
case IN_ANALOG | IN_ACTIVE_HIGH:
analogPoll();
analogPoll(cause);
break;
// No fast polling
@@ -406,43 +452,331 @@ void Input::dht22Poll() {
}
#endif
void Input::contactPoll() {
boolean currentInputState;
bool Input::executeCommand(aJsonObject* cmd, char* defCmd)
{
if (!cmd) return false;
switch (cmd->type)
{
case aJson_String: //legacy - no action
break;
case aJson_Array: //array - recursive iterate
{
aJsonObject * command = cmd->child;
while (command)
{
executeCommand(command,defCmd);
command = command->next;
}
}
break;
case aJson_Object:
{
aJsonObject *item = aJson.getObjectItem(cmd, "item");
aJsonObject *icmd = aJson.getObjectItem(cmd, "icmd");
aJsonObject *ecmd = aJson.getObjectItem(cmd, "ecmd");
aJsonObject *emit = aJson.getObjectItem(cmd, "emit");
char * itemCommand;
if(icmd) itemCommand = icmd->valuestring;
else itemCommand = defCmd;
char * emitCommand;
if(ecmd) emitCommand = ecmd->valuestring;
else emitCommand = defCmd;
debugSerial << F("IN:") << (pin) << F(" : ") <<endl;
if (item) debugSerial << item->valuestring<< F(" -> ")<<itemCommand<<endl;
if (emit) debugSerial << emit->valuestring<< F(" -> ")<<emitCommand<<endl;
if (emit && emitCommand) {
/*
#if defined(ARDUINO_ARCH_STM32)
WiringPinMode inputPinMode;
#endif
#if defined(__SAM3X8E__)||defined(ARDUINO_ARCH_AVR)||defined(ARDUINO_ARCH_ESP8266)||defined(ARDUINO_ARCH_ESP32)
TODO implement
#ifdef WITH_DOMOTICZ
if (getIdxField())
{ (newValue) ? publishDataToDomoticz(0, emit, "{\"command\":\"switchlight\",\"idx\":%s,\"switchcmd\":\"On\"}",
: publishDataToDomoticz(0,emit,"{\"command\":\"switchlight\",\"idx\":%s,\"switchcmd\":\"Off\"}",getIdxField()); getIdxField())
: publishDataToDomoticz(0, emit,
"{\"command\":\"switchlight\",\"idx\":%s,\"switchcmd\":\"Off\"}",
getIdxField());
} else
#endif
*/
uint32_t inputPinMode;
uint8_t inputOnLevel;
if (inType & IN_ACTIVE_HIGH) {
inputOnLevel = HIGH;
inputPinMode = INPUT;
} else {
inputOnLevel = LOW;
inputPinMode = INPUT_PULLUP;
{
char addrstr[MQTT_TOPIC_LENGTH];
strncpy(addrstr,emit->valuestring,sizeof(addrstr));
if (mqttClient.connected() && !ethernetIdleCount)
{
if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,emit->valuestring);
mqttClient.publish(addrstr, emitCommand , true);
}
}
} // emit
if (item && itemCommand) {
//debugSerial <<F("Controlled item:")<< item->valuestring <<endl;
Item it(item->valuestring);
if (it.isValid()) it.Ctrl(itemCommand, true);
}
return true;
}
default:
return false;
} //switch type
}
// TODO Polling via timed interrupt with CHECK_INTERRUPT cause
bool Input::changeState(uint8_t newState, short cause)
{
if (!inputObj || !store) return false;
if (newState == IS_REQSTATE)
if (store->delayedState && cause != CHECK_INTERRUPT)
{
// Requested delayed change State and safe moment
newState=store->reqState; //Retrieve requested state
debugSerial<<F("Pended state retrieved:")<<newState;
}
pinMode(pin, inputPinMode);
else return true; // No pended State
else if (store->delayedState)
return false; //State changing is postponed already (( giving up
aJsonObject *cmd = NULL;
switch (newState)
{
case IS_IDLE:
switch (store->state)
{
case IS_RELEASED: //click
cmd = aJson.getObjectItem(inputObj, "click");
break;
case IS_RELEASED2: //doubleclick
cmd = aJson.getObjectItem(inputObj, "dclick");
break;
case IS_PRESSED3: //tripple click
cmd = aJson.getObjectItem(inputObj, "tclick");
break;
case IS_WAITPRESS: //do nothing
break;
default: //rcmd
cmd = aJson.getObjectItem(inputObj, "rcmd");
;
}
break;
case IS_PRESSED: //scmd
cmd = aJson.getObjectItem(inputObj, "scmd");
break;
case IS_PRESSED2: //scmd2
cmd = aJson.getObjectItem(inputObj, "scmd2");
break;
case IS_PRESSED3: //scmd3
cmd = aJson.getObjectItem(inputObj, "scmd3");
break;
case IS_RELEASED: //rcmd
case IS_WAITPRESS:
case IS_RELEASED2:
cmd = aJson.getObjectItem(inputObj, "rcmd");
break;
case IS_LONG: //lcmd
cmd = aJson.getObjectItem(inputObj, "lcmd");
break;
case IS_REPEAT: //rpcmd
cmd = aJson.getObjectItem(inputObj, "rpcmd");
break;
case IS_LONG2: //lcmd2
cmd = aJson.getObjectItem(inputObj, "lcmd2");
break;
case IS_REPEAT2: //rpcmd2
cmd = aJson.getObjectItem(inputObj, "rpcmd2");
break;
case IS_LONG3: //lcmd3
cmd = aJson.getObjectItem(inputObj, "lcmd3");
break;
case IS_REPEAT3: //rpcmd3
cmd = aJson.getObjectItem(inputObj, "rpcmd3");
break;
}
if (!cmd)
{
store->state=newState;
return true; //nothing to do
}
if (cause != CHECK_INTERRUPT)
{
executeCommand(cmd);
//Executed
store->state=newState;
store->delayedState=false;
return true;
}
else
{
//Postpone actual execution
store->reqState=store->state;
store->delayedState=true;
return true;
}
}
void Input::contactPoll(short cause) {
boolean currentInputState;
if (!store) return;
changeState(IS_REQSTATE,cause); //Check for postponed states transitions
uint8_t inputOnLevel;
if (inType & IN_ACTIVE_HIGH) inputOnLevel = HIGH;
else inputOnLevel = LOW;
currentInputState = (digitalRead(pin) == inputOnLevel);
if (currentInputState != store->currentValue) // value changed
switch (store->state) //Timer based transitions
{
case IS_PRESSED:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_LONG,0xFFFF)) changeState(IS_LONG, cause);
break;
case IS_LONG:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT,0xFFFF))
{
changeState(IS_REPEAT, cause);
store->timestamp16 = millis() & 0xFFFF;
}
break;
case IS_REPEAT:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT_PULSE,0xFFFF))
{
changeState(IS_REPEAT, cause);
store->timestamp16 = millis() & 0xFFFF;
}
break;
case IS_PRESSED2:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_LONG,0xFFFF)) changeState(IS_LONG2, cause);
break;
case IS_LONG2:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT,0xFFFF))
{
changeState(IS_REPEAT2, cause);
store->timestamp16 = millis() & 0xFFFF;
}
break;
case IS_REPEAT2:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT_PULSE,0xFFFF))
{
changeState(IS_REPEAT2, cause);
store->timestamp16 = millis() & 0xFFFF;
}
break;
case IS_PRESSED3:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_LONG,0xFFFF)) changeState(IS_LONG3, cause);
break;
case IS_LONG3:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT,0xFFFF))
{
changeState(IS_REPEAT3, cause);
store->timestamp16 = millis() & 0xFFFF;
}
break;
case IS_REPEAT3:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT_PULSE,0xFFFF))
{
changeState(IS_REPEAT3, cause);
store->timestamp16 = millis() & 0xFFFF;
}
break;
case IS_RELEASED:
case IS_RELEASED2:
case IS_WAITPRESS:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_IDLE,0xFFFF)) changeState(IS_IDLE, cause);
break;
}
if (currentInputState != store->lastValue) // value changed
{
if (store->bounce) store->bounce = store->bounce - 1;
else //confirmed change
{
if (inType & IN_PUSH_TOGGLE) {
store->timestamp16 = millis() & 0xFFFF; //Saving timestamp of changing
if (inType & IN_PUSH_TOGGLE) { //To refactore
if (currentInputState) { //react on leading edge only (change from 0 to 1)
store->logicState = !store->logicState;
store->currentValue = currentInputState;
store->lastValue = currentInputState;
onContactChanged(store->logicState);
}
} else {
store->logicState = currentInputState;
store->currentValue = currentInputState;
onContactChanged(currentInputState);
} else
{
onContactChanged(currentInputState); //Legacy input - to remove later
bool res = true;
if (currentInputState) //Button pressed state transitions
switch (store->state)
{
case IS_IDLE:
res = changeState(IS_PRESSED, cause);
break;
case IS_RELEASED:
case IS_WAITPRESS:
res = changeState(IS_PRESSED2, cause);
break;
case IS_RELEASED2:
res = changeState(IS_PRESSED3, cause);
break;
}
else
switch (store->state) //Button released state transitions
{
case IS_PRESSED:
res = changeState(IS_RELEASED, cause);
break;
case IS_LONG:
case IS_REPEAT:
res = changeState(IS_WAITPRESS, cause);
break;
case IS_PRESSED2:
res = changeState(IS_RELEASED2, cause);
break;
case IS_LONG2:
case IS_REPEAT2:
case IS_LONG3:
case IS_REPEAT3:
case IS_PRESSED3:
res = changeState(IS_IDLE, cause);
break;
}
if (res) { //State changed or postponed
store->logicState = currentInputState;
store->lastValue = currentInputState;
}
}
// store->currentValue = currentInputState;
}
@@ -451,23 +785,26 @@ void Input::contactPoll() {
}
void Input::analogPoll() {
void Input::analogPoll(short cause) {
int16_t inputVal;
int32_t mappedInputVal; // 10x inputVal
aJsonObject *inputMap = aJson.getObjectItem(inputObj, "map");
int16_t Noize = ANALOG_NOIZE;
short simple = 0;
uint32_t inputPinMode;
// uint32_t inputPinMode;
int max=1024*10;
int min=0;
/*
if (inType & IN_ACTIVE_HIGH) {
inputPinMode = INPUT;
} else {
inputPinMode = INPUT_PULLUP;
}
pinMode(pin, inputPinMode);
*/
inputVal = analogRead(pin);
// Mapping
if (inputMap && inputMap->type == aJson_Array)
@@ -522,11 +859,13 @@ void Input::analogPoll() {
void Input::onContactChanged(int newValue) {
debugSerial << F("IN:") << (pin) << F("=") << newValue << endl;
aJsonObject *item = aJson.getObjectItem(inputObj, "item");
aJsonObject *emit = aJson.getObjectItem(inputObj, "emit");
if (!item && !emit) return;
aJsonObject *scmd = aJson.getObjectItem(inputObj, "scmd");
aJsonObject *rcmd = aJson.getObjectItem(inputObj, "rcmd");
aJsonObject *emit = aJson.getObjectItem(inputObj, "emit");
debugSerial << F("LEGACY IN:") << (pin) << F("=") << newValue << endl;
if (emit) {
#ifdef WITH_DOMOTICZ
if (getIdxField())