Smart Doorbell sends video via email and a push notification

Just put my first ever spark project up on hackster.io. There’s some 3D printed bits and some code to make it work!

http://www.hackster.io/hootie81/spark-doorbell

Its a doorbell that sends an email using Pushingbox and also triggers an XBMC script to show a security camera on my TV.

With Pushingbox you could send a tweet or have your Karotz tell you something…

10 Likes

Very nice project @Hootie81 :smile:

I’m working on a Smart Doorbell of my own, and also I have another Doorbell project that incorporates the Pebble watch that I need to get up on Hackster.io very soon.

1 Like

I cant wait for you to share the code… well the pebble parts anyway! i dont know where to begin with the compiling and then getting it on the phone and pebble and stuff.

I can cut and paste from forums and push a little lightning bolt spark style… but after that it requires more of my limited brainpower

1 Like

RTTTL tunes now added… so its now a doorbell that makes a noise… cant believe i didn’t have that at the start

  • a bug fix, tries to connect 5 times before giving up, because i noticed if it had been sitting idle for a while it wouldn’t connect first time round.

New code for this old project, makes it a bit more reliable and easy to set up. Core code is pretty straight forward and there are a few options you can choose from.

If you run XBMC you can have a pop-up show on screen - just set the XBMC to true. load this script into XBMC and set up your camera details

The PHP code connects to pushover to send a push notification and connects to the Foscam camera to take a gif image and send it to an email, apparently DigitalOcean is perfect for hosting the PHP script and files, good little tutorial here, although i run my own server at home purely because i can :slight_smile: There are some dependencies required like the swiftmailer library and some files from this Foscam forum post. and the gifEncoder (which from memory requires a fix)

Webhook is there if you are on the beta test… basically its just a simple publish

Pushing box is a simple setup alternative to the full blown PHP script on a server… visit pushingbox for more info

#include "application.h"
boolean DEBUG = false; // enable or disable Debug

// XBMC Setup
boolean XBMC = true; //true = on or false = turn it off
char xbmcServer[] = "somewhere.com"; // XBMC Server
//IPAddress xbmcServer(192, 168, 0, 6);  //Or use a IPAddress 
int xbmcPort = 80; // XBMC Port
char xbmcScript[] = "/jsonrpc?request={%22jsonrpc%22:%222.0%22,%22method%22:%22Addons.ExecuteAddon%22,%22params%22:{%22addonid%22:%22script.securitycam%22},%22id%22:%221%22}}"; // Script to run
char xbmcLogon[] = "Username:Password"; // Username:Password base64 encoded

// PHP setup
boolean PHP = true;  //true = on or false = turn it off
char phpServer[] = "somewhere.com";
//IPAddress phpServer(192, 168, 0, 5);  //Or use a IPAddress 
int phpPort = 80;
char phpScript[] = "/Doorbell/doorbell.php";
char sparkUID[] = "12345abcde"; //any hex number (to keep webhook compatability)
char eventName[] = "Doorbell";

// Webhook setup
boolean WEBHOOK = false; //true = on or false = turn it off
char webhookName[] = "Doorbell"; //the event the webhook listens for

// Pushing Box Setup
boolean PB = false;  //true = on or false = turn it off
char serverName[] = "api.pushingbox.com"; // Pushingbox Server
char deviceID[] = "/pushingbox?devid=YOURDEVIDHERE"; // Get request 

// Sound setup
boolean TUNE = true;
// Select a Tune from below

//char *song = (char *)"The Simpsons:d=4,o=5,b=168:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p,8f#,8f#,8f#,8g,a#.,8c6,8c6,8c6,c6";
//char *song = (char *)"Indiana:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6";
//char *song = (char *)"TakeOnMe:d=4,o=4,b=160:8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5,8f#5,8e5,8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5";
//char *song = (char *)"Entertainer:d=4,o=5,b=140:8d,8d#,8e,c6,8e,c6,8e,2c.6,8c6,8d6,8d#6,8e6,8c6,8d6,e6,8b,d6,2c6,p,8d,8d#,8e,c6,8e,c6,8e,2c.6,8p,8a,8g,8f#,8a,8c6,e6,8d6,8c6,8a,2d6";
//char *song = (char *)"Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c";
//char *song = (char *)"Xfiles:d=4,o=5,b=125:e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,g6,f#6,e6,d6,e6,2b.,1p,g6,f#6,e6,d6,f#6,2b.,1p,e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,e6,2b.";
//char *song = (char *)"Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f";
//char *song = (char *)"20thCenFox:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p,8e,8g#,8b,1c#6,8f#,8a,8c#6,1e6,8a,8c#6,8e6,1e6,8b,8g#,8a,2b";
//char *song = (char *)"Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6";
//char *song = (char *)"MASH:d=8,o=5,b=140:4a,4g,f#,g,p,f#,p,g,p,f#,p,2e.,p,f#,e,4f#,e,f#,p,e,p,4d.,p,f#,4e,d,e,p,d,p,e,p,d,p,2c#.,p,d,c#,4d,c#,d,p,e,p,4f#,p,a,p,4b,a,b,p,a,p,b,p,2a.,4p,a,b,a,4b,a,b,p,2a.,a,4f#,a,b,p,d6,p,4e.6,d6,b,p,a,p,2b";
//char *song = (char *)"StarWars:d=4,o=5,b=45:32p,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#.6,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#6";
//char *song = (char *)"GoodBad:d=4,o=5,b=56:32p,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,d#,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,c#6,32a#,32d#6,32a#,32d#6,8a#.,16f#.,32f.,32d#.,c#,32a#,32d#6,32a#,32d#6,8a#.,16g#.,d#";
//char *song = (char *)"TopGun:d=4,o=4,b=31:32p,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,16f,d#,16c#,16g#,16g#,32f#,32f,32f#,32f,16d#,16d#,32c#,32d#,16f,32d#,32f,16f#,32f,32c#,g#";
//char *song = (char *)"A-Team:d=8,o=5,b=125:4d#6,a#,2d#6,16p,g#,4a#,4d#.,p,16g,16a#,d#6,a#,f6,2d#6,16p,c#.6,16c6,16a#,g#.,2a#";
//char *song = (char *)"Flinstones:d=4,o=5,b=40:32p,16f6,16a#,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,d6,16f6,16a#.,16a#6,32g6,16f6,16a#.,32f6,32f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,a#,16a6,16d.6,16a#6,32a6,32a6,32g6,32f#6,32a6,8g6,16g6,16c.6,32a6,32a6,32g6,32g6,32f6,32e6,32g6,8f6,16f6,16a#.,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#,16c.6,32d6,32d#6,32f6,16a#6,16c7,8a#.6";
//char *song = (char *)"Jeopardy:d=4,o=6,b=125:c,f,c,f5,c,f,2c,c,f,c,f,a.,8g,8f,8e,8d,8c#,c,f,c,f5,c,f,2c,f.,8d,c,a#5,a5,g5,f5,p,d#,g#,d#,g#5,d#,g#,2d#,d#,g#,d#,g#,c.7,8a#,8g#,8g,8f,8e,d#,g#,d#,g#5,d#,g#,2d#,g#.,8f,d#,c#,c,p,a#5,p,g#.5,d#,g#";
//char *song = (char *)"Gadget:d=16,o=5,b=50:32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,32d#,32f,32f#,32g#,a#,d#6,4d6,32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,8d#";
//char *song = (char *)"Smurfs:d=32,o=5,b=200:4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8f#,p,8a#,p,4g#,4p,g#,p,a#,p,b,p,c6,p,4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8b,p,8f,p,4f#";
//char *song = (char *)"MahnaMahna:d=16,o=6,b=125:c#,c.,b5,8a#.5,8f.,4g#,a#,g.,4d#,8p,c#,c.,b5,8a#.5,8f.,g#.,8a#.,4g,8p,c#,c.,b5,8a#.5,8f.,4g#,f,g.,8d#.,f,g.,8d#.,f,8g,8d#.,f,8g,d#,8c,a#5,8d#.,8d#.,4d#,8d#.";
//char *song = (char *)"LeisureSuit:d=16,o=6,b=56:f.5,f#.5,g.5,g#5,32a#5,f5,g#.5,a#.5,32f5,g#5,32a#5,g#5,8c#.,a#5,32c#,a5,a#.5,c#.,32a5,a#5,32c#,d#,8e,c#.,f.,f.,f.,f.,f,32e,d#,8d,a#.5,e,32f,e,32f,c#,d#.,c#";
//char *song = (char *)"MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d";
//char *song = (char *)"SMBtheme:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6";
//char *song = (char *)"SMBunderground:d=16,o=6,b=100:c,c5,a5,a,a#5,a#,2p,8p,c,c5,a5,a,a#5,a#,2p,8p,f5,f,d5,d,d#5,d#,2p,8p,f5,f,d5,d,d#5,d#,2p,32d#,d,32c#,c,p,d#,p,d,p,g#5,p,g5,p,c#,p,32c,f#,32f,32e,a#,32a,g#,32p,d#,b5,32p,a#5,32p,a5,g#5";
//char *song = (char *)"SMBwater:d=8,o=6,b=225:4d5,4e5,4f#5,4g5,4a5,4a#5,b5,b5,b5,p,b5,p,2b5,p,g5,2e.,2d#.,2e.,p,g5,a5,b5,c,d,2e.,2d#,4f,2e.,2p,p,g5,2d.,2c#.,2d.,p,g5,a5,b5,c,c#,2d.,2g5,4f,2e.,2p,p,g5,2g.,2g.,2g.,4g,4a,p,g,2f.,2f.,2f.,4f,4g,p,f,2e.,4a5,4b5,4f,e,e,4e.,b5,2c.";
//char *song = (char *)"SMBdeath:d=4,o=5,b=90:32c6,32c6,32c6,8p,16b,16f6,16p,16f6,16f.6,16e.6,16d6,16c6,16p,16e,16p,16c";
//char *song = (char *)"RickRoll:d=4,o=5,b=200:8g,8a,8c6,8a,e6,8p,e6,8p,d6.,p,8p,8g,8a,8c6,8a,d6,8p,d6,8p,c6,8b,a.,8g,8a,8c6,8a,2c6,d6,b,a,g.,8p,g,2d6,2c6.,p,8g,8a,8c6,8a,e6,8p,e6,8p,d6.,p,8p,8g,8a,8c6,8a,2g6,b,c6.,8b,a,8g,8a,8c6,8a,2c6,d6,b,a,g.,8p,g,2d6,2c6.";
//char *song = (char *)"2.34kHzBeeps:d=4,o=7,b=240:d,p,d,p,d,p,d,p";
char *song = (char *)"Satellit:d=8,o=6,b=112:g,e,16f,16e,d,c,g,c7,c";

// Input and Output Pins 

int16_t tonePin = D6; //Doorbell Sound Output 
int doorbell = D0; // Doorbell button connects to D0 on the core
int LED = D7; // Built in LED

/*-----------------------------------------------------*/
//    no need to change anything below here
/*-----------------------------------------------------*/


String myID = Spark.deviceID(); //store the device ID - its used in the PHP script 
char coreID[25];//as 1 char space for null is also required

TCPClient client;
 
char reply[512]; //may be used in the future to parse response

// RTTTL setup
int notes[] =
{0, 3817,3597,3401,3205,3030,2857,2703,2551,2404,2273,2146,2024,
1908,1805,1701,1608,1515,1433,1351,1276,1205,1136,1073,1012,
956,903,852,804,759,716,676,638,602,568,536,506,
478,451,426,402,379,358,338,319,301,284,268,253,
239,226,213,201,190,179,169,159,151,142,134,127};
#define OCTAVE_OFFSET 0 
boolean playing = false;
byte default_dur = 4;
byte default_oct = 6;
byte lowest_oct = 3;
int bpm = 63;
int num;
long wholenote;
long duration;
byte note;
byte scale;
bool songDone = false;
char *songPtr;

unsigned long Lockout; // used to stop flooding servers

void setup() {
    myID.toCharArray(coreID, 25); //convert the string to a char array
    coreID[24] = '\0'; //check its terminated properly
    pinMode(tonePin,OUTPUT);
    pinMode(doorbell, INPUT_PULLUP); // other side of doorbell will connect to ground to pull low
    Lockout = millis();
    pinMode(LED, OUTPUT); // sets LED as output
    if(DEBUG){
        digitalWrite(LED,HIGH); // Light the onboard LED so you know it's time to open your Serial Terminal
        Serial.begin(9600);
        while(!Serial.available()); // wait here for user to press ENTER in Serial Terminal
        digitalWrite(LED,LOW);
        Serial.print("SSID: ");
        Serial.println(WiFi.SSID());
        Serial.print("Core IP: ");
        Serial.println(WiFi.localIP());
        Serial.print("Gateway: ");
        Serial.println(WiFi.gatewayIP());
        Serial.print("Mask: ");
        Serial.println(WiFi.subnetMask());
        Serial.print("WiFi RSSI: ");
        Serial.println(WiFi.RSSI());
        Serial.print("CoreID: ");
        Serial.println(coreID);
    }
}

void loop() {
    if (millis() >= Lockout){  // check how long its been since last pushed
        if(digitalRead(doorbell) == LOW) {  //read the doorbell button
            delay(50);
            if(digitalRead(doorbell) == LOW) {  // debounce check again to make sure 
                Lockout = millis() + 15000; // set up the lockout period 15 seconds before it will run again
                RGB.control(true);
                RGB.color(0,255,0);
                if(WEBHOOK) Spark.publish(webhookName, NULL, 60, PRIVATE); //generally fast enough not to cause a delay before sound 
                if(TUNE) playSong(); // plays the chosen song
                SPARK_WLAN_Loop();
                if(XBMC) runXbmcScript(xbmcServer, xbmcPort, xbmcScript, xbmcLogon);  // opens up a script in xbmc that displays an ip camera 
                if(PHP) postDoorbell(phpServer, phpPort, phpScript, sparkUID, coreID, eventName);
                if(PB) sendToPushingBox(serverName, 80, deviceID); // sends push notification to mobile phone or sends email using pushingbox
                RGB.control(false);
            }
        }
    }
}

void out(const char *s) {
client.write( (const uint8_t*)s, strlen(s) );
if (DEBUG)Serial.write( (const uint8_t*)s, strlen(s) );
}

void in(char *ptr, uint8_t timeout) {
        int pos = 0;
        unsigned long lastTime = millis();
        
        while( client.available()==0 && millis()-lastTime<timeout) { //timeout
        }  //do nothing
        lastTime = millis();
        unsigned long lastdata = millis();
        
        while ( client.available() || (millis()-lastdata < 500)) {  //500 millisecond timeout
            if (client.available()) {
                char c = client.read();
                if(DEBUG)Serial.print(c);
                lastdata = millis();
                ptr[pos] = c;
                pos++;
            }
            if (pos >= 512 - 1)
            break;
        }
        ptr[pos] = '\0'; //end the char array
        
        client.flush();  //for safety
        delay(400);
        client.stop();
        if(DEBUG){
            Serial.println();
            Serial.print("Done, Total bytes: ");
            Serial.println(pos);
        }
        
}

void runXbmcScript(char *hostname, int port, char *url, char *xbmclogon) {
        
    char line[255];
    client.stop();
    if(DEBUG){Serial.print("connecting... ");}
    if (client.connect(hostname, port)) {
        if(DEBUG){
            Serial.print("connected to ");
            Serial.println(hostname);
        }
        delay(500);
        digitalWrite(LED, LOW);
        sprintf(line, "GET %s HTTP/1.1\r\n", url);
        out(line);

        sprintf(line, "Host: %s:%d\r\n", hostname, port);
        out(line);

        sprintf(line, "Authorization: Basic %s\r\n", xbmclogon);
        out(line);
        
        out("Connection: close\r\n\r\n");
        
        in(reply, 3000);
        
     }
    else {
        client.flush();
        client.stop();
        if(DEBUG)Serial.println("Could not connect");
    }
}
void runXbmcScript(IPAddress hostname, int port, char *url, char *xbmclogon) { //overload for IPAddress
        
    char line[255];
    client.stop();
    if(DEBUG){Serial.print("connecting... ");}
    if (client.connect(hostname, port)) {
        if(DEBUG){
            Serial.print("connected to ");
            Serial.println(hostname);
        }
        delay(500);
        digitalWrite(LED, LOW);
        sprintf(line, "GET %s HTTP/1.1\r\n", url);
        out(line);

        sprintf(line, "Host: %d.%d.%d.%d:%d\r\n", hostname[0], hostname[1],hostname[2],hostname[3], port);
        out(line);

        sprintf(line, "Authorization: Basic %s\r\n", xbmclogon);
        out(line);
        
        out("Connection: close\r\n\r\n");
        
        in(reply, 3000);
        
     }
    else {
        client.flush();
        client.stop();
        if(DEBUG)Serial.println("Could not connect");
    }
}
void postDoorbell(char *hostname, int port, char *script, char *uid, char *cid, char *event) {

    char line[255];
    client.stop();
    
    if(DEBUG){Serial.print("connecting... ");}
    if (client.connect(hostname, port)) {
        if(DEBUG){
            Serial.print("connected to ");
            Serial.println(hostname);
        }
        delay(500);
        digitalWrite(LED, LOW);
        sprintf(line, "POST %s HTTP/1.1\r\n", script);
        out(line);
        out("Connection: close\r\n");
        sprintf(line, "Host: %s:%d\r\n", hostname, port);
        out(line);
        sprintf(line, "Content-Length: %d\r\n", strlen(uid) + strlen(cid) + strlen(event) + 15);
        out(line);
        out("Content-Type: application/x-www-form-urlencoded\r\n");
        out("\r\n"); //end of headers
        client.flush(); //hope to fix a hard fault
        sprintf(line, "event=%s/%s&coreid=%s\r\n", uid, event, cid);
        out(line);
        in(reply, 20000);
     }
    else {
        client.flush();
        client.stop();
        if(DEBUG)Serial.println("Could not connect");
    }
        
}

void postDoorbell(IPAddress hostname, int port, char *script, char *uid, char *cid, char *event) {

    char line[255];
    client.stop();
    
    if(DEBUG){Serial.print("connecting... ");}
    if (client.connect(hostname, port)) {
        if(DEBUG){
            Serial.print("connected to ");
            Serial.println(hostname);
        }
        delay(500);
        digitalWrite(LED, LOW);
        sprintf(line, "POST %s HTTP/1.1\r\n", script);
        out(line);
        out("Connection: close\r\n");
        sprintf(line, "Host: %d.%d.%d.%d:%d\r\n", hostname[0], hostname[1],hostname[2],hostname[3], port);
        out(line);
        sprintf(line, "Content-Length: %d\r\n", strlen(uid) + strlen(cid) + strlen(event) + 15);
        out(line);
        out("Content-Type: application/x-www-form-urlencoded\r\n");
        out("\r\n"); //end of headers
        client.flush(); //fixed a hard fault
        sprintf(line, "event=%s/%s&coreid=%s\r\n", uid, event, cid);
        out(line);
        in(reply, 20000);
     }
    else {
        client.flush();
        client.stop();
        if(DEBUG)Serial.println("Could not connect");
    }
        
}
void sendToPushingBox(char *hostname, int port, char *devid) {

    char line[255];
    client.stop();
    
    if(DEBUG){Serial.print("connecting... ");}
    if (client.connect(hostname, port)) {
        if(DEBUG){
            Serial.print("connected to ");
            Serial.println(hostname);
        }
        delay(500);
        digitalWrite(LED, LOW);
        sprintf(line, "GET %s HTTP/1.1\r\n", devid);
        out(line);

        sprintf(line, "Host: %s:%d\r\n", hostname, port);
        out(line);
        out("Connection: close\r\n");
        out("User-Agent: Spark\r\n\r\n");
        in(reply, 5000);
     }
    else {
        client.flush();
        client.stop();
        if(DEBUG)Serial.println("Could not connect");
    }  
}

void playSong(){
    playing = true;
    while(playing) {
        if(!songDone) { // Start song
            songDone = true;
            begin_rtttl(song);
        }
        if(!next_rtttl()) { // Play next note
            songDone = false;
            playing = false;
            delay(2000);
        }
               
    }
}

void begin_rtttl(char *p)
{
  while(*p != ':') p++; // ignore name
  p++; // skip ':'
  if(*p == 'd')
  {
    p++; p++; // skip "d="
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    if(num > 0) default_dur = num;
    p++; // skip comma
  }
  // get default octave
  if(*p == 'o')
  {
    p++; p++; // skip "o="
    num = *p++ - '0';
    if(num >= 3 && num <=7) default_oct = num;
    p++; // skip comma
  }
  // get BPM
  if(*p == 'b')
  {
    p++; p++; // skip "b="
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    bpm = num;
    p++; // skip colon
  }
  // BPM usually expresses the number of quarter notes per minute
  wholenote = (60 * 1000L / bpm) * 2; // this is the time for whole note (in milliseconds)
  // Save current song pointer...
  songPtr = p;
}

bool next_rtttl() {

  char *p = songPtr;
  // if notes remain, play next note
  if(*p)
  {
    // first, get note duration, if available
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    
    if(num) duration = wholenote / num;
    else duration = wholenote / default_dur; // we will need to check if we are a dotted note after

    // now get the note
    note = 0;

    switch(*p)
    {
      case 'c':
        note = 1;
        break;
      case 'd':
        note = 3;
        break;
      case 'e':
        note = 5;
        break;
      case 'f':
        note = 6;
        break;
      case 'g':
        note = 8;
        break;
      case 'a':
        note = 10;
        break;
      case 'b':
        note = 12;
        break;
      case 'p':
      default:
        note = 0;
    }
    p++;
    if(*p == '#') // now, get optional '#' sharp
    {
      note++;
      p++;
    }
    if(*p == '.') // now, get optional '.' dotted note
    {
      duration += duration/2;
      p++;
    }
    if(isdigit(*p)) // now, get scale
    {
      scale = *p - '0';
      p++;
    }
    else {
      scale = default_oct;
    }
    scale += OCTAVE_OFFSET;
    if(*p == ',')
      p++; // skip comma for next note (or we may be at the end)
    songPtr = p; // Save current song pointer...
    if(note){ // now play the note
      tone(tonePin, notes[(scale - lowest_oct) * 12 + note], duration);
    }
    else {
      delay(duration);
    }
    return 1; // note played successfully.
  }
  else {
    return 0; // all done
  }
}

void tone(int pin, int16_t note, int16_t duration) {
  for(int16_t x=0;x<(duration*1000/note);x++) {
    PIN_MAP[pin].gpio_peripheral->BSRR = PIN_MAP[pin].gpio_pin; // HIGH
    delayMicroseconds(note);
    PIN_MAP[pin].gpio_peripheral->BRR = PIN_MAP[pin].gpio_pin; // LOW
    delayMicroseconds(note);
  }
}

PHP code

<?php

  
//error_reporting(E_ALL);                // Comment out after debugging. Helps isolate any errors in your error.log for initial setup.
//ini_set('display_errors', '1');        // Comment out after debugging. Helps isolate any errors in your error.log for initial setup.

// E-mail Setup
$smtp = 'smtp.live.com';                //smtp mail server address
$port = 587;                            //Port number
$encryption = 'tls';                    //Encryption
$username = "Send-from-email@hotmail.com";    //Username
$password = "Password";            //Password
$from = array('Send-from-email@hotmail.com' => 'Doorbell');        //From Address, must be set! Hint Doorbell will be the displayed name.

$recipient = array(                        //Send emails to..  
'recipient1email@somewhere.com' => 'Recipient 1 Name' 
, 'recipient2email@somewhere.com' => 'Recipient 2 Name'    //disable for testing
);        
$subject = "Doorbell";                    //Subject
$body = "Someone is at the door";        //Body

// Pushover Stuff
$apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; //api key
$userKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // user key
$pushMessage = "Someone is at the Door";
$pushSound = "Knock";

// Spark core stuff
$spark_id = '55ffxxxxxxxxxxxxxxxxxxxxxxx'; // coreID of the doorbell
$eventname = 'Doorbell';                // This is the name of the event triggered by the webhook

// Camera setup
$ipcam_url = "http://192.168.0.8:8080/snapshot.cgi?user=YOURUSERNAME&pwd=YOURPASSWORD&cnt=".microtime(true);  //visit your cameras manufacturer for details. This is a pretty standard foscam url.

// Local stuff
$nocam = "NoCam.jpg";                    // Image to show when camera unavailable
$jpeg = "photo.jpg";                    // Filename of photo to save
$gif = "photo.gif";                       // Filename of animation
$number_of_photos = 5;                   // Number of pictures to send
$framerate = 1;                           // Frame rate frames/second for output (photos are taken as fast as possible about 5 sec apart)
$font_name = "arial.ttf";              // This file MUST be present in this same folder if date or any text is used in the image.
$show_date_time = true;                // If true date will be displayed in image.
$time_offset = -0;                     // Your web server maybe in a different time zone. If so to display your time. +-3600 for each hour difference.
$date_format = "d/m/Y H:i:s";          // Date format can be changed to European format "d/m/Y H:i:s";
$date_font_size = 8;                   // Date Text Font size. Higher number equals larger text.
$text_after_date = "Doorbell Camera";  // Text following Date. If "$show_date_time = false" then text will display without Date unless = "";
$date_x = 10;                          // Date text x position on image. Will need to be set based on image size.
$date_y = 14;                          // Date text y position on image. Will need to be set based on image size.


// ********************************************
// * End of settings, nothing below to change *
// ********************************************


// Security check for correct core and Event Name
if (isset($_POST['event']) && isset($_POST['coreid'])) { 

    //split the event into spark user id and event name
    list($sparkuid, $event) = explode( '/', ($_POST['event']), 2); 
    if (!ctype_xdigit($sparkuid)) { 
        $file = 'connect.txt';
        file_put_contents($file, 'sparkuid not hex ' , FILE_APPEND);
        die(); } //check spark user id is hex characters
    if ($event != $eventname) { 
        $file = 'connect.txt';
        file_put_contents($file, 'event name mismatch ' , FILE_APPEND);
        die(); } //check spark user id is hex characters
    $coreid = $_POST['coreid'];
    if (!ctype_xdigit($coreid)) { 
        $file = 'connect.txt';
        file_put_contents($file, 'coreid not hex' , FILE_APPEND);
        die(); } //check spark user id is hex characters//check spark core id is hex characters
    if ($coreid != $spark_id) { $file = 'connect.txt';
        file_put_contents($file, 'coreid mismatch' , FILE_APPEND);
        die(); } //check spark user id is hex characters //must be the right core
    
} else { die(); } // Hint: comment out just before the else to disable the checks for testing 

// first up send the push message we want it quickly
include('Pushover.php');
$push = new Pushover();
$push->setToken($apiKey);
$push->setUser($userKey);
$push->setMessage($pushMessage);
$push->setTimestamp(time());
$push->setSound($pushSound);
$go = $push->send();

set_time_limit(30); //reset PHP timeout as taking photos takes ages

require_once '/swift/lib/swift_required.php';      // Swiftmailer library
include('GIFEncoder.class.php');            // Gif Encoder

$font = realpath($font_name);
$no_image = realpath($nocam);

if ($framerate > 2){
    $framerate = 2;                             // Set a max of 2 frames per sec
}
$framerate = (100 / $framerate);                  // Change it to delay time 

if (empty($no_image)) {
     print "No photo available image file is missing from disk";
     die();
}
if ((($show_date_time) || (!empty($text_after_date))) && (empty($font))) {
      print "Font file is missing from disk";
      die();
} 

$images = (array)null;
$frames = (array)null;
if ($number_of_photos > 1) {
for($i = 0; $i < $number_of_photos; $i++) {

$my_image = @imagecreatefromjpeg($ipcam_url); //get the pic from the cam, remove the @ to output debug info
$time_now = time();
if ($my_image !== FALSE) {
    if (($show_date_time) || (!empty($text_after_date))) {
        $white = imagecolorallocate($my_image, 255, 255, 255);
        $grey = imagecolorallocate($my_image, 128, 128, 128);
        $black = imagecolorallocate($my_image, 0, 0, 0);
        if (($show_date_time) || (!empty($text_after_date))) {
            if ($show_date_time)
                $date_text = date($date_format, ($time_now + $time_offset))." ".$text_after_date;
            else
                $date_text = $text_after_date;
            imagettftext($my_image, $date_font_size, 0, $date_x + 1, $date_y + 1, $grey, $font, $date_text);
            imagettftext($my_image, $date_font_size, 0, $date_x, $date_y, $white, $font, $date_text);
       }
    }
} else
    $my_image = file_get_contents($no_image); 
 
ob_start();
imagegif($my_image);
$images[] = ob_get_contents();
$frames[] = $framerate; //delay
ob_end_clean();
imagedestroy($my_image);

usleep(($framerate * 10));
}

$gifanimation = new GIFEncoder($images, $frames, 0, 2, 0, 0, 0, 'bin');
//header ('Content-type:image/gif');  // uncomment these 2 lines to have the gif display in your browser
//echo $gifanimation->GetAnimation();
$fp = fopen($gif, 'w');
fwrite($fp, $gifanimation->GetAnimation());
fclose($fp);

// Create the attachment with your data
$attachment = Swift_Attachment::fromPath($gif);

} else {

$my_image = @imagecreatefromjpeg($ipcam_url); //get the pic from the cam, remove the @ to output debug info

if ($my_image !== FALSE) {
    if (($show_date_time) || (!empty($text_after_date))) {
        $white = imagecolorallocate($my_image, 255, 255, 255);
        $grey = imagecolorallocate($my_image, 128, 128, 128);
        $black = imagecolorallocate($my_image, 0, 0, 0);
        if (($show_date_time) || (!empty($text_after_date))) {
            if ($show_date_time)
                $date_text = date($date_format, ($time_now + $time_offset))." ".$text_after_date;
            else
                $date_text = $text_after_date;
            imagettftext($my_image, $date_font_size, 0, $date_x + 1, $date_y + 1, $grey, $font, $date_text);
            imagettftext($my_image, $date_font_size, 0, $date_x, $date_y, $white, $font, $date_text);
       }
    }
} else
    $my_image = file_get_contents($no_image); 


imagejpeg($my_image, $jpeg);  //save it to disk
$attachment = Swift_Attachment::fromPath($jpeg);
}

set_time_limit(30); //reset PHP timeout as taking phots takes ages

// Create the Transport
$transport = Swift_SmtpTransport::newInstance($smtp, $port, $encryption)
  ->setUsername($username)
  ->setPassword($password)
  ;
// Create the Mailer using your created Transport
$mailer = Swift_Mailer::newInstance($transport);

// Create the message
$message = Swift_Message::newInstance()
  ->setFrom($from)
  ->setTo($recipient)
  ->setSubject($subject)
  ->setBody($body)
  ;
// Attach the image to the message
$message->attach($attachment ->setDisposition('inline'));

if ($mailer->send($message))
{
  echo "Sent\n";
}
else
{
  echo "Failed\n";
}

?>