And now... time to unveil the finished project!
The last step was to hide the electronics from the trees in a semi-festive manner.
Using the little paper box imitations of Altoids tins that I created in my previous blog post, I was able to tuck all the pieces away nicely and create a little stand for the trees at the same time. The Christmas card box material added a bit of a festive touch.
This is where we left off in the previous blog post. The boxes showed great promise of being able to fit all the pieces:
Using a small hole punch I added a hole to fit the button, and firmly taped it into place.
I also added a hole to pass the USB power cable.
And near the top I added a small hole to pass the wires to the Christmas tree.
All the wires and bits fit quite nicely. I added another piece or card to make sure the wires wouldn't short out against the Wemos board.
As you may recall from a previous blog post, I did also have nail polish covering all the exposed solder joints.
The end result turned out nicer than I had imagined! Yay me!
I used iPhone chargers to power the trees. These are actual Apple chargers that provide a true 1Amp (the knock-offs tend to only handle about 600mA, I've noticed).
I took a moment to draw out the schematics. My earlier sketch was, well, rather sketchy...
I've added the fritzing file to my github folder too.
Here is the final source code (also available in my github folder)
/* * ConnectedXmas * * Connected Xmas Trees * * Using MQTT, motion sensed at one tree will light the other tree * * Motion sensed also lights the tip of the local tree, to give indication to the local user. * Note that motion sensor will likely need external power. Or use a simple pushbutton * * free service: https://www.hivemq.com/try-out/ http://www.mqtt-dashboard.com/ Use the browser based client for testing: http://www.hivemq.com/demos/websocket-client/ On the browser client, remember to: 1) connect (use defaults) 2) Subscribe to the topic: ConnectedXmasTreeTopic From browser client, can send a 1 to turn on the tree, 0 to turn it off PubSubClient for Arduino - use Arduino Package Installer. -> see examples that are included in PubSubClient. Also see https://www.baldengineer.com/mqtt-tutorial.html * */ #include <ESP8266WiFi.h> #include <PubSubClient.h> // WiFi connection const char* ssid = "yourWiFiName"; // replace with your own WiFi network name const char* password = "YourWiFiPassword"; // replace with your own WiFi network password // MQTT server const char* mqtt_server = "broker.hivemq.com"; const char* mqtt_topic = "ConnectedXmasTreeTopic"; char mqtt_message[50]; const char myTreeID = '1'; // switch these two numbers for the second tree. const char otherTreeID = '2'; // ie, one WeMos gets programmed with myTreeID = 1 and otherTreeID = 2, the other WeMos gets 2 and 1. // Use WiFiClient class to create TCP connections WiFiClient espClient; PubSubClient client(espClient); // For Wemos D1 Mini, the built-in LED is D4 #define MOTION_SENSOR D2 // same as button on Witty // D8 has *external, always-active* 10k pull-down, D3 has pull-up #define LED_TREETOP D6 // same as Green RGB LED on Witty #define LED_TREE D8 // same as Red RGB LED on Witty #define LED_ON HIGH #define LED_OFF LOW #define SECOND 1000ul #define MINUTE (60ul * SECOND) #define TREE_ON_TIME 10*SECOND // 5*MINUTE #define MIN_TIME_BETWEEN_MESSAGES 5*SECOND unsigned long timeThisTreeTurnedOn; unsigned long timeOtherTreeTurnedOn; bool thisTreeIsOn = false; bool otherTreeIsOn = false; void setup() { Serial.begin(115200); Serial.println(); Serial.println(); Serial.println("\n\nStarting...\n"); pinMode(LED_TREETOP, OUTPUT); pinMode(LED_TREE, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); flashBuiltInLED(2); digitalWrite(LED_TREETOP, LED_OFF); digitalWrite(LED_TREE, LED_OFF); pinMode(MOTION_SENSOR, INPUT); // use a 10k external resistor to ground connectWiFi(); flashBuiltInLED(5); Serial.println("Setting up MQTT connection"); client.setServer(mqtt_server, 1883); client.setCallback(mqtt_callback); Serial.println("Setup is complete.\n"); flashThisTree(2); } void connectWiFi() { delay(10); Serial.print("\n\nConnecting to WiFi: "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); flashBuiltInLED(1); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); // Serial.print("Netmask: "); // Serial.println(WiFi.subnetMask()); // Serial.print("Gateway: "); // Serial.println(WiFi.gatewayIP()); } void mqtt_callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); // Switch on this tree if an 1 was received as first character if ((char)payload[0] == myTreeID) { flashThisTree(7); if (length == 1) { lightThisTree(); } } } void mqtt_reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.println("Attempting MQTT connection..."); // Create a random client ID String clientId = "ConnectedXmasTreeClient-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { Serial.println("connected"); // Once connected, publish an announcement... // this will cause the other tree to flash snprintf (mqtt_message, 50, "%c Hello world!", otherTreeID); client.publish(mqtt_topic, mqtt_message); // ... and resubscribe client.subscribe(mqtt_topic); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying // tree top flashes when waiting to reconnect for (int i=0; i<10; i++) { digitalWrite(LED_TREETOP, LED_ON); delay(250); digitalWrite(LED_TREETOP, LED_OFF); delay(250); } } } } void loop() { if (!client.connected()) { mqtt_reconnect(); } client.loop(); // check for motion, unless motion was already detected recently if (millis() - timeOtherTreeTurnedOn > MIN_TIME_BETWEEN_MESSAGES) { // instead of button, will have motion sensor if (digitalRead(MOTION_SENSOR) == HIGH) { Serial.println("Motion detected!"); lightOtherTree(); } } if (otherTreeIsOn && millis() - timeOtherTreeTurnedOn > TREE_ON_TIME) { // no message needed - other tree turns off at same kind of interval // turn off tree top indicator light digitalWrite(LED_TREETOP, LED_OFF); otherTreeIsOn = false; } if (thisTreeIsOn && millis() - timeThisTreeTurnedOn > TREE_ON_TIME) { digitalWrite(LED_TREE, LED_OFF); thisTreeIsOn = false; } } void lightOtherTree() { char msg[2]; msg[0] = otherTreeID; msg[1] = '\0'; digitalWrite(LED_TREETOP, LED_ON); // turn on local tree top light. Green for testing Serial.println("Sending message to light the other tree"); client.publish(mqtt_topic, msg); otherTreeIsOn = true; timeOtherTreeTurnedOn = millis(); } void lightThisTree() { // flash the tree digitalWrite(LED_TREE, LED_ON); delay(200); digitalWrite(LED_TREE, LED_OFF); delay(200); digitalWrite(LED_TREE, LED_ON); thisTreeIsOn = true; timeThisTreeTurnedOn = millis(); } void flashThisTree(int count) { for (int i=0; i<count; i++) { digitalWrite(LED_TREE, LED_ON); delay(100); digitalWrite(LED_TREE, LED_OFF); delay(100); } } void flashBuiltInLED(int count) { for (int i=0; i<count; i++) { digitalWrite(LED_BUILTIN, LOW); delay(100); digitalWrite(LED_BUILTIN, HIGH); delay(100); } }
For the final real-life implementation you'd want to increase the TREE_ON_TIME to something more than 10 seconds - maybe a minute or 5 minutes.
I think I still would have liked having the motion sensors as part of the project, as that would keep a tree lit up as long as someone is near it setting off the sensor once in a while. With my button setup you have to press the button every so often.
However, I realized that I would need a separate power supply for the sensors to do that. This could probably be done with a little adapter placed at the micro-USB plug that taps into the incoming power supply so that power can be taken off the source, rather than after the diode and fuse that are on the Wemos D1 Mini.
I also learned that the Wemos D1 Mini has some minor details different from the average Arduino. Mainly the added external resistors and the lack of internal pull-up/pull-down. That had my head spinning for a bit.
The lesson there is that I need to check the schematics and not just assume things will be the same.
I'm very happy that I was able to find enough time scattered over the holidays and weekends to actually do a nice little project again. It almost didn't happen this year!
I also thought this project would have been much quicker to implement, but there were more "lessons" than I had anticipated
Giving up would have been too easy, and I'm glad I stuck it out and learned a few new things along the way - and isn't that kind of the point of this hobby?
Cheers,
-Nico
Previous posts:
Connecting the Tree to the Internet
Trees! one with motion sickness...
Motion Sensor Issues, continued
Top Comments