Merry Boxes & LEDs

Enter Your Project for a chance to win a Maker Tool Kit, an Oscilloscope Grand Prize for the project that brings the Most Joy to the Heart, and Gift to Gives!

Back to The Project14 homepage

Project14 Home
Monthly Themes
Monthly Theme Poll

 

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:

Connected Christmas Trees

Assembling the Trees

Finally! Two Trees Assembled

Two Trees by a Tree

Connecting the Tree to the Internet

Motion Sensor on Wemos

Trees! one with motion sickness...

Motion Sensor Issues, continued

The Connected Trees are Working!

Merry Boxes - A Project Within a Project