Introduction: Streettexter, Writing Robot

About: Ja, ich bin ein krimineller. Mein Verbrechen ist die Neugierde. Mein Verbrechen ist es Menschen zu verurteilen nach dem was sie sagen und denken, nicht nachdem wie sie aussehen. Mein Verbrechen ist es schlauer…

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

-

Step 1: 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

Step 2: 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.

Step 3: 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

Step 4: 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); }



Step 5: 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.

Step 6: 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

Step 7: Fails

see picture notes.

Step 8: 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