8#if defined(__ESP32__) || defined(__ESP__)
9#define G_INT_ATTR IRAM_ATTR
14#define USTD_SW_MAX_IRQS (10)
16volatile unsigned long pSwIrqCounter[USTD_SW_MAX_IRQS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
17volatile unsigned long pSwLastIrq[USTD_SW_MAX_IRQS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
18volatile unsigned long pSwDebounceMs[USTD_SW_MAX_IRQS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
20void G_INT_ATTR ustd_sw_irq_master(uint8_t irqno) {
21 unsigned long curr = millis();
23 if (pSwDebounceMs[irqno]) {
24 if (timeDiff(pSwLastIrq[irqno], curr) < pSwDebounceMs[irqno]) {
29 ++pSwIrqCounter[irqno];
30 pSwLastIrq[irqno] = curr;
34void G_INT_ATTR ustd_sw_irq0() {
35 ustd_sw_irq_master(0);
37void G_INT_ATTR ustd_sw_irq1() {
38 ustd_sw_irq_master(1);
40void G_INT_ATTR ustd_sw_irq2() {
41 ustd_sw_irq_master(2);
43void G_INT_ATTR ustd_sw_irq3() {
44 ustd_sw_irq_master(3);
46void G_INT_ATTR ustd_sw_irq4() {
47 ustd_sw_irq_master(4);
49void G_INT_ATTR ustd_sw_irq5() {
50 ustd_sw_irq_master(5);
52void G_INT_ATTR ustd_sw_irq6() {
53 ustd_sw_irq_master(6);
55void G_INT_ATTR ustd_sw_irq7() {
56 ustd_sw_irq_master(7);
58void G_INT_ATTR ustd_sw_irq8() {
59 ustd_sw_irq_master(8);
61void G_INT_ATTR ustd_sw_irq9() {
62 ustd_sw_irq_master(9);
65void (*ustd_sw_irq_table[USTD_SW_MAX_IRQS])() = {ustd_sw_irq0, ustd_sw_irq1, ustd_sw_irq2, ustd_sw_irq3, ustd_sw_irq4,
66 ustd_sw_irq5, ustd_sw_irq6, ustd_sw_irq7, ustd_sw_irq8, ustd_sw_irq9};
68unsigned long getSwResetIrqCount(uint8_t irqno) {
69 unsigned long count = (
unsigned long)-1;
71 if (irqno < USTD_SW_MAX_IRQS) {
72 count = pSwIrqCounter[irqno];
73 pSwIrqCounter[irqno] = 0;
138 static const char *version;
163 int8_t interruptIndex;
164 unsigned long debounceTimeMs;
166 bool useInterrupt =
false;
168 unsigned long lastChangeMs = 0;
169 int physicalState = -1;
170 int logicalState = -1;
171 bool overriddenPhysicalState =
false;
172 bool overridePhysicalActive =
false;
174 bool bCounter =
false;
175 unsigned long counter = 0;
177 bool flipflop =
true;
178 unsigned long activeTimer = 0;
179 unsigned long timerDuration = 1000;
180 unsigned long startEvent = 0;
181 unsigned long durations[2] = {3000, 30000};
183 unsigned long lastStatePublish = 0;
184 unsigned int stateRefresh = 0;
185 bool initialStatePublish =
false;
186 bool initialStateIsPublished =
false;
190 String customTopic =
"", int8_t interruptIndex = -1,
unsigned long debounceTimeMs = 0)
191 : name(name), port(port), mode(mode), activeLogic(activeLogic), customTopic(customTopic),
192 interruptIndex(interruptIndex), debounceTimeMs(debounceTimeMs) {
211 detachInterrupt(ipin);
223 debounceTimeMs = (
unsigned long)ms;
262 timerDuration = duration;
265 overriddenPhysicalState =
false;
266 overridePhysicalActive =
false;
270 initialStateIsPublished =
false;
271 initialStatePublish =
true;
274 startEvent = (
unsigned long)-1;
282 pinMode(port, INPUT_PULLUP);
285 if (interruptIndex >= 0 && interruptIndex < USTD_SW_MAX_IRQS) {
286 ipin = digitalPinToInterrupt(port);
289 attachInterrupt(ipin, ustd_sw_irq_table[interruptIndex], FALLING);
292 attachInterrupt(ipin, ustd_sw_irq_table[interruptIndex], RISING);
295 attachInterrupt(ipin, ustd_sw_irq_table[interruptIndex], CHANGE);
298 pSwDebounceMs[interruptIndex] = debounceTimeMs;
304 auto ft = [=]() { this->loop(); };
305 tID = pSched->add(ft, name, 50000);
307 auto fnall = [=](String topic, String msg, String originator) {
308 this->subsMsg(topic, msg, originator);
310 pSched->subscribe(tID, name +
"/#", fnall);
311 pSched->subscribe(tID,
"mqtt/state", fnall);
322 if (logicalState != newLogicalState) {
323 logicalState = newLogicalState;
324 publishLogicalState(logicalState);
338 stateRefresh = logicalStateRefreshEverySecs;
347 setPhysicalState(!physicalState,
true);
356 setPhysicalState(
true,
true);
357 setPhysicalState(
false,
true);
361 unsigned long getTimestamp() {
363#if defined(__ARDUINO__) || defined(__ARM__) || defined(__RISC_V__)
364 return pSched->getUptime();
366 return (
unsigned long)time(
nullptr);
370 void publishCounter() {
372 sprintf(buf,
"%ld", counter);
374 pSched->publish(name +
"/switch/counter", buf);
375 pSched->publish(name +
"/sensor/counter", buf);
377 pSched->publish(name +
"/switch/counter",
"NaN");
378 pSched->publish(name +
"/sensor/counter",
"NaN");
382 void publishLogicalState(
bool lState) {
385 lastStatePublish = getTimestamp();
386 if (lState ==
true) {
397 pSched->publish(name +
"/switch/state", textState);
398 if (customTopic !=
"")
399 pSched->publish(customTopic, textState);
402 if (lState ==
true) {
403 pSched->publish(name +
"/switch/state",
"trigger");
404 if (customTopic !=
"")
405 pSched->publish(customTopic,
"trigger");
409 if (lState ==
false) {
410 pSched->publish(name +
"/switch/state",
"trigger");
411 if (customTopic !=
"")
412 pSched->publish(customTopic,
"trigger");
416 if (lState ==
true) {
417 startEvent = millis();
419 if (startEvent != (
unsigned long)-1) {
420 unsigned long dt = timeDiff(startEvent, millis());
422 sprintf(msg,
"%ld", dt);
423 pSched->publish(name +
"/switch/duration", msg);
424 if (dt < durations[0]) {
425 pSched->publish(name +
"/switch/shortpress",
"trigger");
426 }
else if (dt < durations[1]) {
427 pSched->publish(name +
"/switch/longpress",
"trigger");
429 pSched->publish(name +
"/switch/verylongpress",
"trigger");
435 pSched->publish(name +
"/binary_sensor/state", binaryState);
436 if (customTopic !=
"")
437 pSched->publish(customTopic, binaryState);
442 void decodeLogicalState(
bool physicalState) {
452 if (physicalState ==
false) {
453 flipflop = !flipflop;
458 if (physicalState ==
false) {
459 activeTimer = millis();
467 void setPhysicalState(
bool newState,
bool override) {
472 overriddenPhysicalState = physicalState;
473 overridePhysicalActive =
true;
474 if (newState != physicalState) {
475 physicalState = newState;
476 decodeLogicalState(newState);
479 if (overridePhysicalActive && newState != overriddenPhysicalState) {
480 overridePhysicalActive =
false;
482 if (overridePhysicalActive)
485 if (timeDiff(lastChangeMs, millis()) > debounceTimeMs || useInterrupt) {
486 lastChangeMs = millis();
487 physicalState = newState;
488 decodeLogicalState(physicalState);
496 unsigned long count = getSwResetIrqCount(interruptIndex);
497 int curstate = digitalRead(port);
500 sprintf(msg,
"%ld", count);
501 pSched->publish(name +
"/switch/irqcount/0", msg);
502 if (curstate == HIGH)
508 for (
unsigned long i = 0; i < count; i++) {
510 setPhysicalState(
false,
false);
511 setPhysicalState(
true,
false);
513 setPhysicalState(
true,
false);
514 setPhysicalState(
false,
false);
519 for (
unsigned long i = 0; i < count; i++) {
521 setPhysicalState(
true,
false);
522 setPhysicalState(
false,
false);
524 setPhysicalState(
false,
false);
525 setPhysicalState(
true,
false);
530 bool iState = ((count % 2) == 0);
533 for (
unsigned long i = 0; i < count; i++) {
535 setPhysicalState(iState,
false);
537 setPhysicalState(!iState,
false);
545 int newstate = digitalRead(port);
546 if (newstate == HIGH)
551 newstate = !newstate;
552 setPhysicalState(newstate,
false);
559 if (timeDiff(activeTimer, millis()) > timerDuration) {
564 if (stateRefresh != 0 || (initialStateIsPublished =
false && initialStatePublish ==
true)) {
566 if (getTimestamp() - lastStatePublish > stateRefresh || (initialStateIsPublished =
false && initialStatePublish ==
true)) {
567 publishLogicalState(logicalState);
568 if (bCounter) publishCounter();
569 initialStateIsPublished =
true;
575 void subsMsg(String topic, String msg, String originator) {
576 if (topic == name +
"/switch/state/get" || topic == name +
"/binary_sensor/state/get") {
577 publishLogicalState(logicalState);
578 }
else if (topic == name +
"/switch/counter/get" || topic == name +
"/sensor/counter/get") {
580 }
else if (topic == name +
"/switch/physicalstate/get") {
586 pSched->publish(name +
"/switch/physicalstate", buf);
587 }
else if (topic == name +
"/switch/mode/set") {
590 strncpy(buf, msg.c_str(), 31);
591 char *p = strchr(buf,
' ');
602 if (!strcmp(buf,
"default")) {
604 }
else if (!strcmp(buf,
"rising")) {
606 }
else if (!strcmp(buf,
"falling")) {
608 }
else if (!strcmp(buf,
"flipflop")) {
610 }
else if (!strcmp(buf,
"binary_sensor")) {
612 }
else if (!strcmp(buf,
"timer")) {
613 unsigned long dur = 1000;
617 }
else if (!strcmp(buf,
"duration")) {
619 durations[1] = 30000;
621 durations[0] = atol(p);
623 durations[1] = atol(p2);
625 if (durations[0] > durations[1]) {
626 durations[1] = (
unsigned long)-1;
630 }
else if (topic == name +
"/switch/set") {
633 strncpy(buf, msg.c_str(), 31);
634 if (!strcmp(buf,
"on") || !strcmp(buf,
"true")) {
638 if (!strcmp(buf,
"off") || !strcmp(buf,
"false")) {
642 if (!strcmp(buf,
"toggle")) {
645 if (!strcmp(buf,
"pulse")) {
648 }
else if (topic == name +
"/switch/debounce/get") {
650 sprintf(buf,
"%ld", debounceTimeMs);
651 pSched->publish(name +
"/debounce", buf);
652 }
else if (topic == name +
"/switch/debounce/set") {
653 long dbt = atol(msg.c_str());
655 }
else if (topic ==
"mqtt/state") {
657 if (msg ==
"connected") {
658 publishLogicalState(logicalState);
664 }
else if (topic == name +
"/switch/counter/start") {
666 }
else if (topic == name +
"/switch/counter/stop") {
672const char *Switch::version =
"0.1.0";
mupplet-core GPIO Switch class
Definition: mup_switch.h:136
Mode
Definition: mup_switch.h:140
@ Falling
Definition: mup_switch.h:145
@ Duration
Definition: mup_switch.h:149
@ Flipflop
Definition: mup_switch.h:147
@ Default
Definition: mup_switch.h:141
@ Rising
Definition: mup_switch.h:143
@ BinarySensor
Definition: mup_switch.h:151
@ Timer
Definition: mup_switch.h:148
void setToggle()
Definition: mup_switch.h:341
void setTimerDuration(unsigned long ms)
Definition: mup_switch.h:238
void setLogicalState(bool newLogicalState)
Definition: mup_switch.h:314
void setPulse()
Definition: mup_switch.h:350
void activateCounter(bool bActive)
Definition: mup_switch.h:226
void setDebounce(long ms)
Definition: mup_switch.h:214
void setMode(Mode newmode, unsigned long duration=0)
Definition: mup_switch.h:248
void begin(Scheduler *_pSched)
Definition: mup_switch.h:277
Switch(String name, uint8_t port, Mode mode=Mode::Default, bool activeLogic=false, String customTopic="", int8_t interruptIndex=-1, unsigned long debounceTimeMs=0)
Definition: mup_switch.h:189
void setStateRefresh(int logicalStateRefreshEverySecs)
Definition: mup_switch.h:334
The muwerk namespace.
Definition: home_assistant.h:10