Streettexter, Writing Robot
For the international climate-strike 2019 we build this little bot.
We hope you will join our fight. Learn, build and rebel!
Everything in this instructable is free to use. Only requirement you have to describe yourself as radical left or similar.
This little machine is loaded with 8 calc sraycans (removable).
By pulling it, the machine spray impulses to write a text at street or wall.
OK, lets start
Supplies
-
Supplies
We need:
1x Skateboard
1x limit switch EATON LS-S11S-P or similar
1x Arduino Uno
1x Adafruit 16-Channel 12-bit PWM/Servo Shield - I2C interface https://www.adafruit.com/product/1411
8x Servo motor TowerPro SG90 Servo or similar
1x switch for on/off 1x 9V battery and cable
2x usb-cable
1x powerbank 8000mA and UP (output >0,5A)
8x chalk spray (to spray down) see pictures
8x Ring terminals (6-2,5, blue)
1x set of spring
some wood, wires, terminals, hole tied, screws
Mechanics 1
first step of mechanics: build a frame
1. take the 8 spraycans side by side
2. measure the frame
should look like this (top view)
---------------------
---OOOOOOOO---
---------------------
our cans have 70mm diameter, this is our norm in Europe.
with this, our frame measures inside at least 560x70mm. don't build it to small!
see drawing.
3. remove spraycan heads (but not the pin mechanic)
4. build something to prevent the cans for falling through the frame
(back view) picture 3
we make a simple stopline with a wood ledge
5. cut one side of the skateboard
see picture 3
6. fix the frame at top of the board
we keep it simple as possible, as you can see.
we changed very frequently, nearly every time that we worked on it.
sorry for this but the most pictures that we take are useless.
Electronics 1
1. solder the robot shield after the instruction form adafruit and test it
add plugs for 5V, Gnd and 8-13 (or solder wires direct to needed places) picture 1
2. prepare one of your USB cable to supply the robot shield with 5V
3. check arduino with blink sketch or similar
4. connect arduino and shield
5. add adafruit PWM Driver Servo library via arduino IDE library manager (pic 2)
6. test with sample program from adafruit
connect servo motors one after one
Programming
Load the .ino
compile it
upload the code
-----------------------------------------------------------------------------------------------------------
read if you want to know how it works, or else go to next step:
!!Pay attention code parts insert via copy and paste. HTML is not optimised to show "<" or inserts. please only use the *.ino!! It is possible that the code below is not OK.
in the first line you give the message that the bot will write. but pay attention ONLY ASCII LETTERS, SEE TABLE in sketch! ä ö ß will not work
char Display_String[] = "System Change, not Climate Change!";
-----------------------------------------------------------------------------------------------------------
next lines are necessary for including code for motors
#include "<"Wire.h">"
#include "<"Adafruit_PWMServoDriver.h">"
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
-----------------------------------------------------------------------------------------------------------
table shows 95 chars (I dont insert this table it's just to long)
every char have 5 columns, and every column have 8bits = 1byte (coded in hex) if you interested or gifted in creating fonds you can edit this or create new chars or graphics. (new fonds are welcome because the actual fond use only 7bit most of the time)
-----------------------------------------------------------------------------------------------------------
after char array, you will find the hardware variables:
int LED_PINS[] = {9,8,7,6,5,4,3,2}; //for debugging and testing with LEDs
int SAFTY_PIN = 10; //if not OK than all servos to zero position
int INPUT_PIN = 11; //pulserless / clock from weel
int DEFAULT_LED =13; //for debugging
uint8_t servonum[] = {0,1,2,3,4,5,6,7}; // servo channels
-----------------------------------------------------------------------------------------------------------
other varialbles and definitions:
int CUSTOM_CHAR_LENGTH = 5; //5 columns per char
int LED_COUNT = 8; //8 bit per columns
int k //counter variable
int buttonStateSafty = 0; // variable for reading the saftybutton status
int buttonStateSaftyold; //detect rising edges
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
int buttonStateInput = 0; // variable for reading the pushbutton status
int buttonStateInputold; //detect rising edges
int lengthstring = 0; // length of Display_String
int chrinstr = 0; // chars in strin int pxinchr = 0;
// pixel (colum) in char
int inByte; char data;
#define SERVOMIN 150 // this is zero position, motor move to this point to be quiet
#define SERVOMAX 350 // this 'maximum' position, bot should spray by this position
-----------------------------------------------------------------------------------------------------------
setup give the information to hardware and start serial communication
void setup() {
pinMode(DEFAULT_LED, OUTPUT);
pinMode(INPUT_PIN, INPUT);
pinMode(SAFTY_PIN, INPUT);
for (int i = 0; i < LED_COUNT; i++) {
pinMode(LED_PINS[i], OUTPUT); }
Serial.begin(9600);
Serial.println("Boot");
pwm.begin();
pwm.setPWMFreq(60); // This is the maximum PWM frequency
delay(10);
}
-----------------------------------------------------------------------------------------------------------
Main loop:
in the main loop we try only take care of safty (all servos to zero position), sadly it does not prevent for hardware errors. And I dont find solutions for powersuppy errors.
first we read the state of our pin SAFTY_PIN, than we check if there is
a new state, it's necessary for detecting rising or falling signals. Inside the if condition we notice the new state. if the new signal is high we give some feedback to serialport "Safety OK: " and some info.
void loop()
{ buttonStateSafty = digitalRead(SAFTY_PIN);
//if (Serial.available() > 0) //debugging stuff
if(buttonStateSaftyold != buttonStateSafty) {
buttonStateSaftyold = buttonStateSafty;
if (buttonStateSafty == HIGH) {
Serial.println("Safety OK: ");
//lengthstring = sizeof(Display_String);
lengthstring = strlen (Display_String
-----------------------------------------------------------------------------------------------------------
);
Serial.print("lenth: ");
Serial.println(lengthstring);
Serial.print("waiting for pulse to start writing: ");
Serial.println(Display_String);
in the follwing while condition we start the "wrtfcn" loop witch actually writes our string.
while(buttonStateSafty == HIGH) {
wrtfcn();
buttonStateSafty = digitalRead(SAFTY_PIN); }
//All to 0 Position }
if (buttonStateSafty == LOW) {
Serial.println("Safety not OK: ");
AllToZero(); }
}
delay(10); }
-----------------------------------------------------------------------------------------------------------
loop (wrtfcn):
first we read the state of our pin INPUT_PIN, than we check if there is a new state, it's necessary for detecting rising or falling signals. Inside the if condition we notice the new state. if the new signal is high the next if conditions activates.
void wrtfcn()
{ buttonStateInput = digitalRead(INPUT_PIN);
if(buttonStateInputold != buttonStateInput) {
buttonStateInputold = buttonStateInput;
if (buttonStateInput == HIGH) {
-----------------------------------------------------------------------------------------------------------
in next if conditions we count impulses form the encoder (clock), every rising edge from INPUT_PIN cout up.
One if condition write a space between the chars, its easyer to read.
One if condition write the chars .
One if condition give order to change to the next char.
if (chrinstr <= lengthstring -1) {
if ( pxinchr == 5) //space between chars {
Serial.println("space ");
AllToZero();
pxinchr++; }
if ( pxinchr < 5) //print char parts {
Serial.print("now printing: ");
Serial.print(pxinchr);
Serial.print(" of char: ");
Serial.println(Display_String[chrinstr]); //print coulum in char job here
data = Display_String[chrinstr];
if (data < 32 || data > 126) //move char to chose right char in array //Buchstaben versetzen um richtige position im array zu nehmen
{
data = 32;
}
data -= 32;
byte temp_data = CUSTOM_CHAR[data][pxinchr]; // chose part 0-5 of one char //übertag nach byte, von aktuellem buchstabe z.B. [1] spalte nehmen [0]
for (int y=0; y// one part of char have 8 parts //für alle 8 zeilen ausführen
{
digitalWrite(LED_PINS[y], bitRead(temp_data,y)); //give values to motors //motor wählen, sollstatus aus byte nehmen
//Serial.print(y);
pwm.setPWM(servonum[y], 0, SERVOMAX);
if (bitRead(temp_data,y)== 1) {
pwm.setPWM(servonum[y], 0, SERVOMAX);
//Serial.print("MAX"); }
else {
pwm.setPWM(servonum[y], 0, SERVOMIN);
//Serial.print("MIN"); } }
//Serial.print("job ");
pxinchr++; }
if ( pxinchr>5) //next char {
Serial.println("next char ");
chrinstr++;
pxinchr = 0; }
}
else {
Serial.println("DONE ");
AllToZero();
digitalWrite(DEFAULT_LED, HIGH); }
}
}
else {
//
}
delay(10);
}
-----------------------------------------------------------------------------------------------------------
in the for loop all servos
void AllToZero ()
{
//Serial.println("00 ");
for (int i=0; i
{
pwm.setPWM(servonum[i], 0, SERVOMIN);
//Serial.println("zero "); }
}
-----------------------------------------------------------------------------------------------------------
the last loop is just for the case that there are problems with servo behavior. It's a part of the Adafruit sample programm.
// you can use this function if you'd like to set the pulse length in seconds
// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. its not precise!
void setServoPulse(uint8_t n, double pulse) {
double pulselength;
pulselength = 1000000; // 1,000,000 us per second
pulselength /= 60; // 60 Hz
Serial.print(pulselength);
Serial.println(" us per period");
pulselength /= 4096; // 12 bits of resolution
Serial.print(pulselength);
Serial.println(" us per bit");
pulse *= 1000000; // convert to us
pulse /= pulselength;
Serial.println(pulse);
pwm.setPWM(n, 0, pulse); }
Downloads
Electronics 2
We programmed one safty-switch to pin 10. encoder to pin 11 and LED to pin 13 (or for debugging 2-9). The input pins are connected to GND with a resistor to get a clean input, the encoder is clamped to no (normaly open) 13-' - 14. Only one servo cable was to short and we have to extend it.
For everything else see pictures or drawing.
Mechanics 2
the encoder have be pressed 2 times by on turn. Pay attention that it works correctly and of course check the direction
On the top we fixed nails and wire to get a safe spraycan position.
servos and arming wire:
servos should be in zero position, than the arm (white top) is now ready to fix.
every thing else you can see in picture 6
Fails
see picture notes.
Utilization/Results
Before arming switch off, shake the cans long enogh and test them before fixing.
Pay attention, many Powerbanks shut down after some time if no energy is in use. LEDs at the shield have to light green and red. Maybe you have to turn on the bank again if arming takes to much time.
By arming the spraycans its good to drill the wires a bit, than you dont loose them while driving.
We stopped time while writing "System Change, not Climate Change!"
Take around 50 seconds.
It was bad underground, the Text is ca. 50cm high and 20m long.
With darker underground we are able to speed up a bit, but if it is too fast the white color is not strong enogh.
Please give feedback and enjoy.
Zynch