Skip to content

Commit ffb13d0

Browse files
committed
New InputPinArrayNode for simple IO + doc improvements
+ InputPinArrayNode can be used to define a vector of GPIOs that are configured as INPUT_PULLUP and announced as array of Contacts. + created doxyfile for doc generation with doxygen + added some doxygen-style doc-strings + small refactoring of RelaisNode
1 parent 2f66c2a commit ffb13d0

7 files changed

+2638
-33
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
/.project
66
/.settings
77
src/main.cpp
8+
/html/
9+
/latex/

ESP_Heizungscontroller.doxyfile

Lines changed: 2494 additions & 0 deletions
Large diffs are not rendered by default.

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "HomieNodeCollection",
3-
"version": "0.9.1",
3+
"version": "0.9.2",
44
"keywords": "Homie, sensor, temperature, display",
55
"description": "Collection of Node implementations for the Homie-ESP8266 library.",
66
"repository": {

src/InputPinArrayNode.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* InputPinArrayNode.cpp
3+
*
4+
* Created on: 09.04.2018
5+
* Author: ian
6+
*/
7+
8+
#include <InputPinArrayNode.h>
9+
10+
InputPinArrayNode::InputPinArrayNode(std::vector<uint8_t> pins, InputPinChangeEventHandler& cb):
11+
HomieNode("Input", "Contact"),
12+
inputPins(pins),
13+
storedPins(pins.size()),
14+
callback(cb) {
15+
advertiseRange("pin", 0, inputPins.size()-1);
16+
}
17+
18+
void InputPinArrayNode::setup() {
19+
for (uint8_t pin : inputPins) {
20+
pinMode(pin, INPUT_PULLUP);
21+
}
22+
}
23+
24+
void InputPinArrayNode::loop() {
25+
uint8_t idx = 0;
26+
for (uint8_t pin : inputPins) {
27+
bool curState = digitalRead(pin);
28+
bool oldState = storedPins[idx];
29+
if (curState != oldState) {
30+
setProperty("pin").setRange(idx).setRetained(true).send(curState ? "OPEN":"CLOSED");
31+
storedPins[idx] = curState;
32+
callback(idx, curState);
33+
}
34+
++idx;
35+
}
36+
}
37+
38+
void InputPinArrayNode::onReadyToOperate() {
39+
uint8_t idx = 0;
40+
for (uint8_t pin : inputPins) {
41+
bool curState = digitalRead(pin);
42+
storedPins[idx] = curState;
43+
setProperty("pin").setRange(idx).setRetained(true).send(curState ? "OPEN":"CLOSED");
44+
idx++;
45+
}
46+
}

src/InputPinArrayNode.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* InputPinArrayNode.h
3+
*
4+
* Created on: 09.04.2018
5+
* Author: ian
6+
*/
7+
8+
#ifndef SRC_INPUTPINARRAYNODE_H_
9+
#define SRC_INPUTPINARRAYNODE_H_
10+
11+
#include <HomieNode.hpp>
12+
13+
14+
15+
class InputPinArrayNode: public HomieNode {
16+
17+
18+
public:
19+
typedef std::function<bool(uint8_t idx, bool state)> InputPinChangeEventHandler;
20+
InputPinArrayNode(std::vector<uint8_t> pins, InputPinChangeEventHandler& cb);
21+
22+
protected:
23+
virtual void setup() override;
24+
virtual void loop() override;
25+
virtual void onReadyToOperate() override;
26+
27+
private:
28+
std::vector<uint8_t> inputPins;
29+
std::vector<bool> storedPins;
30+
InputPinChangeEventHandler & callback;
31+
};
32+
33+
#endif /* SRC_INPUTPINARRAYNODE_H_ */

src/RelaisNode.cpp

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@
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+
*/
1318
RelaisNode::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+
*/
2736
void 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+
*/
3143
void 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+
*/
5698
bool 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+
*/
100127
void 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);

src/RelaisNode.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ class RelaisNode : public HomieNode {
2727

2828
private:
2929
void updateRelais(uint16_t updateMask=0xFFFF);
30+
void readInputs();
3031

3132
uint16_t relais_bitset; // stores actual value
3233
uint16_t invert_bitset; // If bit is set, value written to PCF8575 will be inverted
3334
uint16_t input_mask; // If bit is set, related pin will be configured as (quasi-)input
3435
uint16_t input_data;
3536

37+
uint16_t updateMaskLoop;
38+
3639
PCF8575 io;
3740

3841
};

0 commit comments

Comments
 (0)