summaryrefslogtreecommitdiffstats
path: root/tv_light
diff options
context:
space:
mode:
Diffstat (limited to 'tv_light')
-rw-r--r--tv_light/tv_light.ino335
1 files changed, 335 insertions, 0 deletions
diff --git a/tv_light/tv_light.ino b/tv_light/tv_light.ino
new file mode 100644
index 0000000..5588cef
--- /dev/null
+++ b/tv_light/tv_light.ino
@@ -0,0 +1,335 @@
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 analogWrite(sensor->dimmer.pin, pwmValue(level));
136 }
137 }
138
139#ifdef MY_AES_KEY
140 const uint8_t user_aes_key[16] = { MY_AES_KEY };
141 uint8_t cur_aes_key[16];
142 hwReadConfigBlock((void*)&cur_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(cur_aes_key));
143 if (memcmp(&user_aes_key, &cur_aes_key, 16) != 0)
144 {
145 hwWriteConfigBlock((void*)user_aes_key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, sizeof(user_aes_key));
146 debug(PSTR("AES key written\n"));
147 }
148#endif
149}
150
151void setup()
152{
153#ifdef MY_IS_RFM69HW
154 _radio.setHighPower(true);
155#endif
156#ifdef MY_RFM69_ATC
157 _radio.enableAutoPower(-70);
158 debug(PSTR("ATC enabled\n"));
159#endif
160}
161
162void presentation()
163{
164 sendSketchInfo("TVLight", "1.0");
165
166 // register all sensors to gw (they will be created as child devices)
167 for (uint8_t i = 0; i < NUM(sensors); i++)
168 {
169 struct sensor_t *sensor = &sensors[i];
170 if (sensor->type & SENSOR_DIMMER)
171 present(sensor->id, S_DIMMER);
172 else if (sensor->type & SENSOR_RELAY || sensor->type & SENSOR_BUTTON)
173 present(sensor->id, S_BINARY);
174 }
175
176#if TEMP_SENSOR_ID >= 0
177 present(TEMP_SENSOR_ID, S_TEMP);
178#endif
179}
180
181void loop()
182{
183 //TODO maybe call _radio.rcCalibration() all 1000x changes?
184#if TEMP_SENSOR_ID >= 0
185 checkTemperature();
186#endif
187}
188
189inline void checkTemperature(void)
190{
191 static unsigned long lastTempUpdate = millis();
192 static unsigned int numTempUpdates = 0;
193 static float lastTemp = 0;
194 static MyMessage msgTemp(TEMP_SENSOR_ID, V_TEMP);
195
196 unsigned long now = millis();
197 if (now - lastTempUpdate > TEMP_READ_INTERVAL)
198 {
199 float temp = _radio.readTemperature() + TEMP_OFFSET;
200 lastTempUpdate = now;
201 if (isnan(temp))
202 Serial.println(F("Failed reading temperature"));
203 else if (abs(temp - lastTemp) >= 2 || numTempUpdates == TEMP_N_READS_MSG)
204 {
205 lastTemp = temp;
206 numTempUpdates = 0;
207 send(msgTemp.set(temp, 2));
208#ifdef MY_DEBUG
209 char str_temp[6];
210 dtostrf(temp, 4, 2, str_temp);
211 debug(PSTR("Temperature: %s °C\n"), str_temp);
212#endif
213 }
214 else
215 ++numTempUpdates;
216 }
217}
218
219void receive(const MyMessage &message)
220{
221 if (message.type == V_STATUS || message.type == V_PERCENTAGE)
222 {
223 uint8_t sensor_id = message.sensor;
224 if (sensor_id >= NUM(sensors))
225 {
226 Serial.print(F("Invalid sensor id:"));
227 Serial.println(sensor_id);
228 return;
229 }
230
231 struct sensor_t *sensor = &sensors[sensor_id];
232 if (message.type == V_STATUS && sensor->type & SENSOR_RELAY)
233 {
234 if (mGetCommand(message) == C_REQ)
235 send(msg.setType(V_STATUS).setSensor(sensor->id).set(relayRead(sensor)));
236 else if (mGetCommand(message) == C_SET)
237 relayWrite(sensor, message.getBool());
238 }
239 else if (message.type == V_PERCENTAGE && sensor->type & SENSOR_DIMMER)
240 {
241 if (mGetCommand(message) == C_REQ)
242 send(msg.setType(V_PERCENTAGE).setSensor(sensor->id).set(sensor->dimmer.level));
243 else if (mGetCommand(message) == C_SET)
244 {
245 uint16_t level = message.getUInt();
246 fadeDimmer(sensor, (level > 100) ? 100 : level);
247 }
248 }
249 }
250}
251
252bool relayRead(struct sensor_t *sensor)
253{
254 if (sensor->type & SENSOR_RELAY)
255 return digitalRead(sensor->relay.pin) == RELAY_ON;
256 return false;
257}
258
259void relayWrite(struct sensor_t *sensor, bool state, bool send_update)
260{
261 if (!(sensor->type & SENSOR_RELAY))
262 return;
263
264 Serial.print(F("Incoming change for relay: "));
265 Serial.print(sensor->relay.pin);
266 Serial.print(F(", New state: "));
267 Serial.println(state);
268
269 digitalWrite(sensor->relay.pin, state ? RELAY_ON : RELAY_OFF);
270
271#ifdef SAVE_RESTORE
272 saveState(sensor->id, state ? RELAY_ON : RELAY_OFF);
273#endif
274
275 if (send_update)
276 send(msg.setType(V_STATUS).setSensor(sensor->id).set(state));
277}
278
279void flipRelay(struct sensor_t *sensor, bool send_update)
280{
281 relayWrite(sensor, relayRead(sensor) ? RELAY_OFF : RELAY_ON, send_update);
282}
283
284inline uint8_t pwmValue(uint8_t level)
285{
286 const uint8_t pwmtable[101] PROGMEM = {
287 0, 1, 1, 1, 1, 1, 1, 2, 2, 2,
288 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
289 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
290 5, 6, 6, 6, 7, 7, 8, 8, 8, 9,
291 9, 10, 11, 11, 12, 12, 13, 14, 15, 16,
292 16, 17, 18, 19, 20, 22, 23, 24, 25, 27,
293 28, 30, 32, 33, 35, 37, 39, 42, 44, 47,
294 49, 52, 55, 58, 61, 65, 68, 72, 76, 81,
295 85, 90, 95, 100, 106, 112, 118, 125, 132, 139,
296 147, 156, 164, 174, 183, 194, 205, 216, 228, 241,
297 255
298 };
299 return (uint8_t)pgm_read_byte(&pwmtable[level]);
300}
301
302void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update)
303{
304 if (!(sensor->type & SENSOR_DIMMER))
305 return;
306 if (level > 100)
307 level = 100;
308
309 Serial.print(F("Incoming change for dimmer: "));
310 Serial.print(sensor->dimmer.pin);
311 Serial.print(F(", New level: "));
312 Serial.println(level);
313
314 if (level > 0 && sensor->type & SENSOR_RELAY && !relayRead(sensor))
315 relayWrite(sensor, RELAY_ON);
316
317 int delta = ((int8_t)(level - sensor->dimmer.level) < 0) ? -1 : 1;
318 while (sensor->dimmer.level != level)
319 {
320 sensor->dimmer.level += delta;
321 analogWrite(sensor->dimmer.pin, pwmValue(sensor->dimmer.level));
322 delay(DIMMER_FADE_DELAY);
323 }
324
325 if (level == 0 && sensor->type & SENSOR_RELAY && relayRead(sensor))
326 relayWrite(sensor, RELAY_OFF);
327
328#ifdef SAVE_RESTORE
329 saveState(NUM(sensors) + sensor->id, level);
330#endif
331
332 if (send_update)
333 send(msg.setType(V_PERCENTAGE).setSensor(sensor->id).set(level));
334}
335