summaryrefslogtreecommitdiffstats
path: root/oliver/lr_stripes/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'oliver/lr_stripes/src/main.cpp')
-rw-r--r--oliver/lr_stripes/src/main.cpp393
1 files changed, 393 insertions, 0 deletions
diff --git a/oliver/lr_stripes/src/main.cpp b/oliver/lr_stripes/src/main.cpp
new file mode 100644
index 0000000..b55a198
--- /dev/null
+++ b/oliver/lr_stripes/src/main.cpp
@@ -0,0 +1,393 @@
1#include <Arduino.h>
2#include <ESP8266mDNS.h>
3#include <PubSubClient.h>
4#include <WiFiManager.h>
5#include <Bounce2.h>
6#include <ESP8266httpUpdate.h>
7
8#define _STR(s) #s
9#define STR(s) _STR(s)
10
11const char* mqtt_server = "192.168.1.2"; //MQTT Server IP, your home MQTT server eg Mosquitto on RPi, or some public MQTT
12const int mqtt_port = 1883; //MQTT Server PORT, default is 1883 but can be anything.
13
14const char* mqtt_pingall_sub = "home/pingall";
15const char* mqtt_pingall_pub = "home/pingall/response";
16
17#define MQTT_BASE "home/livingroom/ledstripe"
18const char* mqtt_device_boot = MQTT_BASE "/device";
19
20enum sensor_type : uint8_t
21{
22 SENSOR_RELAY = (1u << 0),
23 SENSOR_DIMMER = (1u << 1),
24 SENSOR_BUTTON = (1u << 2),
25};
26
27struct sensor_t
28{
29 uint8_t type;
30 struct
31 {
32 uint8_t pin; // relay pin
33 const char *mqtt_sub;
34 const char *mqtt_pub;
35 } relay;
36 struct
37 {
38 uint8_t pin; // push button pin
39 Bounce bounce;
40 } button;
41 struct
42 {
43 uint8_t pin; // dimmer pin
44 uint8_t level; // current dim level (0 to 100)
45 const char *mqtt_sub;
46 const char *mqtt_pub;
47 } dimmer;
48};
49
50struct sensor_t sensors[] = {
51 {
52 .type = SENSOR_RELAY | SENSOR_BUTTON | SENSOR_DIMMER,
53 .relay = {
54 .pin = D5,
55 .mqtt_sub = MQTT_BASE,
56 .mqtt_pub = MQTT_BASE "/status"
57 },
58 .button = {
59 .pin = D0,
60 .bounce = Bounce(),
61 },
62 .dimmer = {
63 .pin = D8,
64 .level = 100,
65 .mqtt_sub = MQTT_BASE "/brightness",
66 .mqtt_pub = MQTT_BASE "/brightness/status"
67 },
68 }
69};
70
71//#define SAVE_RESTORE
72
73#define NUM(a) (sizeof(a) / sizeof(*a))
74
75#define RELAY_ON 1 // GPIO value to write to turn on attached relay
76#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
77
78#define DIMMER_FADE_DELAY 40 // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
79
80WiFiClient espClient;
81PubSubClient client(espClient);
82char convBuffer[10];
83
84void callback(char* topic, byte* payload, unsigned int length);
85void blink();
86bool relayRead(struct sensor_t *sensor);
87void relayWrite(struct sensor_t *sensor, bool state, bool send_update=false);
88void flipRelay(struct sensor_t *sensor, bool send_update=false);
89void checkButtons(void);
90inline uint8_t pwmValue(uint8_t level);
91void fadeDimmer(struct sensor_t *sensor, uint8_t level, bool send_update=false);
92void checkFirmwareUpdate();
93
94void setup()
95{
96 Serial.begin(115200);
97 pinMode(LED_BUILTIN, OUTPUT);
98 analogWriteRange(100);
99
100 // set relay pins to output mode + restore to last known state
101 for (uint8_t i = 0; i < NUM(sensors); i++)
102 {
103 struct sensor_t *sensor = &sensors[i];
104 if (sensor->type & SENSOR_RELAY)
105 {
106 pinMode(sensor->relay.pin, OUTPUT);
107#ifdef SAVE_RESTORE
108#error "not implemented"
109#else
110 digitalWrite(sensor->relay.pin, RELAY_OFF);
111#endif
112 }
113
114 if (sensor->type & SENSOR_DIMMER)
115 {
116 pinMode(sensor->dimmer.pin, OUTPUT);
117#ifdef SAVE_RESTORE
118#error "not implemented"
119#else
120 uint8_t level = sensor->dimmer.level;
121#endif
122 if ((sensor->type & SENSOR_RELAY) && !relayRead(sensor))
123 level = 0;
124 analogWrite(sensor->dimmer.pin, pwmValue(level));
125 }
126
127 if (sensor->type & SENSOR_BUTTON)
128 {
129 pinMode(sensor->button.pin, INPUT);
130 sensor->button.bounce.attach(sensor->button.pin);
131 }
132 }
133
134 WiFiManager wifiManager;
135 wifiManager.setConfigPortalTimeout(600);
136 Serial.printf_P(PSTR("Setting up WiFi\n"));
137 if (!wifiManager.autoConnect("ESP8266_LR_STRIPES")) {
138 Serial.printf_P(PSTR("Failed to connect and hit timeout\n"));
139 delay(5000);
140 ESP.restart();
141 }
142
143 yield();
144 checkFirmwareUpdate();
145 yield();
146
147 client.setServer(mqtt_server, mqtt_port);
148 client.setCallback(callback);
149
150 digitalWrite(LED_BUILTIN, HIGH); //Turn off led as default
151}
152
153void reconnect()
154{
155 // Loop until we're reconnected
156 while (!client.connected())
157 {
158 // Create a random client ID
159 String clientId = "ESP8266Client-";
160 clientId += String(random(0xffff), HEX);
161
162 // Attempt to connect
163 if (client.connect(clientId.c_str()))
164 {
165 // Once connected, publish an announcement...
166 client.publish(mqtt_device_boot, "connected");
167 // ... and resubscribe
168 client.subscribe(mqtt_pingall_sub);
169
170 // publish states
171 for (uint8_t i = 0; i < NUM(sensors); i++)
172 {
173 struct sensor_t *sensor = &sensors[i];
174 if (sensor->type & SENSOR_RELAY)
175 {
176 Serial.println(sensor->relay.mqtt_sub);
177 client.subscribe(sensor->relay.mqtt_sub);
178 client.publish(sensor->relay.mqtt_pub, relayRead(sensor) ? "1" : "0",
179 true);
180 }
181
182 if (sensor->type & SENSOR_DIMMER)
183 {
184 Serial.println(sensor->dimmer.mqtt_sub);
185 client.subscribe(sensor->dimmer.mqtt_sub);
186 itoa(sensor->dimmer.level, convBuffer, 10);
187 client.publish(sensor->dimmer.mqtt_pub, convBuffer, true);
188 }
189 }
190 }
191 else
192 {
193 // Wait 5 seconds before retrying
194 delay(5000);
195 }
196 }
197}
198
199void callback(char* topic, byte* payload, unsigned int length)
200{
201 char c_payload[length];
202 memcpy(c_payload, payload, length);
203 c_payload[length] = '\0';
204
205 if (strcmp(topic, mqtt_pingall_sub) == 0)
206 {
207 blink();
208 client.publish(mqtt_pingall_pub,
209 "{\"livingroom_ledstrip\":\"connected\"}");
210 return;
211 }
212
213 for (uint8_t i = 0; i < NUM(sensors); i++)
214 {
215 struct sensor_t *sensor = &sensors[i];
216 if (sensor->type & SENSOR_RELAY
217 && length > 0
218 && strcmp(topic, sensor->relay.mqtt_sub) == 0)
219 {
220 blink();
221 relayWrite(sensor, payload[0] == '1', true);
222 return;
223 }
224
225 if (sensor->type & SENSOR_DIMMER
226 && length > 0
227 && strcmp(topic, sensor->dimmer.mqtt_sub) == 0)
228 {
229 blink();
230 uint8_t level = atoi((char *)payload);
231 fadeDimmer(sensor, (level > 100) ? 100 : level, true);
232 return;
233 }
234 }
235}
236
237void blink()
238{
239 //Blink on received MQTT message
240 digitalWrite(LED_BUILTIN, LOW);
241 delay(25);
242 digitalWrite(LED_BUILTIN, HIGH);
243}
244
245void loop()
246{
247 if (!client.connected())
248 reconnect();
249 client.loop();
250 checkButtons();
251}
252
253bool relayRead(struct sensor_t *sensor)
254{
255 if (sensor->type & SENSOR_RELAY)
256 return digitalRead(sensor->relay.pin) == RELAY_ON;
257 return false;
258}
259
260void relayWrite(struct sensor_t *sensor, bool state, bool send_update)
261{
262 if (!(sensor->type & SENSOR_RELAY))
263 return;
264
265 Serial.print(F("Incoming change for relay: "));
266 Serial.print(sensor->relay.pin);
267 Serial.print(F(", New state: "));
268 Serial.println(state);
269
270 digitalWrite(sensor->relay.pin, state ? RELAY_ON : RELAY_OFF);
271
272 if (sensor->type & SENSOR_DIMMER)
273 analogWrite(sensor->dimmer.pin, state ? pwmValue(sensor->dimmer.level) : 0);
274
275#ifdef SAVE_RESTORE
276#error "not implemented"
277#endif
278
279 if (send_update)
280 client.publish(sensor->relay.mqtt_pub, state ? "1" : "0", true);
281}
282
283void flipRelay(struct sensor_t *sensor, bool send_update)
284{
285 relayWrite(sensor, relayRead(sensor) ? RELAY_OFF : RELAY_ON, send_update);
286}
287
288inline void checkButtons(void)
289{
290 for (uint8_t i = 0; i < NUM(sensors); i++)
291 {
292 struct sensor_t *sensor = &sensors[i];
293 if (sensor->type & SENSOR_BUTTON)
294 {
295 sensor->button.bounce.update();
296 if (sensor->button.bounce.fell())
297 flipRelay(sensor, true);
298 }
299 }
300}
301
302const uint8_t pwmtable[101] PROGMEM = {
303 // value below 0 turns of the power supply
304 0, 10, 10, 11, 11, 11, 11, 12, 12, 12,
305 13, 13, 13, 13, 14, 14, 14, 15, 15, 15,
306 16, 16, 17, 17, 17, 18, 18, 19, 19, 19,
307 20, 20, 21, 21, 22, 22, 23, 23, 24, 25,
308 25, 26, 26, 27, 28, 28, 29, 30, 30, 31,
309 32, 32, 33, 34, 35, 35, 36, 37, 38, 39,
310 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
311 50, 51, 52, 54, 55, 56, 58, 59, 60, 62,
312 63, 65, 66, 68, 69, 71, 72, 74, 76, 78,
313 79, 81, 83, 85, 87, 89, 91, 93, 95, 98,
314 100
315};
316
317inline uint8_t pwmValue(uint8_t level)
318{
319 //uint8_t lvl = 100 - (uint8_t)pgm_read_byte(&pwmtable[level]);
320 //return (lvl < 10) 10 : lvl;
321 //level = (level > 0 && level < 10) ? 10 : level;
322 //return 100 - level;
323 return 100 - (uint8_t)pgm_read_byte(&pwmtable[level]);
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, send_update);
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, send_update);
351
352#ifdef SAVE_RESTORE
353#error "not implemented"
354#endif
355
356 if (send_update)
357 {
358 itoa(level, convBuffer, 10);
359 client.publish(sensor->dimmer.mqtt_pub, convBuffer, true);
360 }
361}
362
363void checkFirmwareUpdate()
364{
365 BearSSL::WiFiClientSecure update_client;
366 update_client.setInsecure();
367
368 client.publish(mqtt_device_boot, "fwupdate running");
369 ESPhttpUpdate.setLedPin(LED_BUILTIN, HIGH);
370 ESPhttpUpdate.rebootOnUpdate(true);
371 t_httpUpdate_return ret = ESPhttpUpdate.update(update_client, FIRMWARE_URL, STR(FIRMWARE_VERSION));
372 switch(ret)
373 {
374 case HTTP_UPDATE_FAILED:
375 {
376 Serial.printf_P(PSTR("HTTP_UPDATE_FAILED Error (%d): %s\n"),
377 ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
378 String tmp = String("fwupdate error: ") + ESPhttpUpdate.getLastErrorString();
379 client.publish(mqtt_device_boot, tmp.c_str());
380 }
381 break;
382
383 case HTTP_UPDATE_NO_UPDATES:
384 Serial.printf_P(PSTR("HTTP_UPDATE_NO_UPDATES\n"));
385 client.publish(mqtt_device_boot, "fwupdate noupdates");
386 break;
387
388 case HTTP_UPDATE_OK:
389 Serial.printf_P(PSTR("HTTP_UPDATE_OK\n"));
390 client.publish(mqtt_device_boot, "fwupdate ok");
391 break;
392 }
393}