Introduction: OpenSquare - Write Big With a RC Car

Hi there,

You're going to transform any RC toy car into a big message writer that can be used to transform your city squares into witty messages, political statements or beautiful drawings, and for less than 20$. The idea is to drop a trail of powder or paint behind the car to write the message.

Every RC car is different, and you'll be using your creativity and improvisation skills to create your machine. The code included enables to "record" a path that you have performed controlling yourself the car, and then "replay" this path by simply pressing a button and letting the car go around do its thing, while you can enjoy the performance or run away depending what you loaded the car with.

Let's get started, shall we?

This is a work in progress. Read the latest news on myide.tumblr.com/tagged/opensquare.

Step 1: What Do I Need?

You need:

  • a RC car (I bought a discounted Lamborghini from Maplin for £15)
  • flour or salt, paint, gas, anything else you want to write with
  • a square
  • 2 Arduino (I used a Uno and a Nano)
  • a servo motor (I used a Futaba S3003)
  • 5 npn transistors (I used a BC 547B)
  • 5 1kohm resistors
  • a breadboard (could do without)
  • some random material, screws and so on
  • a political statement or message or something

And in terms of tools:

  • a soldering iron
  • glue or a glue gun
  • a hand drill

Step 2: Get Inside the Remote

Remove all screws and get access to the inside of the remote control. All remotes are different but it should look something like this. What we are interested in is what's under the up/down left/right buttons.

Depending on your model, you should have either a metal part taped on top of the circuit board or a push button. Now let's look at on of these buttons. All they do is close or open a circuit. So if you grab a piece of wire and try to touch both sides (A and B) on the close up, you should have your car reacting as if you pressed that button.

Following the circuit, you should be able to tell quite easily one side of the switch (or one of the two pads) is going towards one of these micro controller chip while the other is going to the ground (the ground is connected to the - side of the battery).

Once you've got that, we'll need to do a bit of soldering. Grab some cable and solder it to the pad that is going to the microchip. Do that for the 4 buttons. Solder one other wire to the ground of the remote. Now we want the Arduino to do the job of controlling the remote for us. So to do that we need to use transistors. Transistors are like water taps with electricity, a gentle push on the handle controls a much stronger flow of water. Arduino can only do the gentle push.

Follow the wiring diagram and you should have your remote hacked enough for a quick "blink test".

Step 3: Arduino Drives Your Car

Upload the following code to your Arduino and you should have your car moving around.

/*
Blink for OpenSquare RC Car - Pierre - 22/02/14
Turns on the car controls on for one second, then off
for one second, repeatedly.
*/

int up = 4;
int down = 6;
int left = 9;
int right = 10;

// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(up, OUTPUT);
pinMode(down, OUTPUT);
pinMode(left, OUTPUT);
pinMode(right, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
digitalWrite(up, HIGH); // turn the pin on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(up, LOW); // turn the pin off by making the voltage LOW
delay(1000); // wait for a second

digitalWrite(down, HIGH); // turn the pin on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(down, LOW); // turn the pin off by making the voltage LOW
delay(1000); // wait for a second

digitalWrite(left, HIGH); // turn the pin on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(left, LOW); // turn the pin off by making the voltage LOW
delay(1000); // wait for a second

digitalWrite(right, HIGH); // turn the pin on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(right, LOW); // turn the pin off by making the voltage LOW
delay(1000); // wait for a second
}

Continued soon!

Step 4: Hacking the 5th Channel

So as you know, the remote uses 4 channels to control the car: up, down, left and right. However we need to control when the powder will fall and when it won't in order to be able to write something. So we need to have a 5th information sent from the remote. Fortunately, toy companies use standard mass manufactured radio emitter and receptor in their RC cars, and these have a 5th channel. This is sometime used as a Turbo mode or as an extra action. For most cheap cars it is just not used. Fortunately for us.

Now you'll need to look for both emitter (Tx) and receiver (Rx) chips inside the remote and the car. Most chips have a standard pin order but if you can read the serial number of your chip, try to google it to make sure it's the good one. Mine was a RX2/TX2 pair and I attached the diagram of the chips above. As you can see the pins we are interested are the 6 on the remote control side and 12 inside the car. These chips are tiny so take your time to get the solder right.

On the remote side, you'll need to control this one just as the other channels you already wired, which means you need to add a transistor and a resistor to connect it to the Arduino.

Once you're done, locate the ground on the circuit board of the car and solder a wire there. I used the ON/OFF switch because it was easy to access.

Step 5:

Code for the car's Arduino (the one controlling the servo)

The wiring is: +5V to Servo V+
GND to Servo GND
pin 9 to Servo Control
GND to car GND
pin A0 to Rx pin 12 inside the car


// OpenSquare 1.2 - 24/02/14

#include

Servo myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created

int pos1 = 80; // variable to store the servo position
int pos2 = 120; // variable to store the servo position

int previous = 0; // the average

int inputPin = A0;

void setup()
{
// initialize serial communication with computer:
Serial.begin(9600);
myservo.attach(9); // attaches the servo on pin 9 to the servo object

}

void loop() {

// read from the sensor:
previous = analogRead(inputPin);

delay(10); // delay in between reads for stability

if (previous <= 200 && analogRead(inputPin) >= 200) {
delay(1000);
if (analogRead(inputPin) >= 200) {
myservo.write(pos2); // tell servo to go to position in variable 'pos'
}
else {
myservo.write(pos1); // tell servo to go to position in variable 'pos'
}

delay(15);
}
}

Step 6: Create the Extension for the Car

This is where you'll have to improvise. Here is how I built mine to be able to accommodate the gate and the tank.

For the gate, I used a PVC tube pipe to direct the powder towards the tail of the car. I made a part in wood that I mounted on the axis of the motor to come close the hole on the tube. I then added a few blocks of wood to fix the motor in place.

For the tank, I found some sheet of cork, flexible enough to make a funnel. I glue gunned it and locked it in place with a bigger piece of MDF.

I used the platform to fix the arduino that we will use to drive the servo, as well as some extra batteries to boost the power of my car after all this extra payload.

Have fun, make it your way, this is a guerrilla project :)

Step 7: The Code...

The basic idea for the OpenSquare is to control the car with your computer for a test run while the code records your every move. The second piece of code then "replays" the instructions that you recorded so that the car just goes around and drops the powder on its own. You can just drop the car on a square and watch it draw around, or you could draw a simple element and repeat it many times to create a huge urban copy paste.

I used Processing to communicate with the remote's Arduino.

To make things easier, I just uploaded the StandardFirmata sketch you can find under File/Example/Firmata on the Arduino software.

Use the W,S,A and D keys to control the direction of the car. I had some issues with my 5th channel being HIGH all the time so the way I control the servo is by pressing the key C. A short press opens the gate and an long press closes it. To quit the app, press Alt.

Copy the above code in two different sketches and save them as "Record" and "Replay" in the same root folder.
You should have something like this on your computer:
somewhere/OpenSquare/Record/Record.pde
somewhere/OpenSquare/Replay/Replay.pde

Here is the code for Processing for the Recorder:

// OpenSquare v1.2 - 2/2/14

import processing.serial.*;
import cc.arduino.*;
Serial myPort;
Arduino arduino;
PrintWriter outputFile;
boolean keyup = false;
boolean keyright = false;
boolean keyleft = false;
boolean keydown = false;
boolean endofrec = false;
boolean servo = false;

long lastTime = 0;

float x,y;

void setup() {

outputFile = createWriter("positions.csv");

println(Arduino.list());
arduino = new Arduino(this, Arduino.list() [3], 57600);
arduino.pinMode(2, Arduino.OUTPUT);
arduino.pinMode(4, Arduino.OUTPUT);
arduino.pinMode(6, Arduino.OUTPUT);
arduino.pinMode(9, Arduino.OUTPUT);
arduino.pinMode(10, Arduino.OUTPUT);

size(640,360);
x = width/2;
y = height/2;
lastTime = millis();
}

void draw() {
background(51);
fill(255);
ellipse(x,y,16,16);
outputFile.print(millis() + ",");
//println(millis());

if (keyup) {
y--;
arduino.digitalWrite (4, Arduino.HIGH);
outputFile.print(1+",");
}
else {
outputFile.print(0+",");
arduino.digitalWrite (4, Arduino.LOW);
}



if (keydown) {
y++;
arduino.digitalWrite (6, Arduino.HIGH);
outputFile.print(1+",");
}
else {
outputFile.print(0+",");
arduino.digitalWrite (6, Arduino.LOW);
}


if (keyleft) {
x--;
arduino.digitalWrite (9, Arduino.HIGH);
outputFile.print(1+",");
}
else {
outputFile.print(0+",");
arduino.digitalWrite (9, Arduino.LOW);
}


if (keyright){
x++;
arduino.digitalWrite (10, Arduino.HIGH);
outputFile.print(1+",");
}
else {
outputFile.print(0+",");
arduino.digitalWrite (10, Arduino.LOW);
}

if (servo) {
//y--;
arduino.digitalWrite (2, Arduino.HIGH);
outputFile.print(1+",");
}
else {
outputFile.print(0+",");
arduino.digitalWrite (2, Arduino.LOW);
}

outputFile.println();
//println("bla");
while(millis()-lastTime < 20);
lastTime = millis();

if (endofrec){
outputFile.flush(); // Writes the remaining data to the file
outputFile.close(); // Finishes the file
exit(); // Stops the program
}

}

void keyPressed() {
//if (key == CODED) {
if (key == 'w') keyup = true;
if (key == 's') keydown = true;
if (key == 'a') keyleft = true;
if (key == 'c') servo = true;
if (key == 'v') servo = false;
if (key == 'd') {
keyright = true;
//endofrec = true;
}
if (keyCode == ALT) endofrec = true;

}
//}

void keyReleased() {
// if (key == CODED) {
if (key == 'w') keyup = false;
if (key == 's') keydown = false;
if (key == 'a') keyleft = false;
if (key == 'd') keyright = false;
if (key == 'c') servo = false;
//if (key == 'v') servo = false;

}
//}


And now the code for the Replay:

// OpenSquare v1.2 - 2/2/14
import processing.serial.*;
import cc.arduino.*;
Serial myPort;
Arduino arduino;
Table table;
int temps;
int w;
int s;
int a;
int d;
int c;

void setup () {
println(Arduino.list());
arduino = new Arduino(this, Arduino.list() [3], 57600);
arduino.pinMode(2, Arduino.OUTPUT);
arduino.pinMode(4, Arduino.OUTPUT);
arduino.pinMode(6, Arduino.OUTPUT);
arduino.pinMode(9, Arduino.OUTPUT);
arduino.pinMode(10, Arduino.OUTPUT);
table = loadTable("../Record/positions.csv", "header");
println(table.getRowCount() + " total rows in table");




}

void draw() {

for (int i = 0; i < table.getRowCount(); i++) {
TableRow row = table.getRow(i);
temps = row.getInt(0);
w = row.getInt(1);
s = row.getInt(2);
a = row.getInt(3);
d = row.getInt(4);
c = row.getInt(5);

if (w == 1) {
arduino.digitalWrite (4, Arduino.HIGH);
}
else {
arduino.digitalWrite (4, Arduino.LOW);
}
if (s == 1) {
arduino.digitalWrite (6, Arduino.HIGH);
}
else {
arduino.digitalWrite (6, Arduino.LOW);
}
if (a == 1) {
arduino.digitalWrite (9, Arduino.HIGH);
}
else {
arduino.digitalWrite (9, Arduino.LOW);
}
if (d == 1) {
arduino.digitalWrite (10, Arduino.HIGH);
}
else {
arduino.digitalWrite (10, Arduino.LOW);
}
if (c == 1) {
arduino.digitalWrite (2, Arduino.HIGH);
}
else {
arduino.digitalWrite (2, Arduino.LOW);
}
delay(20);
}
arduino.digitalWrite (2, Arduino.LOW);
arduino.digitalWrite (4, Arduino.LOW);
arduino.digitalWrite (6, Arduino.LOW);
arduino.digitalWrite (9, Arduino.LOW);
arduino.digitalWrite (10, Arduino.LOW);

exit();
}

Step 8: Next Steps

Soon