Luis del ValleGermán Martín
Published

Dash Button Santa with Arduino MKR1000

Send information to Santa Claus about the status of the gift request.

AdvancedFull instructions provided2 hours3,785

Things used in this project

Story

Read more

Schematics

Dash Button Santa - Schematics

It looks great if you use a button on the nose of a Rudolf or Santa Claus doll

Code

Main Sketch

Arduino
/*
  DashButtonSanta

  Send information to Santa Claus about the status of the gift request. It uses the geolocation through the WiFi,
  of the Google API, and it is sent to Firebase, along with the state and with the MAC (key value) of the Arduino MKR1000.

  It also sends the information to Twitter @ dashbuttonsanta and to the email dashbuttonsanta@gmail.com through IFTTT.

  On the web https://programarfacil.com/proyectos/dashbuttonsanta/tracking-santa.html you can follow the status of all requests.

  When at the end Santa leaves the gifts on the site sent to Twitter and the email, press the button and gives the delivery finished.

  States:
  0 => Device OFF (LED color red)
  1 => I have behaved well, I have made the bed, I have cleaned the dishes, I have helped an old lady, I pray every night, etc ...
       and I have sent the letter :) (LED color blue)
  2 => Is Christmas Day (LED blink color blue)
  3 => Santa claus left the presents and pressed the button (LED blink color green)

  Errors:
   Wifi shield not present => LED blink Color(233, 149, 16)
   NTP unreachable => LED blink Color(150, 0, 0)

  The circuit:
   Arduino MKR1000
   Pushbutton to pin 6
   pull-down resistor to pushbutton
   3 addressable LEDs
  Created 2017
  By https://programarfacil.com
  Luis del Valle @ldelvalleh
  Germn Martn @gmag12

*/
#include <Adafruit_NeoPixel.h>
#include <WiFi101.h>
#include <WiFiUdp.h>
#include <RTCZero.h>
#include <FlashStorage.h>
#include "WifiLocation.h"

// Fill these fields with your data
#define GOOGLE_API_KEY "YOURGOOGLEAPIKEY"
#define IFTTTKEY "YOURIFTTKEY"

#define SSID "WIFISSID"
#define PASS "WIFIPASS"


// Constants
#define DASHBUTTON 6 // Button
#define PINNEO     7 // Pin Neopixel
#define NUMPIXELS  3 // Num pixels
#define HOST "dash-button-arduino.firebaseio.com" // Host Firebase
#define HOSTIFTTT "maker.ifttt.com" // Host IFTTT to send tweet and email
#define TWITTEREVENT "tweet_santa" // Event name IFTTT Twitter
#define EMAILEVENT "email_santa" // Event name IFTTT email
#define SANTAEVENT "santa_santa" // Event name IFTTT Twitter Santa Claus

#define D_DAY 25 // Christmas Day
#define M_MONTH 12 // Christmas Month
#define LOC_PRECISSION 4 // Accuracy of location data to control privacy


// Neopixel
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PINNEO, NEO_GRB + NEO_KHZ800);

// Dash Button States
int dashButtonState = 0;
volatile bool makeUpdate = false;

// Reserve a portion of flash memory to store an "int" variable
// and call it "state_dashbutton".
FlashStorage(state_dashbutton, int);

// RTC with WiFi
RTCZero rtc;
const int GMT = 1;

int status = WL_IDLE_STATUS;
WiFiClient client;

// Info person
byte mac[6];
location_t location;

void setup() {
  // Init Neopixel
  pixels.begin();

  // Read the content of state_dashbutton
  dashButtonState = state_dashbutton.read();

  pixels.setPixelColor(0, pixels.Color(150, 0, 0));
  pixels.show();

  pixels.setPixelColor(1, pixels.Color(0, 0, 150));
  pixels.show();

  pixels.setPixelColor(2, pixels.Color(0, 150, 0));
  pixels.show();


  // WiFi configuration
  configWiFi();

  // Get location using WiFi networks around
  getLocation();

  // Update Dash Button state
  putRequest(dashButtonState);

  // Init Dash Button
  pinMode(DASHBUTTON, INPUT);

  // Init interrupt
  attachInterrupt(digitalPinToInterrupt(DASHBUTTON), dashbuttonAction, RISING);

  // RTC configuration
  configRTC();

  // Random seed
  randomSeed(analogRead(A0));

}

void loop() {

  // Off
  if (dashButtonState == 0)
  {
    for (int i = 0; i < NUMPIXELS; i++) {

      // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
      pixels.setPixelColor(i, pixels.Color(150, 0, 0));
      pixels.show();
    }
  }
  // Location sended but not Christmas day
  else if (dashButtonState == 1 && rtc.getDay() != D_DAY && rtc.getMonth() != M_MONTH)
  {
    for (int i = 0; i < NUMPIXELS; i++) {

      pixels.setPixelColor(i, pixels.Color(0, 0, 150));
      pixels.show();
    }
  }
  // Location sended and Christmas day
  else if (dashButtonState == 1 && rtc.getDay() == D_DAY && rtc.getMonth() == M_MONTH)
  {
    blinkNeopixel(pixels.Color(0, 0, 150));
  }
  // Shipping
  else if (dashButtonState == 2)
  {
    for (int i = 0; i < NUMPIXELS; i++) {

      pixels.setPixelColor(i, pixels.Color(0, 150, 0));
      pixels.show();
    }
  }

  // If need update
  if (makeUpdate)
  {
    // Change Dash Button State
    if (dashButtonState == 0)
    {
      pixels.setPixelColor(0, pixels.Color(150, 0, 0));
      pixels.show();

      pixels.setPixelColor(1, pixels.Color(0, 0, 150));
      pixels.show();

      pixels.setPixelColor(2, pixels.Color(0, 0, 150));
      pixels.show();
      
      if (putRequest(1))
      {
        // Send Email
        sendEmail();
        delay(2000);
        // Send Twitter
        sendToTwitterPerson();

        dashButtonState = 1;
      }
    }
    // Only Christmas Day
    else if (dashButtonState == 1 && rtc.getDay() == D_DAY && rtc.getMonth() == M_MONTH)
    {
      pixels.setPixelColor(0, pixels.Color(0, 0, 150));
      pixels.show();

      pixels.setPixelColor(1, pixels.Color(0, 150, 0));
      pixels.show();

      pixels.setPixelColor(2, pixels.Color(0, 150, 0));
      pixels.show();
      
      if (putRequest(2))
      {
        sendToTwitterSanta();
        dashButtonState = 2;
      }
    }

    // Change state in FlashStorage
    state_dashbutton.write(dashButtonState);
  }

  makeUpdate = false;
}

// Callback interruption
void dashbuttonAction()
{
  noInterrupts();
  if (!makeUpdate)
    makeUpdate = true;
  interrupts();
}

void blinkNeopixel(uint32_t c)
{
  for (int i = 0; i < NUMPIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color(0, 0, 0));
    pixels.show();
  }
  delay(500);

  for (int i = 0; i < NUMPIXELS; i++) {

    pixels.setPixelColor(i, c);
    pixels.show();
  }
  delay(500);
}

/**************** WiFi Connection ****************/
void configWiFi()
{
  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    // don't continue:
    while (true)
    {
      // Show error shield not present
      blinkNeopixel(pixels.Color(233, 149, 16));
    }
  }

  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(SSID, PASS);

    // wait 10 seconds for connection:
    delay(10000);
  }

  // Get mac
  WiFi.macAddress(mac);
}

/**************** RTCZero ****************/
void configRTC()
{
  // Init RTC
  rtc.begin();

  unsigned long epoch;
  int numberOfTries = 0, maxTries = 6;
  do {
    epoch = WiFi.getTime();
    numberOfTries++;
  }
  while ((epoch == 0) || (numberOfTries > maxTries));

  if (numberOfTries > maxTries) {
    while (1)
    {
      {
        // Show error RTC
        blinkNeopixel(pixels.Color(150, 0, 0));
      }
    }
  }
  else {
    rtc.setEpoch(epoch);

  }

}

/**************** HTTP PUT Request to Firebase ****************/
bool putRequest(int newDashButtonState)
{
  String keyMac = "";

  for (int i = 0; i < 6; i++)
  {
    String pos = String((uint8_t)mac[i], HEX);
    if (mac[i] <= 0xF)
      pos = "0" + pos;
    pos.toUpperCase();
    keyMac += pos;
    if (i < 5)
      keyMac += ":";
  }

  // close any connection before send a new request.
  client.stop();
  client.flush();

  // send SSL request
  if (client.connectSSL(HOST, 443)) {

    // PUT request
    String toSend = "PUT /persons/";
    toSend += keyMac;
    toSend += ".json HTTP/1.1\r\n";
    toSend += "Host:";
    toSend += HOST;
    toSend += "\r\n" ;
    toSend += "Content-Type: application/json\r\n";
    String payload = "{\"lat\":";
    payload += String(location.lat, LOC_PRECISSION);
    payload += ",";
    payload += "\"lon\":";
    payload += String(location.lon, LOC_PRECISSION);
    payload += ",";
    payload += "\"prec\":";
    payload += String(location.accuracy);
    payload += ",";
    payload += "\"status\": \"";
    payload += newDashButtonState;
    payload += "\"}";
    payload += "\r\n";
    toSend += "Content-Length: " + String(payload.length()) + "\r\n";
    toSend += "\r\n";
    toSend += payload;

    client.println(toSend);
    client.println();

    client.flush();
    client.stop();

    return true;
  } else {
    // if you couldn't make a connection:
    client.flush();
    client.stop();
    return false;
  }

}

/**************** Send to Twitter IFTTT ****************/
void sendToTwitterPerson()
{
  requestIFTTT(TWITTEREVENT);
}

void sendToTwitterSanta()
{
  requestIFTTT(SANTAEVENT);
}

/**************** Send email IFTTT ****************/
void sendEmail()
{
  requestIFTTT(EMAILEVENT);
}

/**************** Request IFTTT ****************/
void requestIFTTT(String eventName)
{
  for (int i = 0; i < 3; i++)
  {
    // close any connection before send a new request.
    if (client.connected())
    {
      client.stop();
    }

    client.flush();

    // Random request: from IFTTT Twitter publish Cannot send duplicate tweet.
    long randomRequest = random(1, 10000);

    // send SSL request
    if (client.connectSSL(HOSTIFTTT, 443)) {
      // Make a HTTP request:
      String toSend = "GET /trigger/";
      toSend += eventName;
      toSend += "/with/key/";
      toSend += IFTTTKEY;
      toSend += "?value1=";
      toSend += String(location.lat, LOC_PRECISSION);
      toSend += "&value2=";
      toSend += String(location.lon, LOC_PRECISSION);
      toSend += "&value3=";
      toSend += randomRequest;
      toSend += " HTTP/1.1\r\n";
      toSend += "Host: maker.ifttt.com\r\n";
      toSend += "Connection: close\r\n\r\n";
      client.print(toSend);
      break;
    } else {
      // if you couldn't make a connection:
    }
  }
  client.flush();
  client.stop();
}

/**************** Get Location info ****************/
bool getLocation() {

  WifiLocation gLocation(GOOGLE_API_KEY);

  location = gLocation.getGeoFromWiFi();

  return (location.accuracy < 100);

}

Tracking web

HTML
Is the web page to show de map.
<!DOCTYPE html>
<html>
<head>
	<title>Dash Button Tracking Santa Claus</title>
	<style type="text/css">
      html, body { height: 100%; margin: 0; padding: 0; }
      #map { height: 100%; }
    </style>
    <meta name="robots" content="noindex">
</head>
<body>
	<!--<ul id="costumers" class="list-group">
	</ul>-->

	<div id="map"></div>

	
	<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
	<!-- Latest compiled and minified Bootstrap -->
	<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
	<!-- Include Firebase Library -->
	<script src='https://cdn.firebase.com/js/client/2.2.1/firebase.js'></script>
	<!-- Tracking Store JavaScript -->
	<script src="script.js"></script>
	<!-- API Google Maps -->
	<script async defer
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyABRsKsE4ISjkikM96Hn7RIv9nW-oice8Q&callback=initMap">
    </script>
</body>
</html>

JavaScript

JavaScript
JavaScript to connect Firebase and show the map
// Create a firebase reference
var dbRef = new Firebase('https://dash-button-arduino.firebaseio.com/');
var costumersRef = dbRef.child('persons');
var markers = {}

//load persons
costumersRef.on("child_added", function(snap) {
  
    // Print to map
  addNewPerson(snap.val().lat,snap.val().lon,snap.val().status, snap.key());
});

//change persons
costumersRef.on("child_changed", function (snap) {
  changePerson(snap.val().lat, snap.val().lon, snap.val().status);
});

/******** GOOGLE MAPS *********/
var map;
function initMap() {
  // Center map
  var myLatLng = {lat: 38.392101, lng: -0.525467};

  map = new google.maps.Map(document.getElementById('map'), {
    center: myLatLng,
    zoom: 3
  });
}

function addNewPerson(lat, lon,status, key){
	console.log("status",status);
	var image
	if(status==0)
	{
  		image='images/santa-icon-1.png';
	}
	else if(status==1)
	{
  		image='images/santa-icon-2.png';
	}
	else if(status==2)
	{
  		image='images/santa-icon-3.png';
	}
  var marker = new google.maps.Marker({
    position: new google.maps.LatLng(lat,lon),
    icon: image,
    map: map,
    title: key // Tooltip = MAC address
  });
  markers[key] = marker;
}

function changePerson(lat, lon, status, key) {
    console.log("status", status);
    var image;
    if (status == 0) {
        image = 'images/santa-icon-1.png';
    }
    else if (status == 1) {
        image = 'images/santa-icon-2.png';
    }
    else if (status == 2) {
        image = 'images/santa-icon-3.png';
    }
    marker = markers[key];
    marker = new google.maps.Marker({
        position: new google.maps.LatLng(lat, lon),
        icon: image,
        map: map,
        title: key // Tooltip = MAC address
    });
    markers[key] = marker;
}

FlashStorage

The FlashStorage library aims to provide a convenient way to store and retrieve user's data using the non-volatile flash memory of microcontrollers.

RTCZero

The RTC library enables an Arduino Zero or MKR1000 board to take control of the internal RTC.

WiFi101 modified

Modification of the WiFi101 library to access the MACs of neighboring routers.

WifiLocation

This is a library that sends Google Maps Geolocaion API a request with a list of all WiFi AP's BSSID that your microcontroller listens to. Google, then answer with location and accuracy in JSON format.

Credits

Luis del Valle

Luis del Valle

2 projects • 4 followers
Germán Martín

Germán Martín

2 projects • 3 followers
Thanks to Adafruit.

Comments