Doorbell IRC bot

From Hackerspace ACKspace
Jump to: navigation, search
Project: Doorbell IRC bot
Featured: No
State Completed
Members Prodigity
GitHub No GitHub project defined. Add your project here.
Description A doorbell / IRC interface
Picture
No project picture! Fill in form Picture or Upload a jpeg here

Ah yes, caffeine induced hacking sessions seem to be quite effective...

I've hooked up the doorbell to one of the space arduino's, and used the ethernet shield (which was collecting dust..) to make an IRC doorbell bot which sends a message to the #ackspace irc channel.


V2.0

I updated the project to use the new MP3 doorbell;

Physical modifications to the doorbell were undesired so an alternative solution was devised.

Da_Syntax suggested using a ldr to detect the flashing lights on the doorbell as it rang.

New code:

#include <SPI.h>
#include <Ethernet.h>

EthernetClient client;

/* Configurations */
#define DEBUG
byte mac[] = { 0xFE, 0xAD, 0xDE, 0xAD, 0xBE, 0xEF };
const char server[] = "irc.libera.chat";
const int port = 6667;
const char nick[] = "Deurbel";
const char user[] = "Deurbel 8 * :Deurbel";
const char channel[] = "#ackspace";
const unsigned int ping_msec = 60000;
const int dingdelaymsec = 10000;
const char msg[] = "Another visitor... stay a while.. staaaayyy FOORREEVVEERRR!!!";

unsigned long time;
unsigned long dinged;
unsigned long pinged;

void setup() {
  Serial.begin(9600);
  
  Ethernet.begin(mac);
  delay(1000);
  
  Serial.print("Trying to make a connection with ");
  Serial.print(server);
  Serial.print(":");
  Serial.print(port);
  Serial.println(" ...");
  
  if ( client.connect(server, port) ) {
    Serial.println("Connected!");
    delay(5000);
    Serial.println("Logging in..");
    
    client.print("NICK ");
    client.println(nick);
    
    client.print("USER ");
    client.println(user);
    
    client.print("JOIN ");
    client.println(channel);
  } else {
    Serial.println("Connection failed.");
  }
  
  dinged = 0;
  pinged = 0;
}

void loop() {
  time = millis();
  
  int light = analogRead(0);

  #ifdef DEBUG
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }
  #endif

  if ( light > 200 && (time - dinged > dingdelaymsec || dinged == 0) ) {
    Serial.println("Ding dong!");
    client.print("PRIVMSG ");
    client.print(channel);
    client.print(" :");
    client.println(msg);
    dinged = time;
  }

  if ( time - pinged > ping_msec ) { // Might miss, need a different solution..
    Serial.println("Pinged server..");
    client.println("PING *");
    pinged = time;
  }

  if ( !client.connected() ) {
    Serial.println("Disconnected..");
    client.stop();
    for (;;)
      ;
  }
}

Here's what I did(old)

First I made a simple mental sketch of how everything was going to be hooked up:

The arduino is going to 'read' the doorbell, send data to the ethernet shield which is going to communicate with IRC directly. Furthermore the arduino is going to be provided with a usb cable for power and the ethernet shield is connected with a utp cable ofc.


Power

Soon I realized that the doorbell didn't actually need batteries anymore; the Arduino was being fed with USB power, so the doorbell can just as well leech from that right?

Just to be sure I wasn't going to set fire to anything I measured the current draw of the doorbell by placing the multimeter between one of the batteries and its terminal,

it was 20 mA (I think..) idle and 60 mA under load so there was no question I was going to power the doorbell with USB power and remove the batteries.

The batteries however were about 3.2 volts and the 3.3 volts from the arduino can only supply 50 mA which it might have been able to handle but I wasn't going to take the risk;

instead I opted to use 3 diodes in series with the arduino 5v which gives a voltage drop of about 2.1 (5 - 3 * 0.7 = 2.9).

I could have used 2 diodes but supplying a higher voltage to the doorbell (5 - 2 * 0.7 = 3.6) might have damaged it (Probably not but I rather stay on the safe side).

So yeah, no more batteries!


Interfacing

Now I needed to find a usable signal from the doorbell to present to the arduino.

I noticed there was a LED to signify ringing had took place but there were no wires connected to it (herp derp?)

I probed the board, couldn't find anything, got impatient and decided to use the wires running to the speaker.

First I tried it the 'hard' way:

Transistor to buffer signal with lowpass filter to get a pulse.

However I did not check the signals presented to the speaker properly and could therefore not get it to work.

Measuring the signal yielded something slightly surprising; the speaker wasn't hooked up to ground but to the positive power supply, and the audio signal was achieved by pulling the voltage over the speaker down..

Which led to another discovery; everytime the doorbell rang the multimeter measured a voltage drop (2.85v - 2.15v = 0.7v) over one of the speaker terminals to ground.

Then I did it the 'easy' way:

1: Solder cable to speaker

2: Connect cable to arduino

3: ?????

4: Profit!!

Seriously, that easy >.>


Programming

I used one of the ethernet library examples ("TelnetClient"), as my starting point.

I was quickly annoyed by the fact that it didn't use DHCP but static IP's and a quick search on arduino.cc gave me data I needed..

Except it didn't work... :'(

Soon after the realisation came I was still working with a arduino environment predating the birth of jesus christ.. woops!

One download further and some debugging I found out the ethernet library supports DNS <3

Now I only needed the arduino to connect to chat.freenode.com irc.libera.chat on port 6667, set a nick and user name and join the ackspace channel (and ping every once in a while to let the server know we're still in bussiness).

The first problem I encountered was trying to figure out what to do with the data the server sent me, can't just send stuff before the server acknowledges me right?

So how to interpret the data from the server? NOT!

Delays saved the day, the arduino tells me when the shield is connected and I just add a few seconds of delay after that before sending my user / nick / channel.

The second problem I encountered was pinging the server, the server expects "PING $servername" but the name changes after every connect :S

Then, suddenly as if a voice from the heavens spoke to me; "Try 'PING *', it might work" AND IT DID omgwtfbbq. (thank you deus ex machina).

And now we have an doorbellduinobot (DBDB, quite a sexy abbreviation if I might say so..)


Code

/*
 ACKspace Doorbell IRC bot
 
 Thank you 'Tom Igoe' for the telnet example.
*/
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

unsigned long counter = 0;
int speaker = 0;
EthernetClient client;;

void setup() {

  if (Ethernet.begin(mac) == 0) {
    Serial.println("DHCP Fail :(");
    for(;;)
      ; // Loop, because shit failed and we don't know what to do with ourselfs :@
  }
  
  Serial.begin(9600);
  delay(1000);    // Allow the ethernet shield some booting time.
  Serial.println("connecting...");

  if (client.connect("irc.libera.chat", 6667)) {
    Serial.println("connected");
    delay(5000);                     // Calculated with high level maths
    Serial.println("NICK Deurbel");
    client.println("NICK Deurbel");
    delay(1000);                     // More high level maths
    Serial.println("USER Deurbel 8 * :Deurbel");
    client.println("USER Deurbel 8 * :Deurbel");
    delay(1000);                     // Calculated by NASA
    Serial.println("JOIN #ackspace");
    client.println("JOIN #ackspace");
  } 
  else {
    Serial.println("connection failed");
  }
}

void loop()
{
  counter++;
  speaker = analogRead(3);
  
  if (speaker < 512) {
    // Ding dong motherfuckers, ding dong!
    Serial.println("Ding dong!");
    client.println("PRIVMSG #ackspace :Ding dong!");
    delay(10000);
  }
    
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }

  while (Serial.available() > 0) {
    char inChar = Serial.read();
    if (client.connected()) {
      client.print(inChar); 
    }
  }

  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
    // do nothing:
    while(true);
  }

  if (counter == 650000) {
    Serial.println("PING *");
    client.println("PING *");
    counter = 0;
  }
}