Saturday, November 21, 2009

Chatduino: An AIM Client for Arduino/Wiznet 5100

This is an AIM instant messenger client for Arduino/Wiznet 5100, which allows you to communicate with your Arduino project from anywhere on the internet, in near real-time. You can communicate with your project through any AIM client or even your cell phone by using text messaging with Mobile AIM, and since communication is channeled though the AIM server, both the Arduino+Wiznet and chat client can exist behind firewalls.



At first I looked into creating an Arduino library for XMPP/Jabber. This would allow you to connect to Google Talk/App Engine or any other XMPP service. The problem which this approach is most XMPP services require TLS for security, and TLS isn't going to happen on an ATmega328 (thinly veiled challenge going out to anyone who can prove otherwise). I looked at a few other chat services and finally settled on AOL instant messenger (AIM) because it's very popular and the protocol (TOC) is easy to implement on the Arduino. Although the protocol is proprietary, it is well understood and there are many open source libraries/apps available.

Hardware

Arduino + Wiznet W5100 Ethernet chip. This comes packaged together nicely with the Arduino Ethernet Shield. See below for parts.

Installation and Setup

If you don't already have an AIM account, go to http://www.aim.com/ and register for a free screen name.

Download Chatduino and open in Arduino

The official Arduino ethernet library does not support DHCP. Because of this we need to specify the IP address of the Wiznet. For example:
static byte ip[] = { 192, 168, 1, 99 };
Choose an IP address that is not in use and one that is not used by DHCP. I chose 192.168.1.99 since my router (Linksys) uses 192.168.1.100 and up for DHCP.

The Arduino ethernet library also does not support DNS, so we need to use the IP address of the AIM server. I have provided the two IP addresses for the TOC domain (toc.oscar.aol.com) at this time. Either should work but these could change over time. If you are having connection problems you may need to do a domain lookup on toc.oscar.aol.com to get the new IP addresses.
//static byte server[] = { 64, 12, 202, 14 };
static byte server[] = { 64, 12, 202, 7 };
The default port of the AIM server is 5190. If you are having connectivity issues, it's possible that your network is blocking this port. Try using port 80 instead.

Update: Some users have suggested an alternate Ethernet library that supports both DNS and DHCP. You might want to give this a try.

Scroll down toward the bottom of the sketch and specify your screen name and password:
char screenName[] = "yourscreenanme";
char pass[] = "yourpassword";
Specify the length of the message array. You will still be able to receive messages that are larger but of course only "msgLen" of the message will be stored in the array. Remember that the ATmega has limited memory (1K), so you don't want to make the arrays excessively large.
const uint16_t msgLen = 50;
Specify the length of the "from" screen name. This value should be at least (+1) larger than the largest screen name you expect.
const uint16_t fromLen = 18;
Now add your code inside the if (readMessage(from, fromLen, msg, msgLen) == 0) { block. This statement evaluates to true whenever a message is received.

The from string contains the screen name that sent the message and the msg string contains the message. For example:
if (strcmp(from, "myscreenname") == 0) {
// a message from "myscreenname"
if (strcmp(msg, "get temp") == 0) {
// return analog reading of temp sensor
itoa(analogRead(0), msg, 10);
sendMessage(from, msg);
}
else if (strcmp(msg, "turn on led") == 0) {
// turn on led
digitalWrite(ledPin, HIGH);
sendMessage(from, "ok");
}
}
Because of the bi-directional nature of chat, it's also possible to send messages based on external events. Here's an example:
//Place outside of readMessage block
if (digitalRead(motionPin) == HIGH) {
sendMessage("anyscreenname", "motion detected!");
}
Now you should be able to upload your Sketch and it should sign-on to AIM.

I have provided a few functions that you may find useful. The processPinRequest function provides I/O pin control. For example ar5 returns an analogRead of pin 5, and dw4=1 performs a digitalWrite(4, HIGH). Analog write (PWM) and digital read are also supported. It's recommend that you restrict write operations to verified screen names. To accomplish this, you can set authUser to a specific screen name that is allowed to manipulate the I/O pins. By default all users are allowed to perform pin readings. Refer to the function comments for more information.

It is highly recommended to sign-off from AIM before uploading a new sketch or powering off the unit, or AIM gets confused and may not allow reconnects for a period of time. You can sign-off by sending a "signoff" message. I've encountered a few instances where the Sketch failed to connect to AIM after it was previously connected and signed off. Hitting the reset button a few times seems to fix this issue (wait at least 15 seconds between resets).

The Sketch will automatically attempt to reconnect if disconnected from AIM. You can send the "reconnects" message to get the number of reconnects. I've been running the service for over a week with zero reconnects.

Serial debug can be turned on by setting #define CHATDUINO_DEBUG to 1 at the top of the sketch.

Note: I am using the Wiznet module (WIZ812MJ) directly, without a shield. This means I need to explicitly reset the device on startup. This is done by connecting the Wiznet reset pin to 9 (use a resistor, 1K or so), and setting #define WIZNET812MJ (top of sketch) to 1. The Arduino Ethernet Shield will reset automatically and does not require this step.

Parts

Seeedstudio currently has the best price on the Arduino Ethernet Shield ($29), although they are out of stock at the time of this writing. This is actually a clone but is functionally equivalent to the official shield.

NKC Electronics sells the official Arduino Ethernet Shield for $40 or you can get their version for $32 (requires assembly).

Whatever you choose, make sure you get a Wiznet based device and not Microchip's ENC28J60.

If you're looking for the most cost effective solution (less then the cost of the ethernet shield alone), and you don't mind wiring it together, I recommend an Arduino clone, such as Modern Device's RBBB (~$12) and the WIZNET812MJ (~$21). This setup requires a breadboard/protoboard, 3.3V power, some female/male jumpers, and a USB-serial device to program the RBBB.

Considerations

Currently the sketch only processes incoming messages. Other commands: CONFIG2, UPDATE_BUDDY2, PING etc. are ignored. In a future release I would like to support buddy list updates, which would allow you to receive notifications when your friends signon/signoff.
Other improvements may include using EEPROM to save memory and not blocking on readMessage if the receive buffer is empty. Of course at this time it's just a sketch but if people find it useful, I may release it as an Arduino library.

14 comments:

  1. To get rid of the static Arduino IP-address and the fixed server IP-address I recommend using this http://kegger.googlecode.com/files/Ethernet.zip Ethernet library replacement.

    It provides DHCP and DNS. I use both in my project and it works perfectly.

    ReplyDelete
  2. The forum thread that talks about that ethernet library Markus mentioned is here:
    http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1239258729/30

    ReplyDelete
  3. at university,
    i can connect AIM with PC,
    but arduino cant.
    i tried your codes and
    i tried a code which i implement your codes + DHCP&DNS supported Ethernet Library.
    i can get ip from dhcp, but it cant connect.
    can you explain how can i do for that?

    thanks

    ReplyDelete
  4. Nice!

    Doesn't Google Talk have an AIM gateway now? Might be an alternative to direct XMPP on the Arduino.

    ReplyDelete
  5. i changed the port to 80. and its working now.
    i think my network administrator blocked 5190.port.
    Now, i will use dchp+dns supported ethernet library for that.

    ReplyDelete
  6. I made one! Temperature and light sensor hooked up to it. AIM message to ednolanarduino.

    ReplyDelete
  7. Thanks everyone for the feedback. I have updated the entry.

    ReplyDelete
  8. Hey Andrew!
    This is Awesome and you are Genius! Thank for posting the article and the code. I have it up and running with a Ping Sensor to detect motion. Sweet!

    Al

    ReplyDelete
  9. I just stumbled upon this...It's Great!
    Do you have any interest in porting it to the Asynclabs WiShield?

    ReplyDelete
  10. Hi,
    When I try to compile this using the latest Ubuntu v0022 IDE, I get a compiler type error on this section of code:

    void write(const prog_uchar *str) {
    while (pgm_read_byte(str++) != 0) {
    client.write(pgm_read_byte(str - 1));

    Can anyone assist?

    ReplyDelete
  11. Add to the top:

    #include SPI.h
    #include avr/pgmspace.h

    (in bigger than and less than brackets)

    ReplyDelete