rotozip Posted December 6, 2008 Share Posted December 6, 2008 I have created an IRC bot that allows users to enter various commands in a channel which is then sent to an Arduino controller. I currently have connected the Arduino to a DB serial connector which then connects to a CM17a Firecracker X10 device. An example of what I can do with this is turn on and off lights from an IRC channel. This might be a fun thing to do in the hakHouse. The code is kind of rough right now but it works. Here is a photo of the Arduino->DB9->CM17a: Sorry the blurry picture. Here is the php bot script: #!/usr/bin/php <?php // //Author: Randy Greeen aka rotozip // www.slappingturtle.com //Date: 12/05/08 // /********************** * Start configuration * **********************/ //IRC stuff $debug = false; //Because of the way ustream has set up IRC set isUstream to true if your bot runs on ustream. //For any standard IRC set isUstream to false $isUstream = false; $IRCServer = "irc.wyldryde.org"; $IRCPort = 6667; $channel = "#channel"; $nick = "controlbot"; $password = "password"; //Set mustBeVoiced to true if user must be voiced to send commands $mustBeVoiced = false; //Seconds to queue each message. $waitTime = 20; //Arduino stuff $arduino = "/dev/cu.usbserial-A4000Q4s3"; /******************** * End configuration * ********************/ $messageQueue = NULL; $messageType = ""; if ($isUstream) { $messageType = "PRIVMSG " . $channel; } else { $messageType = "NOTICE "; } // Prevent PHP from stopping the script after 30 sec set_time_limit(0); function queueMessage($socket, $user, $message) { global $messageQueue, $counter, $waitTime, $waitTimeMili, $messageType; $index = count($messageQueue); $messageQueue[$index]["user"] = $user; $messageQueue[$index]["message"] = $message; if(count($messageQueue) > 0 ) { if(count($messageQueue) == 1 ) { $timeTilSent = ($waitTimeMili - ($counter % $waitTimeMili)) / 10; }else{ $timeTilSent = $waitTime + (($waitTimeMili - ($counter % $waitTimeMili)) / 10); } } fputs($socket, $messageType . " " .$user. " : Your command [" .$messageQueue[$index]["message"] . "] has been queued and will be sent in " .$timeTilSent." seconds.\n"); echo '(' . date('H:i:s') . ') ->' . " Queued message [" .$messageQueue[$index]["user"] . "][" .$messageQueue[$index]["message"] . "] will be sent in " .$timeTilSent. " seconds\n"; return true; } function sendMessage($socket) { global $messageQueue, $messageType; fputs($socket, $messageType . " " .$messageQueue[0]["user"]. " : Your command [" .$messageQueue[0]["message"] . "] has been sent.\n"); echo '(' . date('H:i:s') . ') ->' . " Send message [" .$messageQueue[0]["user"] . "][" .$messageQueue[0]["message"] . "]\n"; arduino($messageQueue[0]["message"]); if(count($messageQueue) > 0 ) { $temp = array_shift($messageQueue); } return true; } function checkCharacters($message) { $charsIn = array("<", ">", "&", "`", '"', "'"); $charsOut = array("<", ">", "&", "'", """, "'"); $newMessage = str_replace($charsIn, $charsOut, $message); return $newMessage; } function arduino($message) { global $arduino; echo '(' . date('H:i:s') . ') ->' . " Sending command " .$message. " to Arduino at " .$arduino. "\n"; system("echo $message > $arduino", $retVal); } // Opening the socket to the irc network $socket = fsockopen($IRCServer, $IRCPort); stream_set_timeout($socket, 0, 1); // Send auth info fputs($socket,"USER ".$nick." slappingturtle.com ST :" .$nick. " bot\n"); fputs($socket,"NICK ".$nick."\n"); if($isUstream) { //for c.ustream.tv fputs($socket,"PASS ".$password."\n"); }else{ //for other irc fputs($socket,"NICKSERV :identify ".$password."\n"); } sleep(3); // Join channel fputs($socket,"JOIN ".$channel."\n"); $counter = 0; $buffer = null; $trackNames = false; $waitTimeMili = ($waitTime * 10); echo '(' . date('H:i:s') . ') ->' . " Message queue timer set for " .$waitTime. " seconds\n"; while(!feof($socket)) { usleep(100000); // Timer Stuff // 1 minute is 600 ms if ($counter % $waitTimeMili == 0 && $counter != 0) { echo '(' . date('H:i:s') . ') ->' . " Message queue timer has elapsed\n"; if(count($messageQueue) > 0 ) { sendMessage($socket); $counter = 0; } } $counter++; // Reset counter after 2 hours (7200 seconds with 10 counts per second) if ($counter == 72000) { $counter = 0; } // End Timer Stuff $buffer .= fgets($socket, 512); while (substr_count($buffer, "\n") != 0) { // Get the end of the current line $offset = strpos($buffer, "\n"); // Get the current line $data = substr($buffer, 0, $offset); if($debug) echo '(' . date('H:i:s') . ') -> ' . $data . "\n"; // Remove it from the buffer $buffer = substr($buffer, $offset + 1); flush(); //Track data coming back from NAMES command if ($trackNames) { $replyCode = explode(" ", $data); if ($replyCode[1] != "366") { $userList = explode(" ", $data); //print_r($userList); for ($i = 0; $i <= count($userList); $i++) { $userV = '+'.$userToTrack; $userOp = '@'.$userToTrack; $userHop = '%'.$userToTrack; if(($userList[$i] == $userV) || ($userList[$i] == $userOp) || ($userList[$i] == $userHop) || ($mustBeVoiced == false)) { echo '(' . date('H:i:s') . ') ->' . " User " .$userToTrack. " authorized\n"; $found = true; $trackNames = false; queueMessage($socket, $userToTrack, $messageToTrack); break; }else{ $found = false; } } } if(!$found && ($replyCode[1] == "366")) { fputs($socket, $messageType . " " .$userToTrack. " : Only \"voiced\" users may send control commands, due to a matter of trust. Remember, to become a " . "voiced user, you must be a chat room regular (behave, play nice, help others, be here longer than a day, etc.)... \n"); echo '(' . date('H:i:s') . ') ->' . " User " . $userToTrack . " not authorized\n"; $trackNames = false; } } // Separate all data $ex = explode(' ', $data); // Send PONG back to the server if($ex[0] == "PING"){ fputs($socket, "PONG ".$ex[1]."\n"); echo '(' . date('H:i:s') . ') ->' . " PING\n"; } $usersNic = split("!", $ex[0]); $user = trim($usersNic[0], ":"); $commandFound = str_replace(array(chr(10), chr(13)), '', $ex[3]); switch ($commandFound) { case ":!" .$nick: $text = explode($commandFound, $data); $message = trim($text[1]); if(strlen($message) > 0 && preg_match("/[0-9]/", $message)) { fputs($socket,"NAMES ".$channel."\n"); $trackNames = true; $userToTrack = $user; $messageToTrack = $message; }else{ fputs($socket, $messageType . " " .$user. " : To send a command type !" .$nick. " <Command to send>. Valid command numbers are 0-9. For other ". "available commands type !" .$nick. "help.\n"); } break; case ":!" .$nick. "help": fputs($socket, $messageType . " " .$user. " : Commands are !" .$nick. ", !" .$nick. "help, and !" .$nick. " <Command to send>. Valid command numbers are 0-9.\n"); echo '(' . date('H:i:s') . ') ->' . " User " .$user. " issued the " .$commandFound. " command\n"; break; case ":!" .$nick. "about": fputs($socket, $messageType . " " .$user. " : With the " .$nick. " bot you can send commands to a device to control stuff. " . "This script was created by Randy Green aka rotozip http://www.slappingturtle.com\n"); echo '(' . date('H:i:s') . ') ->' . " User " .$user. " issued the " .$commandFound. " command\n"; break; } } } ?> Here is the Arduino sketch: /* Arduino Interface to the CM17A Wireless X10 dongle. BroHogan 7/19/08 * Modified by Randy Green aka rotozip 12/6/08 * The CM17A gets it power and data using only the RTS, CTS, & Gnd lines. * A MAX232 is not req. (0/+5V work OK) If MAX232 IS used reverse all HIGHs & LOWS * Signal RTS DTR Standby | '1' | Wait | '0' | Wait | '1' | Wait... * Reset 0 0 _____________________ _____________________ * Logical '1' 1 0 RTS _| |_____| * Logical '0' 0 1 ________ ___________________ ________ * Standby 1 1 DTR _| |_____| |_____| * * MINIMUM time for the '1', '0' and 'Wait' states is 0.5ms. * At least one signal must be high to keep CM17A powered while transmitting. * Each xmit is 40 bits -> "Header" 16 bits, "Data" 16 bits, "Footer" 8 bits * CONNECTION: RTS -> DB9 pin 7. DTR -> DB9 pin 4. Gnd. -> DB9 pin 5. */ #define RTS_pin 2 // RTS line for C17A - DB9 pin 7 #define DTR_pin 3 // DTR line for C17A - DB9 pin 4 #define BIT_DELAY 1 // ms delay between bits (0.5ms min.) #define ON 0 // command for ON #define OFF 1 // command for OFF #define BRIGHT 2 // command for 20% brighten #define DIM 3 // command for 20% dim int incomingByte; unsigned int houseCode[16] = { 0x6000, // A 0x7000, // B 0x4000, // C 0x5000, // D 0x8000, // E 0x9000, // F 0xA000, // G 0xB000, // H 0xE000, // I 0xF000, // J 0xC000, // K 0xD000, // L 0x0000, // M 0x1000, // N 0x2000, // O 0x3000, // P }; unsigned int deviceCode[16] = { 0x0000, // 1 0x0010, // 2 0x0008, // 3 0x0018, // 4 0x0040, // 5 0x0050, // 6 0x0048, // 7 0x0058, // 8 0x0400, // 9 0x0410, // 10 0x0408, // 11 0x0418, // 12 0x0440, // 13 0x0450, // 14 0x0448, // 15 0x0458, // 16 }; unsigned int cmndCode[] = { 0x0000, // ON 0x0020, // OFF 0x0088, // 20% BRIGHT (0x00A8=5%) 0x0098, // 20% DIM (0x00B8=5%) }; void setup() { pinMode(RTS_pin,OUTPUT); // RTS -> DB9 pin 7 pinMode(DTR_pin,OUTPUT); // DTR -> DB9 pin 4 Serial.begin(9600); // use the serial port to send the values back to the computer } void loop(){ // Sample Commands /* xmitCM17A('A',5,ON); xmitCM17A('A',5,OFF); xmitCM17A('A',5,DIM); */ //delay(2000); if (Serial.available() > 0) { // read the incoming byte: incomingByte = Serial.read(); //Serial.print("I received: "); //Serial.println(incomingByte); switch (incomingByte) { case 48: //0 for(int i=1; i<10; i++) { //Serial.print("Looping "); xmitCM17A('A',i,OFF); } break; case 49: //1 xmitCM17A('A',1,ON); break; case 50: //2 xmitCM17A('A',2,ON); break; case 51: //3 xmitCM17A('A',3,ON); break; case 52: //4 xmitCM17A('A',4,ON); break; case 53: //5 xmitCM17A('A',5,ON); break; case 54: //6 xmitCM17A('A',6,ON); break; case 55: //7 xmitCM17A('A',7,ON); break; case 56: //8 xmitCM17A('A',8,ON); break; case 57: //9 xmitCM17A('A',9,ON); break; } } } void xmitCM17A(char house, byte device, byte cmnd){ unsigned int dataBuff = 0; byte messageBuff[5]; // Build Message by ORing the parts together. No device if Bright or Dim if(cmnd == ON | cmnd == OFF){ dataBuff = (houseCode[house-'A'] | deviceCode[device-1] | cmndCode[cmnd]); } else dataBuff = houseCode[house-'A'] | cmndCode[cmnd]; // Build a string for the whole message . . . messageBuff[0] = 0xD5; // Header byte 0 11010101 = 0xD5 messageBuff[1] = 0xAA; // Header byte 1 10101010 = 0xAA messageBuff[2] = dataBuff >> 8; // MSB of dataBuff messageBuff[3] = dataBuff & 0xFF; // LSB of dataBuff messageBuff[4] = 0xAD; // Footer byte 10101101 = 0xAD // Now send it out to CM17A . . . digitalWrite(DTR_pin,LOW); // reset device - both low is power off digitalWrite(RTS_pin,LOW); delay(BIT_DELAY); digitalWrite(DTR_pin,HIGH); // standby mode - supply power digitalWrite(RTS_pin,HIGH); delay(35); // need extra time for it to settle for (byte i=0;i<5;i++){ for( byte mask = 0x80; mask; mask >>=1){ if( mask & messageBuff[i]) digitalWrite(DTR_pin,LOW); // 1 = RTS HIGH/DTR-LOW else digitalWrite(RTS_pin,LOW); // 0 = DTR-HIGH/RTS-LOW delay(BIT_DELAY); // delay between bits digitalWrite(DTR_pin,HIGH); // wait state between bits digitalWrite(RTS_pin,HIGH); delay(BIT_DELAY); } } delay(1000); // wait required before next xmit } Quote Link to comment Share on other sites More sharing options...
PLuNK Posted December 7, 2008 Share Posted December 7, 2008 Pretty amazing. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.