Internet enabled Furby
Finally, the holy grail in physical computing and interaction design. Lo and Behold, the internet enabled furby! Whenever you need an email-warning you can stroke, or a furry newsreader, here’s the starting point for your project.. ahum.
I used a lantronix Xport ethernet-to-serial interface to connect a normal Arduino duemillianove board to the internet. The Lantronix Xport module has been configured as simple transparent web-to-serial interface. It listens at port 10001 and puts all characters received through. One of the general purpose IO lines of the Xport has been configured to be used for controlling the connection. After pulling the line high for a short period the xport will drop the connection.
Starting point for the arduino source was a post on ladyada’s blog. It has been extended to accept arguments with the GET instruction. The arduino serves a simple webpage with a http - GET instruction which can contain some instructions for lifting the ears, blinking the eyes, etc. On the other side, the furby has been deprived of its original brain which has been replaced by the arduino connected with flatcable. For the furby-modification, schematics have been used from hackfurby.com. Instead of their approach of replacing the motor with RC servo’s, I chose to use the original electronics as much as possible (since I had no other electronics available).
In the schematics found here you can see that all sensor inputs and motor outputs are directly routed to the large ‘brain’. The smaller (speech) core is normally wired to the speaker and the large brain. Both of the brains have been removed here and replaced by flatcable. The connection is very straightforward, for some input pins the internal pullup resistors in the arduino have been used. In the top part of the code you can see which furby signal is connected to which arduino IO pin. The same names as used in the schematic have been used. In the arduino source below, a number of rudimentary Furby functions have been included. upon reset(); the furby starts to move to its 0-position. This is detected by a switch. The single motor of the furby drives the entire motion-sequence. With an incremental encoder can be counted ‘where’ in the sequence the furby currently lives.
Below’s the arduino source. It is tested on a 328-version, and while being far from clean, it shows the basic functionality. Below some snapshots of a wireshark-log of the data exchange between fireFox and my Furby. In the wireshark log only a simple ‘hello world’ page is served, but it shows the idea. Firefox is pointed to http://192.168.1.3:10001/. The furby serves a page shown in the yellow snapshot below. The code of the arduino example listed below serve’s a page listing 6 sensor values and two position options. The background color and text color of the page vary with the values of furby’s light sensor.
The source is a little longer than usual.. a 2-page Arduino sketch..
#include <NewSoftSerial.h>
#include <string.h>
#define HTTP_HEADER "HTTP/1.0 200 OK\nServer: arduino\nContent-Type: text/html\n\n"
#define IP_ADDRESS "192.168.1.4"
// Furby Pinning
// Analog inputs:
#define tummy 0
#define back 2
#define light 1
#define light_dig 3
#define upside_down 4
#define tilt 5
// digital pins
#define feed 7
#define motor_forward 6
#define motor_backward 5
#define cam_home 4
// external interrupts:
#define gear_rotation 3
#define sound_in 2
// Xport Pinning
#define XPORT_CP2 11 // on shield labeled as CP2, in fact is CP1 in config thingy..
#define XPORT_RXPIN 12
#define XPORT_TXPIN 13
char linebuffer[256];
char c;
int analog[6];
int requestNumber = 0;
NewSoftSerial mySerial = NewSoftSerial(XPORT_TXPIN, XPORT_RXPIN);
volatile int encoder_count;
void setup()
{
pinMode(cam_home,INPUT);
digitalWrite(cam_home,HIGH); //pullup
pinMode(gear_rotation,INPUT);
pinMode(motor_forward,OUTPUT);
pinMode(motor_backward,OUTPUT);
attachInterrupt(1, encoder, RISING);
//attachInterrupt(1, sound, FALLING);
Serial.begin(57600);
pinMode(XPORT_CP2, OUTPUT);
Serial.println("serial port ready");
mySerial.begin(19200);
Serial.println("ethernet response ready");
digitalWrite(XPORT_CP2, HIGH);
for (int n=14; n<20; n++) {digitalWrite(n,HIGH);} // pullups on analog inputs
reset(); // set furby in start position
}
void loop()
{
checkAnalogIns();
if (requested()){
respond();
}
}
void encoder()
{
if(digitalRead(motor_forward)==HIGH) encoder_count++;
if(digitalRead(motor_backward)==HIGH) encoder_count--;
if(digitalRead(cam_home)==LOW) encoder_count=0; // reset
}
void reset()
{
digitalWrite(motor_backward,HIGH);
while (digitalRead(cam_home)==HIGH){};
digitalWrite(motor_backward,LOW);
}
void move(int setpoint)
{
if(setpoint == 0) {reset();}
int error = setpoint - encoder_count;
if (error>0) digitalWrite(motor_forward,HIGH);
else if (error<0) digitalWrite(motor_backward,HIGH);
while (abs(error)>0){
error = setpoint - encoder_count;
}
digitalWrite(motor_forward,LOW);
digitalWrite(motor_backward,LOW);
}
uint8_t serialavail_timeout(int timeout) { // in ms
while (timeout) {
if (mySerial.available()) {
return 1;
}
timeout -= 1;
delay(1);
}
return 0;
}
uint8_t readline_timeout(int timeout) {
uint8_t idx=0;
char c;
while (serialavail_timeout(timeout)) {
c = mySerial.read();
// Serial.print(c);
linebuffer[idx++] = c;
if ((c == '\n') || (idx == 255)) {
linebuffer[idx] = 0; return idx;
}
}
linebuffer[idx] = 0;
return idx;
}
int requested(void) {
uint8_t ret;
char *found=0, *start=0, *end=0;
while (1) {
ret = readline_timeout(200);
found = strstr(linebuffer, "GET /");
if (((int)found) != 0) {
if (strstr(linebuffer, "POS=START") !=0) move(0);
if (strstr(linebuffer, "POS=MID") !=0) move(30);
return 1;
}
return 0;
}
}
void XPort_flush(int timeout) {
while (serialavail_timeout(timeout)) {
mySerial.read();
}
}
void checkAnalogIns(){
for(int n=0; n<6 ; n++)
{analog[n] = analogRead(n);}
}
void respond(){
XPort_flush(50);
int color = analog[3]/4;
mySerial.print(HTTP_HEADER);
mySerial.print("<html><body bgcolor=\"");//<meta http-equiv=\"refresh\" content=\"200\">
mySerial.print(color, HEX);
mySerial.print(color, HEX);
mySerial.print(color, HEX);
mySerial.print("\" text=\"");
mySerial.print(0xff-color, HEX);
mySerial.print(0xff-color, HEX);
mySerial.print(0xff-color, HEX);
mySerial.print("\"><h1>FURBYSERVE</h1><FORM action=\"http://");
mySerial.print(IP_ADDRESS);
mySerial.print(":10001/furby.html\" method=\"get\">");
mySerial.print("<INPUT type=\"radio\" name=\"POS\" value=\"START\"> START<BR>");
mySerial.print("<INPUT type=\"radio\" name=\"POS\" value=\"MID\"> MID<BR>");
mySerial.print("<INPUT type=\"submit\" value=\"S\"></FORM>");
mySerial.print("<h3>My analog-in values are:</h3><ul>");
for (int i = 0; i < 6; i++){
mySerial.print("<li><b>");
mySerial.print(i);
mySerial.print(": </b> ");
mySerial.print(analog[i]);
mySerial.print("</li>");
analog[1] = 0;
}
mySerial.print("</ul></body></html>");
XPort_disconnect();
Serial.print("Requested! No. ");
Serial.println(requestNumber++);
}
void XPort_disconnect(){
digitalWrite(XPORT_CP2, LOW);
delay(20);
digitalWrite(XPORT_CP2, HIGH);
}
This cannot possibly be the first Furby with an IP address ‘out there’, so if you have your own Furby-with-MAC-address, please let’s get them connected!











September 1st, 2009 at 1:19 am
[...] sent us his project to connect a Furby to the internet. The original Furby controller was replaced with an Arduino which in turn was given ethernet [...]
September 1st, 2009 at 2:31 pm
[...] retrointerfacing [...]
September 2nd, 2009 at 12:26 am
very nice project! thanks
November 7th, 2009 at 10:04 pm
Really cool,
May you provide a more detailled explanation on which pin you wired on the board goes where on the COB1 and COB2 parts of the schematic?
I’m on my own way to begin with my furby’s experiments and it’ll be very nice if I can figure this out.
Thanks.
January 3rd, 2010 at 3:49 am
The answer is in the top part of the code, where the pin-mapping is made. I think (hope) I used the same signal names as used in the schematic. The schematic describes the signals coming from the brain chip and the speech-chip. (COB1 and COB2) These are the tiny separate boards that are soldered in at a right angle on the main board.