summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2017-03-26 01:07:06 +0100
committermanuel <manuel@mausz.at>2017-03-26 01:07:06 +0100
commit589d11bfd1e1cfc7a383bc0e6e09d9c5ec02288d (patch)
tree892f5fd6a171b6a59c4d8bf6edba3b19cab78108
downloadarduino-589d11bfd1e1cfc7a383bc0e6e09d9c5ec02288d.tar.gz
arduino-589d11bfd1e1cfc7a383bc0e6e09d9c5ec02288d.tar.bz2
arduino-589d11bfd1e1cfc7a383bc0e6e09d9c5ec02288d.zip
Initial commit
-rw-r--r--.gitignore1
-rw-r--r--couch_light/couch_light.ino283
-rw-r--r--hyperion/hyperion.ino279
-rw-r--r--lr_switch/lr_switch.ino197
-rw-r--r--rgbtv_light/rgbtv_light.ino241
-rw-r--r--testnode/testnode.ino316
-rw-r--r--tv_light/tv_light.ino335
7 files changed, 1652 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7129b63
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
aes_key.h
diff --git a/couch_light/couch_light.ino b/couch_light/couch_light.ino
new file mode 100644
index 0000000..b99381a
--- /dev/null
+++ b/couch_light/couch_light.ino
@@ -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/hyperion/hyperion.ino b/hyperion/hyperion.ino
new file mode 100644
index 0000000..75e6858
--- /dev/null
+++ b/hyperion/hyperion.ino
@@ -0,0 +1,279 @@
1// Arduino "bridge" code between host computer and WS2801-based digital
2// RGB LED pixels (e.g. Adafruit product ID #322). Intended for use
3// with USB-native boards such as Teensy or Adafruit 32u4 Breakout;
4// works on normal serial Arduinos, but throughput is severely limited.
5// LED data is streamed, not buffered, making this suitable for larger
6// installations (e.g. video wall, etc.) than could otherwise be held
7// in the Arduino's limited RAM.
8
9// Some effort is put into avoiding buffer underruns (where the output
10// side becomes starved of data). The WS2801 latch protocol, being
11// delay-based, could be inadvertently triggered if the USB bus or CPU
12// is swamped with other tasks. This code buffers incoming serial data
13// and introduces intentional pauses if there's a threat of the buffer
14// draining prematurely. The cost of this complexity is somewhat
15// reduced throughput, the gain is that most visual glitches are
16// avoided (though ultimately a function of the load on the USB bus and
17// host CPU, and out of our control).
18
19// LED data and clock lines are connected to the Arduino's SPI output.
20// On traditional Arduino boards, SPI data out is digital pin 11 and
21// clock is digital pin 13. On both Teensy and the 32u4 Breakout,
22// data out is pin B2, clock is B1. LEDs should be externally
23// powered -- trying to run any more than just a few off the Arduino's
24// 5V line is generally a Bad Idea. LED ground should also be
25// connected to Arduino ground.
26
27// --------------------------------------------------------------------
28// This file is part of Adalight.
29
30// Adalight is free software: you can redistribute it and/or modify
31// it under the terms of the GNU Lesser General Public License as
32// published by the Free Software Foundation, either version 3 of
33// the License, or (at your option) any later version.
34
35// Adalight is distributed in the hope that it will be useful,
36// but WITHOUT ANY WARRANTY; without even the implied warranty of
37// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38// GNU Lesser General Public License for more details.
39
40// You should have received a copy of the GNU Lesser General Public
41// License along with Adalight. If not, see
42// <http://www.gnu.org/licenses/>.
43// --------------------------------------------------------------------
44
45
46// --------------------------------------------------------------------
47// Note: This is a fork form the LEDstream.
48//
49// This version uses the LIB FastLED to work with the WS2812b LEDs.
50//
51// This Version works with a 150LED-Stripe with a frames/sec: 61, bytes/sec: 11524
52// tested with the programme colorswirl (see the C folder) and an Arduino UNO.
53//
54// TOOD:
55// - Cleanup: remove the SPI code
56// - Show a startup Pattern
57// - remove flicker when sending more the 63 frames/sec.
58//
59// --------------------------------------------------------------------
60
61#include <FastLED.h>
62#include <avr/sleep.h>
63#include <avr/power.h>
64
65#define NUM_LEDS 190
66#define DATA_PIN 3
67CRGB leds[NUM_LEDS];
68
69// A 'magic word' (along with LED count & checksum) precedes each block
70// of LED data; this assists the microcontroller in syncing up with the
71// host-side software and properly issuing the latch (host I/O is
72// likely buffered, making usleep() unreliable for latch). You may see
73// an initial glitchy frame or two until the two come into alignment.
74// The magic word can be whatever sequence you like, but each character
75// should be unique, and frequent pixel values like 0 and 255 are
76// avoided -- fewer false positives. The host software will need to
77// generate a compatible header: immediately following the magic word
78// are three bytes: a 16-bit count of the number of LEDs (high byte
79// first) followed by a simple checksum value (high byte XOR low byte
80// XOR 0x55). LED data follows, 3 bytes per LED, in order R, G, B,
81// where 0 = off and 255 = max brightness.
82
83static const uint8_t magic[] = {'A','d','a'};
84#define MAGICSIZE sizeof(magic)
85#define HEADERSIZE (MAGICSIZE + 3)
86
87#define MODE_HEADER 0
88#define MODE_HOLD 1
89#define MODE_DATA 2
90
91// If no serial data is received for a while, the LEDs are shut off
92// automatically. This avoids the annoying "stuck pixel" look when
93// quitting LED display programs on the host computer.
94static const unsigned long serialTimeout = 15000; // 15 seconds
95
96void setup()
97{
98 // Dirty trick: the circular buffer for serial data is 256 bytes,
99 // and the "in" and "out" indices are unsigned 8-bit types -- this
100 // much simplifies the cases where in/out need to "wrap around" the
101 // beginning/end of the buffer. Otherwise there'd be a ton of bit-
102 // masking and/or conditional code every time one of these indices
103 // needs to change, slowing things down tremendously.
104 uint8_t
105 buffer[256],
106 indexIn = 0,
107 indexOut = 0,
108 mode = MODE_HEADER,
109 hi, lo, chk, i, spiFlag,
110 r,b,g, l;
111 int16_t
112 bytesBuffered = 0,
113 hold = 0,
114 c;
115 int32_t
116 bytesRemaining;
117 unsigned long
118 startTime,
119 lastByteTime,
120 lastAckTime,
121 t;
122 uint32_t value,index = 0, ledcount;
123
124 delay(5000);
125
126#if 1
127 power_adc_disable();
128 power_usart0_disable();
129 power_usart1_disable();
130 power_spi_disable();
131 power_twi_disable();
132 power_timer1_disable();
133 power_timer2_disable();
134 power_timer3_disable();
135#endif
136
137 FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
138
139 Serial.begin(115200); // Teensy/32u4 disregards baud rate; is OK!
140
141 //set the last LED to white
142 leds[NUM_LEDS-1].setRGB(100,100,100);
143 FastLED.show();
144 //delay(1000);
145 //reset();
146 //delay(1000);
147
148 Serial.print("Ada\n"); // Send ACK string to host
149 RXLED1;
150 TXLED1;
151
152 startTime = micros();
153 lastByteTime = lastAckTime = millis();
154
155 // loop() is avoided as even that small bit of function overhead
156 // has a measurable impact on this code's overall throughput.
157
158 for(;;)
159 {
160 RXLED1;
161 TXLED1;
162
163 // Implementation is a simple finite-state machine.
164 // Regardless of mode, check for serial input each time:
165 t = millis();
166 if ((bytesBuffered < 256) && ((c = Serial.read()) >= 0))
167 {
168 buffer[indexIn++] = c;
169 bytesBuffered++;
170 lastByteTime = lastAckTime = t; // Reset timeout counters
171 }
172 else
173 {
174 // No data received. If this persists, send an ACK packet
175 // to host once every second to alert it to our presence.
176 if((t - lastAckTime) > 1000)
177 {
178 Serial.print("Ada\n"); // Send ACK string to host
179 lastAckTime = t; // Reset counter
180 }
181 // If no data received for an extended time, turn off all LEDs.
182 if((t - lastByteTime) > serialTimeout)
183 {
184 reset();
185 delay(1); // One millisecond pause = latch
186 lastByteTime = t; // Reset counter
187 }
188 }
189
190 switch(mode)
191 {
192 case MODE_HEADER:
193 // In header-seeking mode. Is there enough data to check?
194 if(bytesBuffered >= HEADERSIZE)
195 {
196 // Indeed. Check for a 'magic word' match.
197 for(i=0; (i<MAGICSIZE) && (buffer[indexOut++] == magic[i++]););
198 if(i == MAGICSIZE)
199 {
200 // Magic word matches. Now how about the checksum?
201 hi = buffer[indexOut++];
202 lo = buffer[indexOut++];
203 chk = buffer[indexOut++];
204 if(chk == (hi ^ lo ^ 0x55))
205 {
206 // Checksum looks valid. Get 16-bit LED count, add 1
207 // (# LEDs is always > 0) and multiply by 3 for R,G,B.
208 bytesRemaining = 3L * (256L * (long)hi + (long)lo + 1L);
209 bytesBuffered -= 3;
210 ledcount = 0;
211 mode = MODE_HOLD; // Proceed to latch wait mode
212 }
213 else
214 {
215 // Checksum didn't match; search resumes after magic word.
216 indexOut -= 3; // Rewind
217 }
218 } // else no header match. Resume at first mismatched byte.
219 bytesBuffered -= i;
220 }
221 break;
222
223 case MODE_HOLD:
224 // Ostensibly "waiting for the latch from the prior frame
225 // to complete" mode, but may also revert to this mode when
226 // underrun prevention necessitates a delay.
227
228 if((micros() - startTime) < hold) break; // Still holding; keep buffering
229
230 // Latch/delay complete. Advance to data-issuing mode...
231 //LED_PORT &= ~LED_PIN; // LED off
232 mode = MODE_DATA; // ...and fall through (no break):
233
234 case MODE_DATA:
235 if(bytesRemaining > 2)
236 {
237 if(bytesBuffered > 2)
238 {
239 //we read one LED -> 3 Bytes r.g.b
240 bytesBuffered -= 3;
241 bytesRemaining -= 3;
242 leds[ledcount++].setRGB(buffer[indexOut++], buffer[indexOut++], buffer[indexOut++]);
243 }
244 // If serial buffer is threatening to underrun, start
245 // introducing progressively longer pauses to allow more
246 // data to arrive (up to a point).
247 if((bytesBuffered < 32) && (bytesRemaining > bytesBuffered) && (mode!=MODE_HEADER))
248 {
249 startTime = micros();
250 hold = 100 + (32 - bytesBuffered) * 10;
251 mode = MODE_HOLD;
252 }
253 }
254 else
255 {
256 // End of data -- issue latch:
257 startTime = micros();
258 hold = 1000; // Latch duration = 1000 uS
259 //LED_PORT |= LED_PIN; // LED on
260 mode = MODE_HEADER; // Begin next header search
261 FastLED.show();
262 }
263 } // end switch
264 } // end for(;;)
265}
266
267void reset()
268{
269 for (uint16_t i=0; i< NUM_LEDS; i++)
270 leds[i] = CRGB::Black;
271 FastLED.show();
272}
273
274void loop()
275{
276 // Not used. See note in setup() function.
277}
278
279
diff --git a/lr_switch/lr_switch.ino b/lr_switch/lr_switch.ino
new file mode 100644
index 0000000..673eb3a
--- /dev/null
+++ b/lr_switch/lr_switch.ino
@@ -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/rgbtv_light/rgbtv_light.ino b/rgbtv_light/rgbtv_light.ino
new file mode 100644
index 0000000..98120a6
--- /dev/null
+++ b/rgbtv_light/rgbtv_light.ino
@@ -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/testnode/testnode.ino b/testnode/testnode.ino
new file mode 100644
index 0000000..999c32a
--- /dev/null
+++ b/testnode/testnode.ino
@@ -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/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