10
10
#include < LoggerNode.h>
11
11
#include < Homie.hpp>
12
12
13
+ /* * Constructor. Use it to configure the inputs and outputs
14
+ * @param defaults initial default value
15
+ * @param invert If a bit is set, the corresponding IO is inverted (OUTPUT only, also inverts initial value)
16
+ * @param inputmask: If a bit is set, the corresponding IO is configured as INPUT_PULLUP, if not, as OUTPUT
17
+ */
13
18
RelaisNode::RelaisNode (uint16_t defaults, uint16_t invert, uint16_t inputmask) :
14
19
HomieNode(" Relais" , " switch16" ),
15
20
relais_bitset((defaults^invert) | inputmask),
16
21
invert_bitset(invert),
17
22
input_mask(inputmask),
18
23
input_data(0x0000 ),
24
+ updateMaskLoop(0x0000 ),
19
25
io(0x20 , false ) {
20
26
for (uint_fast8_t i = 0 ; i < 16 ; i++) {
21
27
bool in = ((input_mask & (1 << i)) != 0 );
@@ -24,35 +30,71 @@ RelaisNode::RelaisNode(uint16_t defaults, uint16_t invert, uint16_t inputmask) :
24
30
advertiseRange (" in" ,1 ,16 ).settable ();
25
31
}
26
32
33
+ /* * only thing to do in setup(): Initial write of output values to PCF
34
+ *
35
+ */
27
36
void RelaisNode::setup () {
28
37
updateRelais (0 ); // write to PCF only
29
38
}
30
39
40
+ /* * only thing to do in onReadyToOperate(): Send initial values to Homie MQTT
41
+ *
42
+ */
31
43
void RelaisNode::onReadyToOperate () {
32
44
LN.log (" RelaisNode" , LoggerNode::DEBUG, " Ready" );
33
- delay (200 );
34
45
RelaisNode::updateRelais (0xFFFF );
35
46
}
36
47
37
- void RelaisNode::loop () {
38
- static uint_fast8_t count = 0 ;
39
- if (!(++count % 8 )==0 ) return ; // read I2C on every 8th cycle only
48
+ /* * Read input from IO device
49
+ * Checks for difference in the selected bits (every bit set in field input_mask).
50
+ * In case of difference, the new property (ranged index) of the RelaisNode is send over MQTT */
51
+ void RelaisNode::readInputs () {
40
52
uint16_t input = io.read16 ();
41
53
uint16_t diff = ((input ^ input_data) & input_mask);
42
54
if (diff == 0 ) return ;
43
- for (uint_fast8_t i = 0 ; i<16 ; i++) {
44
- if ((diff & (1 << i)) != 0 )
45
- {
55
+ for (uint_fast8_t i = 0 ; i < 16 ; i++) {
56
+ if ((diff & (1 << i)) != 0 ) {
46
57
bool on = (input_data & (1 << i)) != 0 ;
47
58
bool inverted = (invert_bitset & (1 << i)) != 0 ;
48
- LN.logf (" RN::loop" , LoggerNode::INFO, " Input %d changed to %c%s, new: %x, old: %x, diff: %x" ,
49
- i, inverted ? ' ~' :' ' , on ? " On" : " Off" , input, input_data, diff);
50
- setProperty (" in" ).setRange (i+1 ).send (on ^ inverted ? " ON" : " OFF" );
59
+ LN.logf (" RN::loop" , LoggerNode::INFO,
60
+ " Input %d changed to %c%s, new: %x, old: %x, diff: %x" , i,
61
+ inverted ? ' ~' : ' ' , on ? " On" : " Off" , input, input_data,
62
+ diff);
63
+ setProperty (" in" ).setRange (i + 1 ).send (
64
+ on ^ inverted ? " ON" : " OFF" );
51
65
}
52
66
}
53
67
input_data = input;
68
+ }
69
+
70
+ /* * loop() is called every cycle from Homie
71
+ * Overrides the HomieNode::loop() method. Every 8th cycle it checks the inputs for changes.
72
+ * Furthermore it updates the outputs if a change has occured.
73
+ * To do the actual change of output within the loop() function is necessary
74
+ * because the handleInput() method is running in the network task of the NON-OS SDK
75
+ * due to the use of async IO. If a write would occur during the handleInput there
76
+ * is a race condition between writing the output and reading the input that may disturb
77
+ * the I2C communication or may give false readings and/or writings.
78
+ *
79
+ * See also https://euphi.github.io/2018/03/31/ArduinoESP8266-multipleTasks.html
80
+ *
81
+ */
82
+ void RelaisNode::loop () {
83
+ static uint_fast8_t count = 0 ;
84
+ if ((++count % 8 )==0 ) readInputs (); // read I2C on every 8th cycle only
85
+ if (updateMaskLoop) {
86
+ updateRelais (updateMaskLoop);
87
+ updateMaskLoop = 0x0000 ;
88
+ }
54
89
55
90
}
91
+
92
+ /* * handleInput() handles the received MQTT messages from Homie
93
+ *
94
+ * The bit to change is
95
+ * The property is not checked (but this is done by homie when evaluating the range)
96
+ *
97
+ */
56
98
bool RelaisNode::handleInput (const String &property, const HomieRange& range, const String &value) {
57
99
int16_t id = range.index ;
58
100
if (id <= 0 || id > 16 ) {
@@ -72,31 +114,16 @@ bool RelaisNode::handleInput(const String &property, const HomieRange& range, c
72
114
} else {
73
115
relais_bitset &= ~selected_bit;
74
116
}
75
- updateRelais ( selected_bit) ;
117
+ updateMaskLoop |= selected_bit;
76
118
return true ;
77
119
}
78
120
79
- //
80
- // void RelaisNode::drawFrame(OLEDDisplay& display, OLEDDisplayUiState& state, int16_t x, int16_t y) {
81
- // bool blink = ((millis() >> 7) % 2) != 0;
82
- // display.setFont(ArialMT_Plain_16);
83
- // display.drawString(0+x,16,"Relais");
84
- // for (uint_fast8_t i=0; i<8;i++) {
85
- // int16_t xpos = (i*8)+4;
86
- // int16_t ypos = 40;
87
- // if (((i + 1) == encoder.state()) && blink) continue;
88
- // bool on = (relais_bitset & (1 << i)) != 0;
89
- // display.drawRect(xpos,ypos,on?6:5,on?6:5);
90
- // if (on) {
91
- // display.drawRect(xpos+1,ypos+1,4,4);
92
- // display.drawRect(xpos+2,ypos+2,2,2);
93
- // }
94
- // }
95
- // display.drawHorizontalLine(0,60,128);
96
- // }
97
-
98
-
99
-
121
+ /* * helper method to update output state
122
+ * @param updateMask bits that needs to be updated on MQTT
123
+ *
124
+ * This method write all data to the I2C device.
125
+ * Furthermore it checks which bits should be updated on Homie MQTT and sends their state
126
+ */
100
127
void RelaisNode::updateRelais (uint16_t updateMask) {
101
128
static uint16_t last = relais_bitset;
102
129
LN.logf (" RelaisNode::updateRelais()" , LoggerNode::DEBUG, " Value: %x (Update: %x)" , relais_bitset, updateMask);
0 commit comments