summaryrefslogtreecommitdiffstats
path: root/mysensors
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2023-10-12 22:15:29 +0200
committermanuel <manuel@mausz.at>2023-10-12 22:15:29 +0200
commit68c812fe8c690b8a24637076f83537d6a2e94f32 (patch)
tree071992659d4b4e0329f0969e4af0d0c894f112f6 /mysensors
parent28a73cb136b18b0407e0e15de4fafa881cc40114 (diff)
downloadarduino-68c812fe8c690b8a24637076f83537d6a2e94f32.tar.gz
arduino-68c812fe8c690b8a24637076f83537d6a2e94f32.tar.bz2
arduino-68c812fe8c690b8a24637076f83537d6a2e94f32.zip
Move mysensors project to subdirectory
Diffstat (limited to 'mysensors')
-rw-r--r--mysensors/couch_light/.gitignore5
-rw-r--r--mysensors/couch_light/platformio.ini26
-rw-r--r--mysensors/couch_light/src/main.cpp283
-rw-r--r--mysensors/lr_switch/.gitignore5
-rw-r--r--mysensors/lr_switch/platformio.ini26
-rw-r--r--mysensors/lr_switch/src/main.cpp197
-rw-r--r--mysensors/rgbtv_light/.gitignore5
-rw-r--r--mysensors/rgbtv_light/platformio.ini26
-rw-r--r--mysensors/rgbtv_light/src/main.cpp241
-rw-r--r--mysensors/testnode/.gitignore5
-rw-r--r--mysensors/testnode/platformio.ini26
-rw-r--r--mysensors/testnode/src/main.cpp316
-rw-r--r--mysensors/tv_light/.gitignore5
-rw-r--r--mysensors/tv_light/platformio.ini26
-rw-r--r--mysensors/tv_light/src/main.cpp358
15 files changed, 1550 insertions, 0 deletions
diff --git a/mysensors/couch_light/.gitignore b/mysensors/couch_light/.gitignore
new file mode 100644
index 0000000..89cc49c
--- /dev/null
+++ b/mysensors/couch_light/.gitignore
@@ -0,0 +1,5 @@
1.pio
2.vscode/.browse.c_cpp.db*
3.vscode/c_cpp_properties.json
4.vscode/launch.json
5.vscode/ipch
diff --git a/mysensors/couch_light/platformio.ini b/mysensors/couch_light/platformio.ini
new file mode 100644
index 0000000..d4ceb9b
--- /dev/null
+++ b/mysensors/couch_light/platformio.ini
@@ -0,0 +1,26 @@
1; PlatformIO Project Configuration File
2;
3; Build options: build flags, source filter, extra scripting
4; Upload options: custom port, speed and extra flags
5; Library options: dependencies, extra library storages
6;
7; Please visit documentation for the other options and examples
8; http://docs.platformio.org/en/stable/projectconf.html
9
10[platformio]
11default_envs = pro8MHzatmega328
12
13[env:miniatmega328]
14platform=atmelavr
15board=miniatmega328
16framework=arduino
17
18[env:pro8MHzatmega328]
19platform=atmelavr
20board=pro8MHzatmega328
21framework=arduino
22; minicore/optirun uses 38400 for 8MHz
23upload_speed=38400
24
25[platformio]
26lib_dir=/home/manuel/coding/Arduino/libraries
diff --git a/mysensors/couch_light/src/main.cpp b/mysensors/couch_light/src/main.cpp
new file mode 100644
index 0000000..b99381a
--- /dev/null
+++ b/mysensors/couch_light/src/main.cpp
@@ -0,0 +1,283 @@
1/**
2 * The MySensors Arduino library handles the wireless radio link and protocol
3 * between your home built sensors/actuators and HA controller of choice.
4 * The sensors forms a self healing radio network with optional repeaters. Each
5 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
6 * network topology allowing messages to be routed to nodes.
7 */
8
9// Enable debug prints to serial monitor
10//#define MY_DEBUG
11
12// configure radio
13#define MY_RADIO_RFM69
14
15/** @brief RFM69 frequency to use (RF69_433MHZ for 433MHz, RF69_868MHZ for 868MHz or RF69_915MHZ for 915MHz). */
16#define MY_RFM69_FREQUENCY RF69_868MHZ
17
18/** @brief Enable this if you're running the RFM69HW model. */
19//#define MY_IS_RFM69HW
20
21/** @brief RFM69 Network ID. Use the same for all nodes that will talk to each other. */
22#define MY_RFM69_NETWORKID 1
23
24/** @brief Node id defaults to AUTO (tries to fetch id from controller). */
25#define MY_NODE_ID 1
26
27/** @brief If set, transport traffic is unmonitored and GW connection is optional */
28#define MY_TRANSPORT_DONT_CARE_MODE
29
30/** @brief Node parent defaults to AUTO (tries to find a parent automatically). */
31#define MY_PARENT_NODE_ID 0
32
33/** @brief The user-defined AES key to use for EEPROM personalization */
34#include "aes_key.h"
35
36// Enable repeater functionality for this node
37//#define MY_REPEATER_FEATURE
38
39/** @brief Enables RFM69 automatic transmit power control class. */
40//#define MY_RFM69_ATC
41
42#ifdef MY_AES_KEY
43/** @brief enables RFM69 encryption */
44#define MY_RFM69_ENABLE_ENCRYPTION
45#endif
46
47#include <Arduino.h>
48#include <MySensors.h>
49#include <Bounce2.h>
50
51enum sensor_type : uint8_t
52{
53 SENSOR_RELAY = (1u << 0),
54 SENSOR_DIMMER = (1u << 1),
55 SENSOR_BUTTON = (1u << 2),
56};
57
58struct sensor_t
59{
60 uint8_t id;
61 uint8_t type;
62 struct
63 {
64 uint8_t pin; // relay pin
65 } relay;
66 struct
67 {
68 uint8_t pin; // push button pin
69 Bounce bounce;
70 } button;
71};
72
73struct sensor_t sensors[] = {
74 {
75 .id = 0,
76 .type = SENSOR_RELAY | SENSOR_BUTTON,
77 .relay = { .pin = 4 },
78 .button = { .pin = 6, .bounce = Bounce() },
79 },
80 {
81 .id = 1,
82 .type = SENSOR_RELAY | SENSOR_BUTTON,
83 .relay = { .pin = 5 },
84 .button = { .pin = 7, .bounce = Bounce() },
85 },
86};
87
88//#define SAVE_RESTORE
89
90#define NUM(a) (sizeof(a) / sizeof(*a))
91
92#define RELAY_ON 1 // GPIO value to write to turn on attached relay
93#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
94
95#define TEMP_SENSOR_ID 254
96#define TEMP_READ_INTERVAL 1000L // read temp every 1 sec
97#define TEMP_N_READS_MSG 60*60 // force temp message every n reads
98#define TEMP_OFFSET 0
99
100MyMessage msg(0, V_STATUS);
101
102inline void checkTemperature(void);
103bool relayRead(struct sensor_t *sensor);
104void relayWrite(struct sensor_t *sensor, bool state, bool send_update=false);
105void flipRelay(struct sensor_t *sensor, bool send_update=false);
106void checkButtons(void);
107
108void before()
109{
110 // set relay pins to output mode + restore to last known state
111 for (uint8_t i = 0; i < NUM(sensors); i++)
112 {
113 struct sensor_t *sensor = &sensors[i];
114 if (sensor->type & SENSOR_RELAY)
115 {
116 pinMode(sensor->relay.pin, OUTPUT);
117#ifdef SAVE_RESTORE
118 digitalWrite(sensor->relay.pin, loadState(sensor->id) ? RELAY_ON : RELAY_OFF);
119#else
120 digitalWrite(sensor->relay.pin, RELAY_OFF);
121#endif
122 }
123 }
124
125#ifdef MY_AES_KEY
126 const uint8_t user_aes_key[16] = { MY_AES_KEY };
127 uint8_t cur_aes_key[16];
128 hwReadConfigBlock((void*)&cur_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(cur_aes_key));
129 if (memcmp(&user_aes_key, &cur_aes_key, 16) != 0)
130 {
131 hwWriteConfigBlock((void*)user_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(user_aes_key));
132 debug(PSTR("AES key written\n"));
133 }
134#endif
135}
136
137void setup()
138{
139#ifdef MY_IS_RFM69HW
140 _radio.setHighPower(true);
141#endif
142#ifdef MY_RFM69_ATC
143 _radio.enableAutoPower(-70);
144 debug(PSTR("ATC enabled\n"));
145#endif
146
147 for (uint8_t i = 0; i < NUM(sensors); i++)
148 {
149 struct sensor_t *sensor = &sensors[i];
150 if (sensor->type & SENSOR_BUTTON)
151 {
152 pinMode(sensor->button.pin, INPUT_PULLUP);
153 sensor->button.bounce.attach(sensor->button.pin);
154 }
155 }
156}
157
158void presentation()
159{
160 sendSketchInfo("CouchLight", "1.0");
161
162 // register all sensors to gw (they will be created as child devices)
163 for (uint8_t i = 0; i < NUM(sensors); i++)
164 {
165 struct sensor_t *sensor = &sensors[i];
166 if (sensor->type & SENSOR_RELAY || sensor->type & SENSOR_BUTTON)
167 present(sensor->id, S_BINARY);
168 }
169
170#if TEMP_SENSOR_ID >= 0
171 present(TEMP_SENSOR_ID, S_TEMP);
172#endif
173}
174
175void loop()
176{
177 //TODO maybe call _radio.rcCalibration() all 1000x changes?
178 checkButtons();
179
180#if TEMP_SENSOR_ID >= 0
181 checkTemperature();
182#endif
183}
184
185inline void checkTemperature(void)
186{
187 static unsigned long lastTempUpdate = millis();
188 static unsigned int numTempUpdates = 0;
189 static float lastTemp = 0;
190 static MyMessage msgTemp(TEMP_SENSOR_ID, V_TEMP);
191
192 unsigned long now = millis();
193 if (now - lastTempUpdate > TEMP_READ_INTERVAL)
194 {
195 float temp = _radio.readTemperature() + TEMP_OFFSET;
196 lastTempUpdate = now;
197 if (isnan(temp))
198 Serial.println(F("Failed reading temperature"));
199 else if (abs(temp - lastTemp) >= 2 || numTempUpdates == TEMP_N_READS_MSG)
200 {
201 lastTemp = temp;
202 numTempUpdates = 0;
203 send(msgTemp.set(temp, 2));
204#ifdef MY_DEBUG
205 char str_temp[6];
206 dtostrf(temp, 4, 2, str_temp);
207 debug(PSTR("Temperature: %s °C\n"), str_temp);
208#endif
209 }
210 else
211 ++numTempUpdates;
212 }
213}
214
215void receive(const MyMessage &message)
216{
217 if (message.type == V_STATUS || message.type == V_PERCENTAGE)
218 {
219 uint8_t sensor_id = message.sensor;
220 if (sensor_id >= NUM(sensors))
221 {
222 Serial.print(F("Invalid sensor id:"));
223 Serial.println(sensor_id);
224 return;
225 }
226
227 struct sensor_t *sensor = &sensors[sensor_id];
228 if (message.type == V_STATUS && sensor->type & SENSOR_RELAY)
229 {
230 if (mGetCommand(message) == C_REQ)
231 send(msg.setType(V_STATUS).setSensor(sensor->id).set(relayRead(sensor)));
232 else if (mGetCommand(message) == C_SET)
233 relayWrite(sensor, message.getBool());
234 }
235 }
236}
237
238bool relayRead(struct sensor_t *sensor)
239{
240 if (sensor->type & SENSOR_RELAY)
241 return digitalRead(sensor->relay.pin) == RELAY_ON;
242 return false;
243}
244
245void relayWrite(struct sensor_t *sensor, bool state, bool send_update)
246{
247 if (!(sensor->type & SENSOR_RELAY))
248 return;
249
250 Serial.print(F("Incoming change for relay: "));
251 Serial.print(sensor->relay.pin);
252 Serial.print(F(", New state: "));
253 Serial.println(state);
254
255 digitalWrite(sensor->relay.pin, state ? RELAY_ON : RELAY_OFF);
256
257#ifdef SAVE_RESTORE
258 saveState(sensor->id, state ? RELAY_ON : RELAY_OFF);
259#endif
260
261 if (send_update)
262 send(msg.setType(V_STATUS).setSensor(sensor->id).set(state));
263}
264
265void flipRelay(struct sensor_t *sensor, bool send_update)
266{
267 relayWrite(sensor, relayRead(sensor) ? RELAY_OFF : RELAY_ON, send_update);
268}
269
270inline void checkButtons(void)
271{
272 for (uint8_t i = 0; i < NUM(sensors); i++)
273 {
274 struct sensor_t *sensor = &sensors[i];
275 if (sensor->type & SENSOR_BUTTON)
276 {
277 sensor->button.bounce.update();
278 if (sensor->button.bounce.fell())
279 flipRelay(sensor, true);
280 }
281 }
282}
283
diff --git a/mysensors/lr_switch/.gitignore b/mysensors/lr_switch/.gitignore
new file mode 100644
index 0000000..89cc49c
--- /dev/null
+++ b/mysensors/lr_switch/.gitignore
@@ -0,0 +1,5 @@
1.pio
2.vscode/.browse.c_cpp.db*
3.vscode/c_cpp_properties.json
4.vscode/launch.json
5.vscode/ipch
diff --git a/mysensors/lr_switch/platformio.ini b/mysensors/lr_switch/platformio.ini
new file mode 100644
index 0000000..d4ceb9b
--- /dev/null
+++ b/mysensors/lr_switch/platformio.ini
@@ -0,0 +1,26 @@
1; PlatformIO Project Configuration File
2;
3; Build options: build flags, source filter, extra scripting
4; Upload options: custom port, speed and extra flags
5; Library options: dependencies, extra library storages
6;
7; Please visit documentation for the other options and examples
8; http://docs.platformio.org/en/stable/projectconf.html
9
10[platformio]
11default_envs = pro8MHzatmega328
12
13[env:miniatmega328]
14platform=atmelavr
15board=miniatmega328
16framework=arduino
17
18[env:pro8MHzatmega328]
19platform=atmelavr
20board=pro8MHzatmega328
21framework=arduino
22; minicore/optirun uses 38400 for 8MHz
23upload_speed=38400
24
25[platformio]
26lib_dir=/home/manuel/coding/Arduino/libraries
diff --git a/mysensors/lr_switch/src/main.cpp b/mysensors/lr_switch/src/main.cpp
new file mode 100644
index 0000000..673eb3a
--- /dev/null
+++ b/mysensors/lr_switch/src/main.cpp
@@ -0,0 +1,197 @@
1/**
2 * The MySensors Arduino library handles the wireless radio link and protocol
3 * between your home built sensors/actuators and HA controller of choice.
4 * The sensors forms a self healing radio network with optional repeaters. Each
5 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
6 * network topology allowing messages to be routed to nodes.
7 */
8
9// Enable debug prints to serial monitor
10//#define MY_DEBUG
11
12// configure radio
13#define MY_RADIO_RFM69
14
15/** @brief RFM69 frequency to use (RF69_433MHZ for 433MHz, RF69_868MHZ for 868MHz or RF69_915MHZ for 915MHz). */
16#define MY_RFM69_FREQUENCY RF69_868MHZ
17
18/** @brief Enable this if you're running the RFM69HW model. */
19#define MY_IS_RFM69HW
20
21/** @brief RFM69 Network ID. Use the same for all nodes that will talk to each other. */
22#define MY_RFM69_NETWORKID 1
23
24/** @brief Node id defaults to AUTO (tries to fetch id from controller). */
25#define MY_NODE_ID 3
26
27/** @brief If set, transport traffic is unmonitored and GW connection is optional */
28#define MY_TRANSPORT_DONT_CARE_MODE
29
30/** @brief Node parent defaults to AUTO (tries to find a parent automatically). */
31#define MY_PARENT_NODE_ID 0
32
33/** @brief The user-defined AES key to use for EEPROM personalization */
34#include "aes_key.h"
35
36// Enable repeater functionality for this node
37//#define MY_REPEATER_FEATURE
38
39/** @brief Enables RFM69 automatic transmit power control class. */
40//#define MY_RFM69_ATC
41
42#ifdef MY_AES_KEY
43/** @brief enables RFM69 encryption */
44#define MY_RFM69_ENABLE_ENCRYPTION
45#endif
46
47#include <Arduino.h>
48#include <MySensors.h>
49
50enum sensor_type : uint8_t
51{
52 SENSOR_RELAY = (1u << 0),
53 SENSOR_DIMMER = (1u << 1),
54 SENSOR_BUTTON = (1u << 2),
55 SENSOR_SCENE = (1u << 3),
56};
57
58struct sensor_t
59{
60 uint8_t id;
61 uint8_t type;
62 struct
63 {
64 uint8_t pin; // push button pin
65 } button;
66};
67
68struct sensor_t sensors[] = {
69 {
70 .id = 0,
71 .type = SENSOR_BUTTON | SENSOR_SCENE,
72 .button = { .pin = 3 },
73 },
74};
75
76#define NUM(a) (sizeof(a) / sizeof(*a))
77
78#define TEMP_SENSOR_ID -1
79#define TEMP_READ_INTERVAL 1000L // read temp every 1 sec
80#define TEMP_N_READS_MSG 60*60 // force temp message every n reads
81#define TEMP_OFFSET 0
82
83MyMessage msg(0, V_SCENE_ON);
84
85inline void checkTemperature(void);
86void wakeUp(void);
87
88void before()
89{
90#ifdef MY_AES_KEY
91 const uint8_t user_aes_key[16] = { MY_AES_KEY };
92 uint8_t cur_aes_key[16];
93 hwReadConfigBlock((void*)&cur_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(cur_aes_key));
94 if (memcmp(&user_aes_key, &cur_aes_key, 16) != 0)
95 {
96 hwWriteConfigBlock((void*)user_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(user_aes_key));
97 debug(PSTR("AES key written\n"));
98 }
99#endif
100}
101
102void setup()
103{
104#ifdef MY_IS_RFM69HW
105 _radio.setHighPower(true);
106#endif
107 //_radio.setPowerLevel(10);
108#ifdef MY_RFM69_ATC
109 _radio.enableAutoPower(-70);
110 debug(PSTR("ATC enabled\n"));
111#endif
112
113 for (uint8_t i = 0; i < NUM(sensors); i++)
114 {
115 struct sensor_t *sensor = &sensors[i];
116 if (sensor->type & SENSOR_BUTTON)
117 pinMode(sensor->button.pin, INPUT_PULLUP);
118 }
119}
120
121void presentation()
122{
123 sendSketchInfo("LRSwitch", "1.0");
124
125 // register all sensors to gw (they will be created as child devices)
126 for (uint8_t i = 0; i < NUM(sensors); i++)
127 {
128 struct sensor_t *sensor = &sensors[i];
129 if (sensor->type & SENSOR_SCENE)
130 present(sensor->id, S_SCENE_CONTROLLER);
131 }
132
133#if TEMP_SENSOR_ID >= 0
134 present(TEMP_SENSOR_ID, S_TEMP);
135#endif
136}
137
138void loop()
139{
140 //TODO maybe call _radio.rcCalibration() all 1000x changes?
141
142 struct sensor_t *sensor = &sensors[0];
143 uint8_t pin = sensor->button.pin;
144
145 // delay() instead of sleep()
146 // sleep() will for whatever reason trigger the external interrupt?!
147 delay(1000);
148
149 Serial.println("r");
150 Serial.flush();
151 uint8_t state_before = digitalRead(pin);
152 int8_t intr = sleep(digitalPinToInterrupt(pin), CHANGE, 0);
153 if (intr == digitalPinToInterrupt(pin))
154 {
155 uint8_t state_now = digitalRead(pin);
156 if (state_before != state_now)
157 {
158 Serial.println("m");
159 send(msg.setType(V_SCENE_ON).setSensor(sensor->id).set(1));
160 }
161 }
162
163#if TEMP_SENSOR_ID >= 0
164 checkTemperature();
165#endif
166}
167
168inline void checkTemperature(void)
169{
170 static unsigned long lastTempUpdate = millis();
171 static unsigned int numTempUpdates = 0;
172 static float lastTemp = 0;
173 static MyMessage msgTemp(TEMP_SENSOR_ID, V_TEMP);
174
175 unsigned long now = millis();
176 if (now - lastTempUpdate > TEMP_READ_INTERVAL)
177 {
178 float temp = _radio.readTemperature() + TEMP_OFFSET;
179 lastTempUpdate = now;
180 if (isnan(temp))
181 Serial.println(F("Failed reading temperature"));
182 else if (abs(temp - lastTemp) >= 2 || numTempUpdates == TEMP_N_READS_MSG)
183 {
184 lastTemp = temp;
185 numTempUpdates = 0;
186 send(msgTemp.set(temp, 2));
187#ifdef MY_DEBUG
188 char str_temp[6];
189 dtostrf(temp, 4, 2, str_temp);
190 debug(PSTR("Temperature: %s °C\n"), str_temp);
191#endif
192 }
193 else
194 ++numTempUpdates;
195 }
196}
197
diff --git a/mysensors/rgbtv_light/.gitignore b/mysensors/rgbtv_light/.gitignore
new file mode 100644
index 0000000..89cc49c
--- /dev/null
+++ b/mysensors/rgbtv_light/.gitignore
@@ -0,0 +1,5 @@
1.pio
2.vscode/.browse.c_cpp.db*
3.vscode/c_cpp_properties.json
4.vscode/launch.json
5.vscode/ipch
diff --git a/mysensors/rgbtv_light/platformio.ini b/mysensors/rgbtv_light/platformio.ini
new file mode 100644
index 0000000..d4ceb9b
--- /dev/null
+++ b/mysensors/rgbtv_light/platformio.ini
@@ -0,0 +1,26 @@
1; PlatformIO Project Configuration File
2;
3; Build options: build flags, source filter, extra scripting
4; Upload options: custom port, speed and extra flags
5; Library options: dependencies, extra library storages
6;
7; Please visit documentation for the other options and examples
8; http://docs.platformio.org/en/stable/projectconf.html
9
10[platformio]
11default_envs = pro8MHzatmega328
12
13[env:miniatmega328]
14platform=atmelavr
15board=miniatmega328
16framework=arduino
17
18[env:pro8MHzatmega328]
19platform=atmelavr
20board=pro8MHzatmega328
21framework=arduino
22; minicore/optirun uses 38400 for 8MHz
23upload_speed=38400
24
25[platformio]
26lib_dir=/home/manuel/coding/Arduino/libraries
diff --git a/mysensors/rgbtv_light/src/main.cpp b/mysensors/rgbtv_light/src/main.cpp
new file mode 100644
index 0000000..98120a6
--- /dev/null
+++ b/mysensors/rgbtv_light/src/main.cpp
@@ -0,0 +1,241 @@
1/**
2 * The MySensors Arduino library handles the wireless radio link and protocol
3 * between your home built sensors/actuators and HA controller of choice.
4 * The sensors forms a self healing radio network with optional repeaters. Each
5 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
6 * network topology allowing messages to be routed to nodes.
7 */
8
9// Enable debug prints to serial monitor
10#define MY_DEBUG
11
12// configure radio
13#define MY_RADIO_RFM69
14
15/** @brief RFM69 frequency to use (RF69_433MHZ for 433MHz, RF69_868MHZ for 868MHz or RF69_915MHZ for 915MHz). */
16#define MY_RFM69_FREQUENCY RF69_868MHZ
17
18/** @brief Enable this if you're running the RFM69HW model. */
19//#define MY_IS_RFM69HW
20
21/** @brief RFM69 Network ID. Use the same for all nodes that will talk to each other. */
22#define MY_RFM69_NETWORKID 1
23
24/** @brief Node id defaults to AUTO (tries to fetch id from controller). */
25#define MY_NODE_ID 3
26
27/** @brief If set, transport traffic is unmonitored and GW connection is optional */
28#define MY_TRANSPORT_DONT_CARE_MODE
29
30/** @brief Node parent defaults to AUTO (tries to find a parent automatically). */
31#define MY_PARENT_NODE_ID 0
32
33/** @brief The user-defined AES key to use for EEPROM personalization */
34#include "aes_key.h"
35
36// Enable repeater functionality for this node
37//#define MY_REPEATER_FEATURE
38
39/** @brief Enables RFM69 automatic transmit power control class. */
40//#define MY_RFM69_ATC
41
42#ifdef MY_AES_KEY
43/** @brief enables RFM69 encryption */
44#define MY_RFM69_ENABLE_ENCRYPTION
45#endif
46
47#include <Arduino.h>
48#include <MySensors.h>
49#include <FastLED.h>
50
51#define RELAY_1_PIN 4 // pin number of first relay (second on pin+1 etc)
52#define NUMBER_OF_RELAYS 1 // Total number of attached relays
53#define RELAY_ON 1 // GPIO value to write to turn on attached relay
54#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
55
56#define RGB_PIN 7
57#define NUM_LEDS 30
58#define RGB_CHIPSET WS2812B
59#define RGB_COLOR_ORDER GRB
60#define RGB_CHILD_ID 0
61
62#define TEMP_READ_INTERVAL 1000L // read temp every 1 sec
63#define TEMP_N_READS_MSG 60*60 // force temp message every n reads
64#define TEMP_OFFSET 0
65#define TEMP_CHILD_ID 254
66
67MyMessage msgRGB(RGB_CHILD_ID, 0);
68static uint8_t brightness = 128;
69
70MyMessage msgRelais(0, V_STATUS);
71
72unsigned long lastTempUpdate = millis();
73unsigned int numTempUpdates = 0;
74float lastTemp = 0;
75MyMessage msgTemp(TEMP_CHILD_ID, V_TEMP);
76
77CRGB leds[NUM_LEDS];
78
79void changeRelay(uint8_t relay, uint8_t val, bool send_update=false);
80
81void before()
82{
83 // set relay pins to output mode + restore to last known state
84 for (uint8_t relay = 0; relay < NUMBER_OF_RELAYS; relay++)
85 {
86 pinMode(relay + RELAY_1_PIN, OUTPUT);
87 digitalWrite(relay + RELAY_1_PIN, loadState(relay) ? RELAY_ON : RELAY_OFF);
88 }
89
90#ifdef MY_AES_KEY
91 const uint8_t user_aes_key[16] = { MY_AES_KEY };
92 uint8_t cur_aes_key[16];
93 hwReadConfigBlock((void*)&cur_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(cur_aes_key));
94 if (memcmp(&user_aes_key, &cur_aes_key, 16) != 0)
95 {
96 hwWriteConfigBlock((void*)user_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(user_aes_key));
97 debug(PSTR("AES key written\n"));
98 }
99#endif
100}
101
102void setup()
103{
104#ifdef MY_RFM69_ATC
105 _radio.enableAutoPower(-70);
106 debug(PSTR("ATC enabled\n"));
107#endif
108 FastLED.addLeds<RGB_CHIPSET, RGB_PIN, RGB_COLOR_ORDER>(leds, NUM_LEDS);
109 //TODO restore mode(static/ambilight)/color/brightness from flash?
110 FastLED.setBrightness(brightness);
111}
112
113void presentation()
114{
115 // Send the sketch version information to the gateway and Controller
116 sendSketchInfo("Ambilight", "1.0");
117
118 // Register all sensors to gw (they will be created as child devices)
119 present(0, S_RGB_LIGHT, "ambilight");
120#if 0
121 for (uint8_t relay = 0; relay < NUMBER_OF_RELAYS; relay++)
122 present(relay + 1, S_BINARY);
123 present(TEMP_CHILD_ID, S_TEMP);
124#endif
125
126 delay(3000);
127 send(msgRGB.setType(V_STATUS).set(1));
128 delay(500);
129 send(msgRGB.setType(V_DIMMER).set(FastLED.getBrightness()));
130 delay(500);
131 send(msgRGB.setType(V_RGB).set("ffffff"));
132 FastLED.show();
133}
134
135void loop()
136{
137 //TODO maybe call _radio.rcCalibration() all 1000x changes?
138
139 //FastLED.show();
140 //FastLED.delay(8);
141
142#if 0
143 // check temperature
144 unsigned long now = millis();
145 if (now - lastTempUpdate > TEMP_READ_INTERVAL)
146 {
147 float temp = _radio.readTemperature() + TEMP_OFFSET;
148 lastTempUpdate = now;
149 if (isnan(temp))
150 Serial.println("Failed reading temperature");
151 else if (abs(temp - lastTemp) >= 2 || numTempUpdates == TEMP_N_READS_MSG)
152 {
153 lastTemp = temp;
154 numTempUpdates = 0;
155 send(msgTemp.set(temp, 2));
156#ifdef MY_DEBUG
157 char str_temp[6];
158 dtostrf(temp, 4, 2, str_temp);
159 debug(PSTR("Temperature: %s °C\n"), str_temp);
160#endif
161 }
162 else
163 ++numTempUpdates;
164 }
165#endif
166}
167
168void receive(const MyMessage &message)
169{
170 Serial.println(_radio.readRSSI());
171 if (message.sensor == RGB_CHILD_ID)
172 {
173 if (mGetCommand(message) == C_SET)
174 {
175 if (message.type == V_STATUS)
176 {
177 bool val = message.getBool();
178 // datatype=0, message=0/1
179 Serial.println("light on/off");
180 //TODO restore brightness.
181 }
182 else if (message.type == V_RGB && mGetLength(message) == 6)
183 {
184 uint32_t colorcode = strtol(message.getString(), NULL, 16);
185 fill_solid(leds, NUM_LEDS, CRGB(colorcode));
186 FastLED.show();
187 }
188 else if (message.type == V_PERCENTAGE)
189 {
190 //TODO fade?
191 uint8_t val = message.getByte();
192 if (val < 0 || val > 100)
193 return;
194 Serial.print("dim: ");
195 Serial.println(val, DEC);
196 brightness = map(val, 0, 100, 0, 255);
197 Serial.println(brightness, DEC);
198 // datatype=0, message=1-100
199 FastLED.setBrightness(brightness);
200 FastLED.show();
201 }
202 }
203 }
204
205#if 0
206 if (message.type == V_STATUS && message.sensor >= 1)
207 {
208 uint8_t relay = message.sensor - 1;
209 if (relay >= NUMBER_OF_RELAYS)
210 {
211 Serial.print("Invalid relay index:");
212 Serial.println(relay);
213 return;
214 }
215
216 if (mGetCommand(message) == C_REQ)
217 send(msg.setSensor(relay + 1).set(digitalRead(relay + RELAY_1_PIN)));
218 else if (mGetCommand(message) == C_SET)
219 changeRelay(relay, message.getBool() ? RELAY_ON : RELAY_OFF);
220 }
221#endif
222}
223
224void changeRelay(uint8_t relay, uint8_t value, bool send_update)
225{
226 if (relay >= NUMBER_OF_RELAYS)
227 return;
228 Serial.print("Incoming change for relay: ");
229 Serial.print(relay);
230 Serial.print(", New status: ");
231 Serial.println(value);
232
233 // change relay state + store state in eeprom
234 digitalWrite(relay + RELAY_1_PIN, value);
235 saveState(relay, value);
236
237 // send msg
238 if (send_update)
239 send(msgRelais.setSensor(relay + 1).set(value));
240}
241
diff --git a/mysensors/testnode/.gitignore b/mysensors/testnode/.gitignore
new file mode 100644
index 0000000..89cc49c
--- /dev/null
+++ b/mysensors/testnode/.gitignore
@@ -0,0 +1,5 @@
1.pio
2.vscode/.browse.c_cpp.db*
3.vscode/c_cpp_properties.json
4.vscode/launch.json
5.vscode/ipch
diff --git a/mysensors/testnode/platformio.ini b/mysensors/testnode/platformio.ini
new file mode 100644
index 0000000..d4ceb9b
--- /dev/null
+++ b/mysensors/testnode/platformio.ini
@@ -0,0 +1,26 @@
1; PlatformIO Project Configuration File
2;
3; Build options: build flags, source filter, extra scripting
4; Upload options: custom port, speed and extra flags
5; Library options: dependencies, extra library storages
6;
7; Please visit documentation for the other options and examples
8; http://docs.platformio.org/en/stable/projectconf.html
9
10[platformio]
11default_envs = pro8MHzatmega328
12
13[env:miniatmega328]
14platform=atmelavr
15board=miniatmega328
16framework=arduino
17
18[env:pro8MHzatmega328]
19platform=atmelavr
20board=pro8MHzatmega328
21framework=arduino
22; minicore/optirun uses 38400 for 8MHz
23upload_speed=38400
24
25[platformio]
26lib_dir=/home/manuel/coding/Arduino/libraries
diff --git a/mysensors/testnode/src/main.cpp b/mysensors/testnode/src/main.cpp
new file mode 100644
index 0000000..999c32a
--- /dev/null
+++ b/mysensors/testnode/src/main.cpp
@@ -0,0 +1,316 @@
1/**
2 * The MySensors Arduino library handles the wireless radio link and protocol
3 * between your home built sensors/actuators and HA controller of choice.
4 * The sensors forms a self healing radio network with optional repeaters. Each
5 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
6 * network topology allowing messages to be routed to nodes.
7 */
8
9// Enable debug prints to serial monitor
10#define MY_DEBUG
11
12// configure radio
13#define MY_RADIO_RFM69
14
15/** @brief RFM69 frequency to use (RF69_433MHZ for 433MHz, RF69_868MHZ for 868MHz or RF69_915MHZ for 915MHz). */
16#define MY_RFM69_FREQUENCY RF69_868MHZ
17
18/** @brief Enable this if you're running the RFM69HW model. */
19//#define MY_IS_RFM69HW
20
21/** @brief RFM69 Network ID. Use the same for all nodes that will talk to each other. */
22#define MY_RFM69_NETWORKID 1
23
24/** @brief Node id defaults to AUTO (tries to fetch id from controller). */
25#define MY_NODE_ID 4
26
27/** @brief If set, transport traffic is unmonitored and GW connection is optional */
28#define MY_TRANSPORT_DONT_CARE_MODE
29
30/** @brief Node parent defaults to AUTO (tries to find a parent automatically). */
31#define MY_PARENT_NODE_ID 0
32
33/** @brief The user-defined AES key to use for EEPROM personalization */
34#include "aes_key.h"
35
36// Enable repeater functionality for this node
37//#define MY_REPEATER_FEATURE
38
39/** @brief Enables RFM69 automatic transmit power control class. */
40//#define MY_RFM69_ATC
41
42#ifdef MY_AES_KEY
43/** @brief enables RFM69 encryption */
44#define MY_RFM69_ENABLE_ENCRYPTION
45#endif
46
47#include <Arduino.h>
48#include <MySensors.h>
49
50enum sensor_type : uint8_t
51{
52 SENSOR_RELAY = (1u << 0),
53 SENSOR_DIMMER = (1u << 1),
54 SENSOR_BUTTON = (1u << 2),
55 SENSOR_SCENE = (1u << 3),
56};
57
58struct sensor_t
59{
60 uint8_t id;
61 uint8_t type;
62 struct
63 {
64 uint8_t pin; // relay pin
65 } relay;
66 struct
67 {
68 uint8_t pin; // dimmer pin
69 uint16_t level; // current dim level (0 to 100)
70 } dimmer;
71};
72
73struct sensor_t sensors[] = {
74 {
75 .id = 0,
76 .type = SENSOR_RELAY,
77 .relay = { .pin = 3 },
78 },
79 {
80 .id = 1,
81 .type = SENSOR_RELAY | SENSOR_DIMMER,
82 .relay = { .pin = 4 },
83 .dimmer = { .pin = 6, .level = 100 },
84 },
85};
86
87//#define SAVE_RESTORE
88
89#define NUM(a) (sizeof(a) / sizeof(*a))
90
91#define RELAY_ON 1 // GPIO value to write to turn on attached relay
92#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
93
94#define DIMMER_FADE_DELAY 10 // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
95
96#define TEMP_SENSOR_ID 254
97#define TEMP_READ_INTERVAL 1000L // read temp every 1 sec
98#define TEMP_N_READS_MSG 60*60 // force temp message every n reads
99#define TEMP_OFFSET 0
100
101MyMessage msg(0, V_STATUS);
102
103inline void checkTemperature(void);
104bool relayRead(struct sensor_t *sensor);
105void relayWrite(struct sensor_t *sensor, bool state, bool send_update=false);
106void flipRelay(struct sensor_t *sensor, bool send_update=false);
107void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update=false);
108
109void before()
110{
111 // set relay pins to output mode + restore to last known state
112 for (uint8_t i = 0; i < NUM(sensors); i++)
113 {
114 struct sensor_t *sensor = &sensors[i];
115 if (sensor->type & SENSOR_RELAY)
116 {
117 pinMode(sensor->relay.pin, OUTPUT);
118#ifdef SAVE_RESTORE
119 digitalWrite(sensor->relay.pin, loadState(sensor->id) ? RELAY_ON : RELAY_OFF);
120#else
121 digitalWrite(sensor->relay.pin, RELAY_ON);
122#endif
123 }
124
125 if (sensor->type & SENSOR_DIMMER)
126 {
127 pinMode(sensor->dimmer.pin, OUTPUT);
128#ifdef SAVE_RESTORE
129 digitalWrite(sensor->relay.pin, loadState(NUM(sensors) + sensor->id));
130#else
131 analogWrite(sensor->dimmer.pin, map(sensor->dimmer.level, 0, 100, 0, 255));
132#endif
133 }
134 }
135
136#ifdef MY_AES_KEY
137 const uint8_t user_aes_key[16] = { MY_AES_KEY };
138 uint8_t cur_aes_key[16];
139 hwReadConfigBlock((void*)&cur_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(cur_aes_key));
140 if (memcmp(&user_aes_key, &cur_aes_key, 16) != 0)
141 {
142 hwWriteConfigBlock((void*)user_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(user_aes_key));
143 debug(PSTR("AES key written\n"));
144 }
145#endif
146}
147
148void setup()
149{
150#ifdef MY_IS_RFM69HW
151 _radio.setHighPower(true);
152#endif
153#ifdef MY_RFM69_ATC
154 _radio.enableAutoPower(-70);
155 debug(PSTR("ATC enabled\n"));
156#endif
157}
158
159void presentation()
160{
161 sendSketchInfo("TVLight", "1.0");
162
163 // register all sensors to gw (they will be created as child devices)
164 for (uint8_t i = 0; i < NUM(sensors); i++)
165 {
166 struct sensor_t *sensor = &sensors[i];
167 if (sensor->type & SENSOR_DIMMER)
168 present(sensor->id, S_DIMMER);
169 else if (sensor->type & SENSOR_RELAY)
170 present(sensor->id, S_BINARY);
171 }
172
173#if TEMP_SENSOR_ID >= 0
174 present(TEMP_SENSOR_ID, S_TEMP);
175#endif
176}
177
178void loop()
179{
180 //TODO maybe call _radio.rcCalibration() all 1000x changes?
181#if TEMP_SENSOR_ID >= 0
182 checkTemperature();
183#endif
184}
185
186inline void checkTemperature(void)
187{
188 static unsigned long lastTempUpdate = millis();
189 static unsigned int numTempUpdates = 0;
190 static float lastTemp = 0;
191 static MyMessage msgTemp(TEMP_SENSOR_ID, V_TEMP);
192
193 unsigned long now = millis();
194 if (now - lastTempUpdate > TEMP_READ_INTERVAL)
195 {
196 float temp = _radio.readTemperature() + TEMP_OFFSET;
197 lastTempUpdate = now;
198 if (isnan(temp))
199 Serial.println(F("Failed reading temperature"));
200 else if (abs(temp - lastTemp) >= 2 || numTempUpdates == TEMP_N_READS_MSG)
201 {
202 lastTemp = temp;
203 numTempUpdates = 0;
204 send(msgTemp.set(temp, 2));
205#ifdef MY_DEBUG
206 char str_temp[6];
207 dtostrf(temp, 4, 2, str_temp);
208 debug(PSTR("Temperature: %s °C\n"), str_temp);
209#endif
210 }
211 else
212 ++numTempUpdates;
213 }
214}
215
216void receive(const MyMessage &message)
217{
218 if (message.type == V_STATUS || message.type == V_PERCENTAGE)
219 {
220 uint8_t sensor_id = message.sensor;
221 if (sensor_id >= NUM(sensors))
222 {
223 Serial.print(F("Invalid sensor id:"));
224 Serial.println(sensor_id);
225 return;
226 }
227
228 struct sensor_t *sensor = &sensors[sensor_id];
229 if (message.type == V_STATUS && sensor->type & SENSOR_RELAY)
230 {
231 if (mGetCommand(message) == C_REQ)
232 send(msg.setType(V_STATUS).setSensor(sensor->id).set(relayRead(sensor)));
233 else if (mGetCommand(message) == C_SET)
234 relayWrite(sensor, message.getBool());
235 }
236 else if (message.type == V_PERCENTAGE && sensor->type & SENSOR_DIMMER)
237 {
238 if (mGetCommand(message) == C_REQ)
239 send(msg.setType(V_PERCENTAGE).setSensor(sensor->id).set(sensor->dimmer.level));
240 else if (mGetCommand(message) == C_SET)
241 {
242 uint16_t level = message.getUInt();
243 if (level > 255)
244 return;
245 fadeDimmer(sensor, level);
246 }
247 }
248 }
249}
250
251bool relayRead(struct sensor_t *sensor)
252{
253 if (sensor->type & SENSOR_RELAY)
254 return digitalRead(sensor->relay.pin) == RELAY_ON;
255 return false;
256}
257
258void relayWrite(struct sensor_t *sensor, bool state, bool send_update)
259{
260 if (!(sensor->type & SENSOR_RELAY))
261 return;
262
263 Serial.print(F("Incoming change for relay: "));
264 Serial.print(sensor->relay.pin);
265 Serial.print(F(", New state: "));
266 Serial.println(state);
267
268 digitalWrite(sensor->relay.pin, state ? RELAY_ON : RELAY_OFF);
269
270#ifdef SAVE_RESTORE
271 saveState(sensor->id, state ? RELAY_ON : RELAY_OFF);
272#endif
273
274 if (send_update)
275 send(msg.setType(V_STATUS).setSensor(sensor->id).set(state));
276}
277
278void flipRelay(struct sensor_t *sensor, bool send_update)
279{
280 relayWrite(sensor, relayRead(sensor) ? RELAY_OFF : RELAY_ON, send_update);
281}
282
283void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update)
284{
285 if (!(sensor->type & SENSOR_DIMMER))
286 return;
287
288 Serial.print(F("Incoming change for dimmer: "));
289 Serial.print(sensor->dimmer.pin);
290 Serial.print(F(", Old level: "));
291 Serial.print(sensor->dimmer.level);
292 Serial.print(F(", New level: "));
293 Serial.println(level);
294
295 if (level > 0 && sensor->type & SENSOR_RELAY && !relayRead(sensor))
296 relayWrite(sensor, RELAY_ON);
297
298 int delta = ((int8_t)(level - sensor->dimmer.level) < 0) ? -1 : 1;
299 while (sensor->dimmer.level != level)
300 {
301 sensor->dimmer.level += delta;
302 analogWrite(sensor->dimmer.pin, map(sensor->dimmer.level, 0, 100, 0, 255));
303 delay(DIMMER_FADE_DELAY);
304 }
305
306 if (level == 0 && sensor->type & SENSOR_RELAY && relayRead(sensor))
307 relayWrite(sensor, RELAY_OFF);
308
309#ifdef SAVE_RESTORE
310 saveState(NUM(sensors) + sensor->id, level);
311#endif
312
313 if (send_update)
314 send(msg.setType(V_PERCENTAGE).setSensor(sensor->id).set(level));
315}
316
diff --git a/mysensors/tv_light/.gitignore b/mysensors/tv_light/.gitignore
new file mode 100644
index 0000000..89cc49c
--- /dev/null
+++ b/mysensors/tv_light/.gitignore
@@ -0,0 +1,5 @@
1.pio
2.vscode/.browse.c_cpp.db*
3.vscode/c_cpp_properties.json
4.vscode/launch.json
5.vscode/ipch
diff --git a/mysensors/tv_light/platformio.ini b/mysensors/tv_light/platformio.ini
new file mode 100644
index 0000000..d4ceb9b
--- /dev/null
+++ b/mysensors/tv_light/platformio.ini
@@ -0,0 +1,26 @@
1; PlatformIO Project Configuration File
2;
3; Build options: build flags, source filter, extra scripting
4; Upload options: custom port, speed and extra flags
5; Library options: dependencies, extra library storages
6;
7; Please visit documentation for the other options and examples
8; http://docs.platformio.org/en/stable/projectconf.html
9
10[platformio]
11default_envs = pro8MHzatmega328
12
13[env:miniatmega328]
14platform=atmelavr
15board=miniatmega328
16framework=arduino
17
18[env:pro8MHzatmega328]
19platform=atmelavr
20board=pro8MHzatmega328
21framework=arduino
22; minicore/optirun uses 38400 for 8MHz
23upload_speed=38400
24
25[platformio]
26lib_dir=/home/manuel/coding/Arduino/libraries
diff --git a/mysensors/tv_light/src/main.cpp b/mysensors/tv_light/src/main.cpp
new file mode 100644
index 0000000..caf3a6e
--- /dev/null
+++ b/mysensors/tv_light/src/main.cpp
@@ -0,0 +1,358 @@
1/**
2 * The MySensors Arduino library handles the wireless radio link and protocol
3 * between your home built sensors/actuators and HA controller of choice.
4 * The sensors forms a self healing radio network with optional repeaters. Each
5 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
6 * network topology allowing messages to be routed to nodes.
7 */
8
9// Enable debug prints to serial monitor
10//#define MY_DEBUG
11
12// configure radio
13#define MY_RADIO_RFM69
14
15/** @brief RFM69 frequency to use (RF69_433MHZ for 433MHz, RF69_868MHZ for 868MHz or RF69_915MHZ for 915MHz). */
16#define MY_RFM69_FREQUENCY RF69_868MHZ
17
18/** @brief Enable this if you're running the RFM69HW model. */
19#define MY_IS_RFM69HW
20
21/** @brief RFM69 Network ID. Use the same for all nodes that will talk to each other. */
22#define MY_RFM69_NETWORKID 1
23
24/** @brief Node id defaults to AUTO (tries to fetch id from controller). */
25#define MY_NODE_ID 2
26
27/** @brief If set, transport traffic is unmonitored and GW connection is optional */
28#define MY_TRANSPORT_DONT_CARE_MODE
29
30/** @brief Node parent defaults to AUTO (tries to find a parent automatically). */
31#define MY_PARENT_NODE_ID 0
32
33/** @brief The user-defined AES key to use for EEPROM personalization */
34#include "aes_key.h"
35
36// Enable repeater functionality for this node
37//#define MY_REPEATER_FEATURE
38
39/** @brief Enables RFM69 automatic transmit power control class. */
40//#define MY_RFM69_ATC
41
42#ifdef MY_AES_KEY
43/** @brief enables RFM69 encryption */
44#define MY_RFM69_ENABLE_ENCRYPTION
45#endif
46
47#include <Arduino.h>
48#include <MySensors.h>
49#include <avr/pgmspace.h>
50
51enum sensor_type : uint8_t
52{
53 SENSOR_RELAY = (1u << 0),
54 SENSOR_DIMMER = (1u << 1),
55 SENSOR_BUTTON = (1u << 2),
56};
57
58struct sensor_t
59{
60 uint8_t id;
61 uint8_t type;
62 struct
63 {
64 uint8_t pin; // relay pin
65 } relay;
66 struct
67 {
68 uint8_t pin; // dimmer pin
69 uint16_t level; // current dim level (0 to 100)
70 } dimmer;
71};
72
73struct sensor_t sensors[] = {
74 {
75 .id = 0,
76 .type = SENSOR_RELAY,
77 .relay = { .pin = 4 },
78 },
79 {
80 .id = 1,
81 .type = SENSOR_RELAY | SENSOR_DIMMER,
82 //.type = SENSOR_RELAY,
83 .relay = { .pin = 5 },
84 .dimmer = { .pin = 6, .level = 100 },
85 },
86};
87
88//#define SAVE_RESTORE
89
90#define NUM(a) (sizeof(a) / sizeof(*a))
91
92#define RELAY_ON 1 // GPIO value to write to turn on attached relay
93#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
94
95#define DIMMER_FADE_DELAY 40 // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
96
97#define TEMP_SENSOR_ID 254
98#define TEMP_READ_INTERVAL 1000L // read temp every 1 sec
99#define TEMP_N_READS_MSG 60*60 // force temp message every n reads
100#define TEMP_OFFSET 0
101
102MyMessage msg(0, V_STATUS);
103
104inline void checkTemperature(void);
105bool relayRead(struct sensor_t *sensor);
106void relayWrite(struct sensor_t *sensor, bool state, bool send_update=false);
107void flipRelay(struct sensor_t *sensor, bool send_update=false);
108inline uint8_t pwmValue(uint8_t level);
109void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update=false);
110
111void before()
112{
113 // set relay pins to output mode + restore to last known state
114 for (uint8_t i = 0; i < NUM(sensors); i++)
115 {
116 struct sensor_t *sensor = &sensors[i];
117 if (sensor->type & SENSOR_RELAY)
118 {
119 pinMode(sensor->relay.pin, OUTPUT);
120#ifdef SAVE_RESTORE
121 digitalWrite(sensor->relay.pin, loadState(sensor->id) ? RELAY_ON : RELAY_OFF);
122#else
123 digitalWrite(sensor->relay.pin, RELAY_OFF);
124#endif
125 }
126
127 if (sensor->type & SENSOR_DIMMER)
128 {
129 pinMode(sensor->dimmer.pin, OUTPUT);
130#ifdef SAVE_RESTORE
131 uint8_t level = loadState(NUM(sensors) + sensor->id;
132#else
133 uint8_t level = sensor->dimmer.level;
134#endif
135 if ((sensor->type & SENSOR_RELAY) && !relayRead(sensor))
136 level = 0;
137 analogWrite(sensor->dimmer.pin, pwmValue(level));
138 }
139 }
140
141#ifdef MY_AES_KEY
142 const uint8_t user_aes_key[16] = { MY_AES_KEY };
143 uint8_t cur_aes_key[16];
144 hwReadConfigBlock((void*)&cur_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(cur_aes_key));
145 if (memcmp(&user_aes_key, &cur_aes_key, 16) != 0)
146 {
147 hwWriteConfigBlock((void*)user_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(user_aes_key));
148 debug(PSTR("AES key written\n"));
149 }
150#endif
151}
152
153void setup()
154{
155#ifdef MY_IS_RFM69HW
156 _radio.setHighPower(true);
157#endif
158#ifdef MY_RFM69_ATC
159 _radio.enableAutoPower(-70);
160 debug(PSTR("ATC enabled\n"));
161#endif
162}
163
164void presentation()
165{
166 sendSketchInfo("TVLight", "1.0");
167
168 // register all sensors to gw (they will be created as child devices)
169 for (uint8_t i = 0; i < NUM(sensors); i++)
170 {
171 struct sensor_t *sensor = &sensors[i];
172 if (sensor->type & SENSOR_DIMMER)
173 present(sensor->id, S_DIMMER);
174 else if (sensor->type & SENSOR_RELAY || sensor->type & SENSOR_BUTTON)
175 present(sensor->id, S_BINARY);
176 }
177
178#if TEMP_SENSOR_ID >= 0
179 present(TEMP_SENSOR_ID, S_TEMP);
180#endif
181}
182
183void loop()
184{
185 //TODO maybe call _radio.rcCalibration() all 1000x changes?
186#if TEMP_SENSOR_ID >= 0
187 checkTemperature();
188#endif
189}
190
191inline void checkTemperature(void)
192{
193 static unsigned long lastTempUpdate = millis();
194 static unsigned int numTempUpdates = 0;
195 static float lastTemp = 0;
196 static MyMessage msgTemp(TEMP_SENSOR_ID, V_TEMP);
197
198 unsigned long now = millis();
199 if (now - lastTempUpdate > TEMP_READ_INTERVAL)
200 {
201 float temp = _radio.readTemperature() + TEMP_OFFSET;
202 lastTempUpdate = now;
203 if (isnan(temp))
204 Serial.println(F("Failed reading temperature"));
205 else if (abs(temp - lastTemp) >= 2 || numTempUpdates == TEMP_N_READS_MSG)
206 {
207 lastTemp = temp;
208 numTempUpdates = 0;
209 send(msgTemp.set(temp, 2));
210#ifdef MY_DEBUG
211 char str_temp[6];
212 dtostrf(temp, 4, 2, str_temp);
213 debug(PSTR("Temperature: %s °C\n"), str_temp);
214#endif
215 }
216 else
217 ++numTempUpdates;
218 }
219}
220
221void receive(const MyMessage &message)
222{
223 if (message.type == V_STATUS || message.type == V_PERCENTAGE)
224 {
225 uint8_t sensor_id = message.sensor;
226 if (sensor_id >= NUM(sensors))
227 {
228 Serial.print(F("Invalid sensor id:"));
229 Serial.println(sensor_id);
230 return;
231 }
232
233 struct sensor_t *sensor = &sensors[sensor_id];
234 if (message.type == V_STATUS && sensor->type & SENSOR_RELAY)
235 {
236 if (mGetCommand(message) == C_REQ)
237 send(msg.setType(V_STATUS).setSensor(sensor->id).set(relayRead(sensor)));
238 else if (mGetCommand(message) == C_SET)
239 relayWrite(sensor, message.getBool());
240 }
241 else if (message.type == V_PERCENTAGE && sensor->type & SENSOR_DIMMER)
242 {
243 if (mGetCommand(message) == C_REQ)
244 send(msg.setType(V_PERCENTAGE).setSensor(sensor->id).set(sensor->dimmer.level));
245 else if (mGetCommand(message) == C_SET)
246 {
247 uint16_t level = message.getUInt();
248 fadeDimmer(sensor, (level > 100) ? 100 : level);
249 }
250 }
251 }
252}
253
254bool relayRead(struct sensor_t *sensor)
255{
256 if (sensor->type & SENSOR_RELAY)
257 return digitalRead(sensor->relay.pin) == RELAY_ON;
258 return false;
259}
260
261void relayWrite(struct sensor_t *sensor, bool state, bool send_update)
262{
263 if (!(sensor->type & SENSOR_RELAY))
264 return;
265
266 Serial.print(F("Incoming change for relay: "));
267 Serial.print(sensor->relay.pin);
268 Serial.print(F(", New state: "));
269 Serial.println(state);
270
271 digitalWrite(sensor->relay.pin, state ? RELAY_ON : RELAY_OFF);
272
273 if (sensor->type & SENSOR_DIMMER)
274 analogWrite(sensor->dimmer.pin, state ? pwmValue(sensor->dimmer.level) : 0);
275
276#ifdef SAVE_RESTORE
277 saveState(sensor->id, state ? RELAY_ON : RELAY_OFF);
278#endif
279
280 if (send_update)
281 send(msg.setType(V_STATUS).setSensor(sensor->id).set(state));
282}
283
284void flipRelay(struct sensor_t *sensor, bool send_update)
285{
286 relayWrite(sensor, relayRead(sensor) ? RELAY_OFF : RELAY_ON, send_update);
287}
288
289inline uint8_t pwmValue(uint8_t level)
290{
291 // see https://www.mikrocontroller.net/articles/LED-Fading
292#if 0
293 static const uint8_t pwmtable[101] PROGMEM = {
294 0, 1, 1, 1, 1, 1, 1, 2, 2, 2,
295 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
296 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
297 5, 6, 6, 6, 7, 7, 8, 8, 8, 9,
298 9, 10, 11, 11, 12, 12, 13, 14, 15, 16,
299 16, 17, 18, 19, 20, 22, 23, 24, 25, 27,
300 28, 30, 32, 33, 35, 37, 39, 42, 44, 47,
301 49, 52, 55, 58, 61, 65, 68, 72, 76, 81,
302 85, 90, 95, 100, 106, 112, 118, 125, 132, 139,
303 147, 156, 164, 174, 183, 194, 205, 216, 228, 241,
304 255
305 };
306 return (uint8_t)pgm_read_byte(&pwmtable[level]);
307#else
308 // max pwm: 255-19, 101 steps, every step +20
309 static const uint8_t pwmtable[101] PROGMEM = {
310 0, 20, 20, 20, 20, 20, 20, 21, 21, 21,
311 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
312 22, 22, 22, 23, 23, 23, 23, 24, 24, 24,
313 24, 25, 25, 25, 26, 26, 26, 27, 27, 28,
314 28, 29, 29, 30, 30, 31, 32, 32, 33, 34,
315 35, 36, 37, 38, 39, 40, 41, 42, 43, 45,
316 46, 48, 49, 51, 53, 55, 57, 59, 61, 63,
317 66, 68, 71, 74, 77, 80, 83, 87, 91, 95,
318 99, 103, 108, 113, 118, 124, 130, 136, 142, 149,
319 156, 164, 172, 181, 190, 199, 209, 220, 231, 243,
320 255
321 };
322 return 255 - (uint8_t)pgm_read_byte(&pwmtable[level]);
323#endif
324}
325
326void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update)
327{
328 if (!(sensor->type & SENSOR_DIMMER))
329 return;
330 if (level > 100)
331 level = 100;
332
333 Serial.print(F("Incoming change for dimmer: "));
334 Serial.print(sensor->dimmer.pin);
335 Serial.print(F(", New level: "));
336 Serial.println(level);
337
338 if (level > 0 && sensor->type & SENSOR_RELAY && !relayRead(sensor))
339 relayWrite(sensor, RELAY_ON);
340
341 int delta = ((int8_t)(level - sensor->dimmer.level) < 0) ? -1 : 1;
342 while (sensor->dimmer.level != level)
343 {
344 sensor->dimmer.level += delta;
345 analogWrite(sensor->dimmer.pin, pwmValue(sensor->dimmer.level));
346 delay(DIMMER_FADE_DELAY);
347 }
348
349 if (level == 0 && sensor->type & SENSOR_RELAY && relayRead(sensor))
350 relayWrite(sensor, RELAY_OFF);
351
352#ifdef SAVE_RESTORE
353 saveState(NUM(sensors) + sensor->id, level);
354#endif
355
356 if (send_update)
357 send(msg.setType(V_PERCENTAGE).setSensor(sensor->id).set(level));
358}