1010#include < LoggerNode.h>
1111#include < Homie.hpp>
1212
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+ */
1318RelaisNode::RelaisNode (uint16_t defaults, uint16_t invert, uint16_t inputmask) :
1419 HomieNode(" Relais" , " switch16" ),
1520 relais_bitset((defaults^invert) | inputmask),
1621 invert_bitset(invert),
1722 input_mask(inputmask),
1823 input_data(0x0000 ),
24+ updateMaskLoop(0x0000 ),
1925 io(0x20 , false ) {
2026 for (uint_fast8_t i = 0 ; i < 16 ; i++) {
2127 bool in = ((input_mask & (1 << i)) != 0 );
@@ -24,35 +30,71 @@ RelaisNode::RelaisNode(uint16_t defaults, uint16_t invert, uint16_t inputmask) :
2430 advertiseRange (" in" ,1 ,16 ).settable ();
2531}
2632
33+ /* * only thing to do in setup(): Initial write of output values to PCF
34+ *
35+ */
2736void RelaisNode::setup () {
2837 updateRelais (0 ); // write to PCF only
2938}
3039
40+ /* * only thing to do in onReadyToOperate(): Send initial values to Homie MQTT
41+ *
42+ */
3143void RelaisNode::onReadyToOperate () {
3244 LN.log (" RelaisNode" , LoggerNode::DEBUG, " Ready" );
33- delay (200 );
3445 RelaisNode::updateRelais (0xFFFF );
3546}
3647
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 () {
4052 uint16_t input = io.read16 ();
4153 uint16_t diff = ((input ^ input_data) & input_mask);
4254 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 ) {
4657 bool on = (input_data & (1 << i)) != 0 ;
4758 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" );
5165 }
5266 }
5367 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+ }
5489
5590}
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+ */
5698bool RelaisNode::handleInput (const String &property, const HomieRange& range, const String &value) {
5799 int16_t id = range.index ;
58100 if (id <= 0 || id > 16 ) {
@@ -72,31 +114,16 @@ bool RelaisNode::handleInput(const String &property, const HomieRange& range, c
72114 } else {
73115 relais_bitset &= ~selected_bit;
74116 }
75- updateRelais ( selected_bit) ;
117+ updateMaskLoop |= selected_bit;
76118 return true ;
77119}
78120
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+ */
100127void RelaisNode::updateRelais (uint16_t updateMask) {
101128 static uint16_t last = relais_bitset;
102129 LN.logf (" RelaisNode::updateRelais()" , LoggerNode::DEBUG, " Value: %x (Update: %x)" , relais_bitset, updateMask);
0 commit comments