summaryrefslogtreecommitdiffstats
path: root/tv_light/src/tv_light.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tv_light/src/tv_light.cpp')
-rw-r--r--tv_light/src/tv_light.cpp358
1 files changed, 358 insertions, 0 deletions
diff --git a/tv_light/src/tv_light.cpp b/tv_light/src/tv_light.cpp
new file mode 100644
index 0000000..caf3a6e
--- /dev/null
+++ b/tv_light/src/tv_light.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}