TXT ME

by englishman_in_berlin in Circuits > Art

37142 Views, 127 Favorites, 0 Comments

TXT ME

IMG_3206.JPG

TXT ME receives SMS messages and displays them, one letter at a time, by raising arms with letters on the end. Our world is getting faster and faster, so this deliberately takes its time and slowly "reads out" your message. If you don't concentrate, you will miss a letter. You will find it hard to avoid trying to guess what a word will be when only a few letters have been revealed. Patience is required, it will not be rushed. If no one sends any messages, after a while it will read out TEXT ME as an invitation.

How does it work?

There is an Arduino Uno in the box, with a GSM shield. Most of the software was taken from the Arduino examples.

Video

TXT ME

How I Made It

Example of TXT ME drawing.png
IMG_3073.JPG
IMG_3074.JPG

I drew the parts in Inkscape (see the example above) and went to the Fablab in Berlin to cut 5 mm perspex using their 40 Watt Epilog Zing 6030 laser cutter. The cutter takes 30 cm x 60 cm sheets, so I brought enough of those along for the design plus a few extra for corrections. I moved the parts around in the drawings to make best use of the material. Peel off the top protective layer from the perspex but keep the bottom layer so that the laser doesn't scorch the bottom of the perspex. When the first parts are cut, check how well the parts go together and adjust the sizes for a good fit. I cut a hole in the baseplate for a servo, but used tie wraps for the servo on the arm. If I were to make it again, I would use 90° brackets to hold the servo on the arm. The hinges for the arms is just a piece of wire, threaded all of the way round. It might be nice to use EL wire for this, to light it up from inside.

What Were the Main Problems?

IMG_3160.JPG
IMG_3159.JPG
IMG_3161.JPG

The servomotors were challenging. A 5 Euro servo with plastic gearing liked to vibrate incredibly violently when the arm was connected to it and I tried to move it. A 20 Euro servo was stable but did not cover the range of angles that I needed. A 10 Euro servo was stable, but ... over the course of a busy evening, the servo motor's precision varied, so that it started missing the "A" arm completely. On the following morning, it was back to normal. I guess that even more money is required to get a servo which has the required precision, or maybe some external sensors could offer regular auto-calibration.

The Arduino was very sensitive to my soldering iron switching on and off as the iron controls its temperature. As I couldn't do too much about that, once I had understood what was happening, I added a heartbeat LED which flashes for each pass through the main loop, to give me confidence that the Arduino was still running and not hung up, causing me to look for non-existent faults.

The Arduino Code

/* TEXT ME

This sketch, for the Arduino GSM shield, waits for an SMS message and displays it through the serial port and on Kim's TEXT ME machine.

Some code from Javier Zorzano / TD http://arduino.cc/en/Tutorial/GSMExamplesReceiveSMS */

/******************************************************************************************

* T H E C I R C U I T

*

* An external power supply is mandatory, otherwise the GSM shield doesn't work

* The GSM shield is plugged onto an Arduino Uno

* There are two servos both connected to GND and +5V* The rotary servo control line goes to pin 5

* The vertical servo control line goes to pin 10

* The ready LED goes to pin 11 via a 200 ohm resistor

*******************************************************************************************/

#include // Include the GSM library

#include // Include the servo library

#define PINNUMBER "XXXX" // PIN number for the GSM SIM card

#define rotaryPin 5 // Connect pin 5 to the rotary servo

#define verticalPin 10 // Connect pin 10 to the vertical servo

#define readyLed 11 // Connect pin 11 to LED which shows that GSM unit is connected

GSM gsmAccess; // Initialise the library instances

GSM_SMS sms;char senderNumber[20]; // Array to hold the number that an SMS is retrieved from

Servo rotaryServo; // Create servo object to control rotary servo

Servo verticalServo; // Create servo object to control vertical servo

int rotation [] = { 161, 154, 148, 142, 136, 130, 124, 118, 112, 105, 99, 92, 85, 78, 72, 66, 60, 54, 48, 42, 36, 30, 24, 19, 12, 7};

int numberOfLetters = 26;

int currentServoPosition;

int newServoPosition;

int up = 170; // Define the two extremes of vertical servo

int down = 117;

int inactivityTimer = 0;

/**************************************************************************************

* Set up

**************************************************************************************/

void setup()

{

Serial.begin (9600); // Initialise serial communication at 9600 rotary

Servo.attach (rotaryPin); // Attach the rotary servo vertical

Servo.attach (verticalPin); // Attach the finger servo boolean

notConnected = true; // GSM connection state

parkArm(); // Move to a safe space if not there already

prompt();

parkArm(); // Move to a safe space

while (notConnected) // Wait until GSM connection is established

{

digitalWrite(readyLed, LOW); // Turn the ready LED off

if (gsmAccess.begin (PINNUMBER) == GSM_READY) notConnected = false;

else

{

Serial.println ("Not connected");

delay (1000);

}

}

Serial.println ("GSM initialized"); // Confidence building message

Serial.println ("Waiting for messages");

digitalWrite (readyLed, HIGH); // Turn the ready LED on} /**************************************************************************************

* Main loop

**************************************************************************************/

void loop()

{

char c;

// If there are no SMSs available at regular intervals make a show

inactivityTimer = inactivityTimer++; // Count up for each loop

if (inactivityTimer > 300) // Approx. 1 sec per loop

{

inactivityTimer = 0; // Reset the tomer

prompt(); // Draw attention to yourself

}

// Heartbeat on LED to show that loop is still looping

digitalWrite(readyLed, LOW); // Turn the ready LED off

delay(100); // Time that LED is off

digitalWrite(readyLed, HIGH); // Turn the ready LED on

// If there is an SMS available

if (sms.available())

{

inactivityTimer = 0; // If message then reset inactivity timer

Serial.println("Message received from:");

// Read message bytes and print them

while(c=sms.read())

{

raiseLetterArm (c); // "Print" to perspex arms

}

sms.flush(); // Delete message from modem memory

} delay(1000);

}

/**************************************************************************************

* Prompt anyone nearby

**************************************************************************************/

void prompt()

{

raiseLetterArm (84); // "Print" TEXT ME to perspex arms

raiseLetterArm (69);

raiseLetterArm (88);

raiseLetterArm (84);

raiseLetterArm (77);

raiseLetterArm (69);

}

/**************************************************************************************

* Put the arm out of harm's way

**************************************************************************************/

void parkArm()

{

rotaryServo.attach (rotaryPin); // Attach the rotary servo

newServoPosition = 180; // Park where the arm can do no harm when switched on/off

currentServoPosition = 9; // Make sure that it is clear that a move is needed

rotateServo(); // Move the rotary servo

delay(2000); // Wait for the servo to get there

rotaryServo.detach (); // Keep things quiet

}

/**************************************************************************************

* Raise the letter arm specified

**************************************************************************************/

void raiseLetterArm (int charEntered)

{

rotaryServo.attach (rotaryPin); // Attach the servo on pin

// 'A' is 65 'a' is 97 'Z' is 90

if ((charEntered < 65) || ((charEntered > 91) && (charEntered < 97)) || (charEntered > 123))

{

// Then is not A to Z so do nothing - wait a bit

}

else

{

// Make lower case into upper case

if (charEntered > 96)

{

charEntered = charEntered - 32;

}

if ((charEntered > 64)&&(charEntered < 91))

{

newServoPosition = rotation [charEntered - 65]; // Set where to turn the arm to

rotateServo();

}

// Show what was sent:

Serial.print("You sent me: \'");

Serial.write(charEntered);

Serial.print("\' ASCII Value: ");

Serial.println(charEntered);

pressDown ();

}

rotaryServo.detach (); // Stop random movements}

/*********************************************************************************

* Push the finger down - make sure it is up before returning

!*********************************************************************************/

void pressDown()

{

verticalServo.attach (verticalPin); // Attach the finger servo

delay(500);

for (int i= 0; i < up-down; i++)

{

verticalServo.write (up-i); // Finger down one degree at a time

delay(10);

}

delay(2000);

for (int i= 0; i < up-down; i++)

{

verticalServo.write (down +i); // Finger up one degree at a time

delay(10);

}

delay(500);

verticalServo.detach (); // Stop random movements}

/*********************************************************************************

* Move the arm around - make sure that the finger is up before using it*********************************************************************************/

void rotateServo()

{

int difference;

int noOfSlowSteps = 5; // No of degrees to move slowly was 5

int waitForServo = 20; // Do not reduce too much or it misses steps

verticalServo.attach (verticalPin); // Attach the finger servo

verticalServo.write (up); // Make sure it's out of the way

verticalServo.detach (); // Detach

difference = newServoPosition - currentServoPosition;

if (difference > noOfSlowSteps)

{

rotaryServo.write (newServoPosition - noOfSlowSteps); // Quickly move

currentServoPosition = newServoPosition - noOfSlowSteps; // Note new position }

if (difference < -1*noOfSlowSteps)

{

rotaryServo.write (newServoPosition + noOfSlowSteps); // Quickly move

currentServoPosition = newServoPosition + noOfSlowSteps; // Note new position

}

difference = newServoPosition - currentServoPosition; // Recalculate difference if (difference > 0)

// Then slowly increase current position until difference is 0

{

for (int i = 0; i < difference+1; i++)

{

rotaryServo.write (currentServoPosition + i);

delay (waitForServo);

}

}

else // Then slowly decrease current position until difference is 0

{

for (int i = 0; i < (-1*difference)+1; i++)

{

rotaryServo.write (currentServoPosition - i);

delay (waitForServo);

}

}

currentServoPosition = newServoPosition; // Remember where arm is}

/**********************************************************************************

* Windscreen wiper test (commented out)

**********************************************************************************/

/* for (letter=0; letter < numberOfLetters; letter++)

{

newServoPosition = rotation [letter]; // Set where to turn the arm to

rotateServo();

Serial.print("rotation [letter] ");

Serial.println(rotation [letter]);

}

delay(5000);

for (letter=numberOfLetters; letter +1 > 0; letter--)

{

newServoPosition = rotation [letter]; // Set where to turn the arm to

rotateServo();

Serial.print("rotation [letter] ");

Serial.println(rotation [letter]);

}

delay(5000); */

/**********************************************************************************

/* To calibrate the rotary servo (commented out) - need to set potpin

**********************************************************************************/

/*

val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)

val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value between 0 and 180)

rotaryServo.write(val); // sets the servo position according to the scaled value

delay(15); // waits for the servo to get there

// print out the value you read:

Serial.println(val); */