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" " 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" " Input %d changed to %c%s, new: %x, old: %x, diff: %x" 
49- 					i, inverted ? ' ~' '  ' " On" " Off" 
50- 			setProperty (" in" setRange (i+1 ).send (on ^ inverted ? " ON" " OFF" 
59+ 			LN.logf (" RN::loop" 
60+ 					" Input %d changed to %c%s, new: %x, old: %x, diff: %x" 
61+ 					inverted ? ' ~' '  ' " On" " Off" 
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()" " Value: %x (Update: %x)" 
0 commit comments