TOP

April 1st on the Diyode IRC

So, probably like many hackerspaces, Diyode spends a lot of time on our IRC channel. We also have a good few members who compulsively check the webcam whenever someone enters the shop. So when April first rolled around on a Monday this year, I figured it was time to put the CodeShield to work. I present: the IRC triggered, arduino controlled, floating head! This is the feed from the classroom webcam:

The face rising is triggered by a specific phrase on the Diyode IRC channel. We have a server in the shop that runs our access control system, including a bot that hangs out on the IRC channel so that it can post workshop events to the IRC, and collect to-do items and meeting topics from the chat room. The bot can also speak Text-to-Speech phrases with an IRC command.

The prank was to attract attention to the Diyode webcam on IRC (“Hey, someone just entered the shop without the doorbot noticing”), and then trigger a face to appear right in front of the cam while everyone was watching. For the face, I chose the disturbingly brilliant Jackson Blankenship. A specific phrase spoken on the IRC channel would trigger IRC python script on the linux box to send a message over USB to the arduino above the camera. Then the servo on the codeshield would make the face rise slowly in front of the webcam, then drop back down, freaking the crap out of anyone watching.

The Setup

prank

 

The photo above shows the arduino/codeshield on a wooden block above the webcam. Three laser-cut arms control the face rising (I tweaked the design a bit after I took the photo).

Here’s the .ai file for laser cutting the arms. Particularly useful for the cut-out of the servo setting, if you’re doing something similar with sg90 servos.

The Code:

The Access Control box is running linux, with the responsibilities for the the IRC being handling by a python script. Some bits, obviously, have been changed for security reasons…

#!/usr/bin/python

import sys 
import socket 
import string 
import os
import time
import conf
import datetime
import httplib
import urllib2
import MySQLdb
import calendar

import serial
from serial.tools import list_ports

if conf.platform == 'linux':  
  from festival import say

HOST='irc.freenode.net' #The server we want to connect to 
PORT=6667 #The connection port which is usually 6667 
NICK='DiyodeDoorBot' #The bot's nickname 
IDENT='pybot' 
REALNAME='Diyode HQ Door Bot' 
OWNER='diyode' #The bot owner's nick 
CHANNELINIT='#diyode' #The default channel for the bot 
readbuffer='' #Here we store all the messages from server 

s=socket.socket( ) #Create the socket 
s.settimeout(5)
s.connect((HOST, PORT)) #Connect to server 
s.send('NICK '+NICK+'n') #Send the nick to server 
s.send('USER '+IDENT+' '+HOST+' bla :'+REALNAME+'n') #Identify to server

def doSpeak(msg):
  if conf.platform == 'linux':  
    say(msg)
  else:
    os.system('say "'+msg.replace('"','')+'"') 

def saveMessage(message, sender):
  d = datetime.datetime.utcnow()
  timestamp = calendar.timegm(d.utctimetuple())
  conn = MySQLdb.connect(host= "localhost", user="-------", passwd="----------", db="-------")
  x = conn.cursor()
  sql = "INSERT INTO irc_events (message,timestamp,sender) VALUES ('%s','%s','%s')" % (message,timestamp,sender)
  x.execute("INSERT INTO irc_events (message,timestamp,sender) VALUES (%s, %s, %s)", (message,timestamp,sender))
  conn.commit()
  conn.close()  

def parsemsg(msg): 
  global CHANNELINIT
  complete=msg[1:].split(':',1) #Parse the message into useful data 
  info=complete[0].split(' ') 
  msgpart=complete[1] 
  msgpartLower = msgpart.lower()
  sender=info[0].split('!') 
  saveIt = 1
  if msgpart[0]=='`' and sender[0]==OWNER: #Treat all messages starting with '`' as command 
    saveIt = 0;
    cmd=msgpart[1:].split(' ') 
    if cmd[0]=='op': 
      s.send('MODE '+info[2]+' +o '+cmd[1]+'n') 
    if cmd[0]=='deop': 
      s.send('MODE '+info[2]+' -o '+cmd[1]+'n') 
    if cmd[0]=='voice': 
      s.send('MODE '+info[2]+' +v '+cmd[1]+'n') 
    if cmd[0]=='devoice': 
      s.send('MODE '+info[2]+' -v '+cmd[1]+'n') 
    if cmd[0]=='sys': 
      syscmd(msgpart[1:],info[2]) 
  elif msgpartLower.find('!speak') == 0:
    doSpeak(msgpart[6:])
    s.send('PRIVMSG '+CHANNELINIT+' :Message spoken.n') 
    if msgpartLower.find('!speak: hello?') == 0:
      doPrank()
  elif msgpartLower.find('!newtopic") == 0:
    doSaveTopic(msgpart[13:], sender[0])
  elif msgpartLower.find('!todo') == 0:
    doSaveTask(msgpart[5:], sender[0])
  if msgpart[0]=='-' and sender[0]==OWNER : #Treat msgs with - as explicit command to send to server 
    saveIt = 0;
    cmd=msgpart[1:] 
    s.send(cmd+'n') 
    if conf.debug:
      print 'cmd='+cmd
  if saveIt and msgpart.find('Welcome to the freenode Internet Relay Chat Network') != 0:
    saveMessage(msgpart.strip(), sender[0])

def syscmd(commandline,channel): 
  cmd=commandline.replace('sys ','') 
  cmd=cmd.rstrip() 
  os.system(cmd+' >temp.txt') 
  a=open('temp.txt') 
  ot=a.read() 
  ot.replace('n','|') 
  a.close() 
  s.send('PRIVMSG '+channel+' :'+ot+'n') 
  return 0

def checkFile():
  h = open(conf.ircFlagFile,'r')
  contents = h.read()
  h.close()
  return contents    

def clearFile():
  h = open(conf.ircFlagFile,'w')
  h.write('-')
  h.close()
  
def doSaveTopic(topic, sender):
  h = open(conf.topicFile,'a')
  now = str(datetime.datetime.utcnow())
  topic = "%s:::%s:::%sn" % (now,topic.strip(),sender.strip());
  print topic
  h.write(topic)
  h.close()
  s.send('PRIVMSG '+CHANNELINIT+' :' + 'Topic Saved: http://diyode.com/topics.phpn') 
  getURL('http://www.diyode.com/interface/UpdateTopics.php')

def doSaveTask(task, sender):
  h = open(conf.taskFile,'a')
  now = str(datetime.datetime.utcnow())
  task = "%s:::%s:::%sn" % (now,task.strip(),sender.strip());
  print task
  h.write(task)
  h.close()
  s.send('PRIVMSG '+CHANNELINIT+' :' + 'To Do Item Saved: http://diyode.com/tasks.phpn') 
  getURL('http://www.diyode.com/interface/updateTasks.php')

def getURL(urlStr):
  #print "getting URL: "+urlStr
  try:
    fileHandle = urllib2.urlopen(urlStr,timeout=3)
    str1 = fileHandle.read()
    fileHandle.close()
    #print 'getUrl Result: '+str1
    return str1
  except IOError:
    print 'Cannot open URL %s for reading' % urlStr
    return False


def doPrank():
  if arduino1:
    arduino1.write("P");


port1 = ""
ports = list_ports.comports()
for port in ports:
    if (port[0].find("usb") != -1):
        if port1 == "":
            port1 = port[0]
if (port1 == ""):
    print "there are no arduinos attached"
else:
  arduino1 = serial.Serial(port1, 115200)
  print("arduino1 connected to "+port1)


clearFile()
pongCount = 0;

while pongCount < 600: 
  pongCount = pongCount + 1
  if pongCount % 10:
    if not conf.checkRun():
      print "Stopping irc"
      exit()
  try:
    line = s.recv(1000) #recieve server messages 
    time.sleep(0.25)
  except:
    line = ''
  if len(line):
    if conf.debug:
      print line #server message is output 
    if line.find(' :+i')!=-1: 
      time.sleep(0.5)
      print 'joining #diyode'
      s.send('JOIN '+CHANNELINIT+'n') #Join a channel 
      pongCount = 0
    if line.find('PRIVMSG')!=-1: #Call a parsing function 
      parsemsg(line) 
    line=line.rstrip() #remove trailing 'rn' 
    linebits=line.split() 
    if(linebits[0]=='PING'): #If server pings then pong 
      if conf.debug:
        print "pongCount %d" % (pongCount)
      s.send('PONG '+linebits[1]+'n')
      pongCount = 0
  sendMsg = checkFile()
  if sendMsg != '-':
    if conf.debug:
      print sendMsg
    s.send('PRIVMSG '+CHANNELINIT+' :'+sendMsg+'n') 
    clearFile()

conn.close()
									

Plugged into the linux box is an arduino with CodeShield attached, and the face hanging on a laser cut arm attached to the codeshield’s servo. This is the sketch running on the arduino

#include <Servo.h>

#define ENCODER_A 14
#define ENCODER_B 15
#define ENCODER_PORT PINC
#define SWITCH 13
#define BUTTON 12
#define RGB_RED 11
#define RGB_GREEN 10
#define RGB_BLUE 9
#define LED 6
#define SERVO 5
#define PIEZO 3
#define RELAY 2
#define POT 2
#define HALL 3
#define THERMISTOR 4
#define PHOTOCELL 5

Servo aServo;
char incomingByte;
char mode = '-';
char val[7];
int col = 1;
int a = 0;

void setup() {
  Serial.begin (115200);
  aServo.attach(SERVO);
}

void loop() {
  if (Serial.available() > 0) {
    incomingByte = Serial.read();
    if (incomingByte == 'p' ||incomingByte == 'P') {
      if (a == 0) {
        a = 1;
      }
    }
  }
  if (a) {
    int angle = 45 + 90 * sin(a * 0.00174532922);
    aServo.write(angle);
    if (a++ > 1800) {
      a = 0;
    }
  }
  
  delay(10);
}

									

 

Read More