Robot 2 (under $10 version)

This was the second robot I made for collective motion research purposes. As you can see, it is vastly simpler, cheaper and easier to build. Of course it doesn't have nearly as many features or as much potential as robot 1, but I think it is much better for what I'm trying to do.
Here's a picture of the prototype without the battery.



Goals and requirements - 
  • It should be as easy and cheap to make as possible so that I can make several of them.
  • It needs to sense some aspect of its environment.
  • It needs to be able to interact with other robots of its kind.
  • It needs to move in a controllable and repeatable way.
The prototype satisfies almost all of these, but it can't yet interact with other bots. I still have a couple of IO pins to work with(if I disable the reset pin and lose control of the LED) and I'm trying to think of a good way to do this. The cost is roughly 530 yen, which is much less than $10 and about half as much as robot 1.

Although it is not my goal, at this point I basically have a line following robot much like dozens of others you can find on the internet, but it actually only follows an edge between light and dark because it only has two sensors, left and right. If you stuck it on a chess board, it would go around tracing the edges of the squares. Actually, I kind of want to try that.

Electronics - 
An ATtiny85 running at 8MHz controls everything here. It makes use of the libraries from MIT that are detailed here that allow me to easily program it with Arduino. Here are some other electronics details.
  • The two pager motors are driven by 2N7002K N-channel mosfets which conveniently include protection diodes.
  • The two clear LED looking things out front are phototransistors, and the red one is a red LED. They work together to determine the lightness of the surface and are also the front legs for the robot. They are spaced about 5mm apart giving the robot a left and right value.
  • The battery is a 3.7V LIR2032 that I pulled out of a 100yen solar key chain light.
Here is a schematic of the current version. Notice I'm still using Paint to make these.
and here is a picture of the messy underside. Those black things are the mosfets. If you look closely, you can see the SMD resistors soldered between pins. Also, note that the LED and sensor leads run in a gap between the chip and the socket. They are the three solder points in the middle.


Hardware - 
As with robot 1, the pager motors were from ebay and I don't have much data on them. They are roughly 2cm long including shaft and 7mm in diameter. They are hot glued to a little piece of protoboard that makes up the chassis. The little red things on the shafts are bits of 22awg wire insulation that provide traction.
The battery connects via a homemade connector using 22awg wire and two short pin sockets. The battery just sticks on top of the chip with some tape. This was done because I have to take it off every time I reprogram the chip or recharge the battery. There is no power switch.
Well, there really isn't much more to say about the hardware.

It works -
There are, of course, some issues. Since the sensors are 5mm apart, it doesn't have a very tight "grip" on the edge between light and dark, so it tends to wobble left and right as the edge bounces between the sensors, occasionally going in just the right direction for a few centimeters. I think this would improve a little if I could place the sensors closer, but they are almost touching already. I have tried a few software adjustments, but the basic problem is still there.

Anyway, you probably want to see a video. Here is a short one.

Also, here are some more pictures.





 Since I had some requests, here is the code. It doesn't do much and I haven't really looked at it since I first wrote it. I hope some day I can get back to this project.

/*
Robot version 2
Simpler. ATtiny85, 2 motors, LED, two sensors

Sensor stuff:
    both white    :    no prior    :    random walk
                :    prior        :    turn in direction of last black
   
    one white    :    ideal situation, move straight
   
    both black    :    no prior    :    random walk
                :    prior        :    turn in direction of last white
Sorry if that was hard to understand
*/
#include "Arduino.h"

// the pin definitions
#define lmotorpin 1 // PB1 pin 6
#define rmotorpin 0 // PB0 pin 5
#define lsensepin 3 //ADC3 pin 2
#define rsensepin 1 //ADC1 pin 7
#define ledpin 4 //PB4 pin 3

// numbers for random walk and memory
#define steplength 300
#define smallturn 200
#define bigturn 500
#define memtime 1000

uint8_t lspd, rspd;
uint16_t lsenseval, rsenseval, lwhiteval, rwhiteval;

// functions
void testSensors();
void followEdge();
void moveTime(uint8_t lspeed, uint8_t rspeed, uint16_t time);
void move(uint8_t lspeed, uint8_t rspeed);
void stop();
void senseInit();
void flashLED(uint8_t flashes);

// just for convenience and simplicity (HIGH is off)
#define ledoff PORTB |= 0b00010000
#define ledon PORTB &= 0b11101111

void setup(){
    pinMode(lmotorpin, OUTPUT);
    pinMode(rmotorpin, OUTPUT);
    pinMode(2, INPUT);
    pinMode(3, INPUT);
    ledoff;
    pinMode(ledpin, OUTPUT);
    analogWrite(lmotorpin, 0);
    analogWrite(rmotorpin, 0);
   
    lspd = 17;
    rspd = 17;
   
    // give a 6 second pause to set the thing on a white surface
    lsenseval = 6;
    while(lsenseval){
        lsenseval--;
        flashLED(1);
        delay(989);
    }
    flashLED(4);
    delay(500);
    senseInit();
}

void loop(){
    followEdge();
}

void followEdge(){
    // now look for edge
    uint8_t lastMove = 1; //0=straight, 1=left, 2=right
    unsigned long moveEndTime = 0; // the millis at which to stop
    unsigned long randomBits = micros();
   
    unsigned long prior = 0; // after edge encounter set to millis + memtime
    uint8_t priorDir = 0; //0=left, 1=right, 2=both
    uint8_t lastSense = 1; //0=edge, 1=both white, 2=both black
    uint8_t i = 0; // iterator
   
    while(true){
        // only update about once every 20ms
        delay(18);
        // read the value 4 times and average
        ledon;
        delay(2);
        lsenseval = 0;
        rsenseval = 0;
        for(i=0; i<4; i++){
            lsenseval += analogRead(lsensepin);
            rsenseval += analogRead(rsensepin);
        }
        // don't divide by 4 because it is used below
        ledoff;
       
        if(randomBits == 0){ randomBits = micros(); }
       
        if((lsenseval > lwhiteval*3) && (rsenseval > rwhiteval*3)){
            // both white - if prior turn to black, else random walk
            if(lastSense == 2 || millis() < prior){
                // turn toward last black or left
                if(priorDir == 0){
                    moveEndTime = millis()+smallturn;
                    move(0, rspd); // turn left
                    lastMove = 1;
                }else if(priorDir == 1){
                    moveEndTime = millis()+smallturn;
                    move(lspd, 0); // turn right
                    lastMove = 2;
                }else{
                    moveEndTime = millis()+bigturn;
                    move(0, rspd); // turn left a lot
                    lastMove = 1;
                }
            }else{
                // random walk
                if(millis() < moveEndTime){
                    // just continue moving
                }else{
                    if(lastMove){
                        moveEndTime = millis()+steplength;
                        move(lspd, rspd); // go straight
                        lastMove = 0;
                    }else{
                        if(randomBits & 1){
                            moveEndTime = millis()+smallturn;
                            move(0, rspd); // turn left
                            lastMove = 1;
                        }else{
                            moveEndTime = millis()+smallturn;
                            move(lspd, 0); // turn right
                            lastMove = 2;
                        }
                        randomBits >>= 1;
                    }
                }
            }
            lastSense = 1;
           
        }else if((lsenseval > lwhiteval*3) || (rsenseval > rwhiteval*3)){
            // one white - this is the edge
            // just go straight
            moveEndTime = millis()+steplength;
            move(lspd, rspd); // go straight
            lastMove = 0;
            lastSense = 0;
            prior = millis()+memtime;
            if(lsenseval > lwhiteval*3){
                // the right one is black
                priorDir = 1;
            }else{
                // the left one is black
                priorDir = 0;
            }
           
        }else{
            // both black - if prior turn to white, else random walk
            if(lastSense == 1 || millis() < prior){
                // turn toward last white or left
                if(priorDir == 0){
                    moveEndTime = millis()+smallturn;
                    move(lspd, 0); // turn right
                    lastMove = 2;
                }else if(priorDir == 1){
                    moveEndTime = millis()+smallturn;
                    move(0, rspd); // turn left
                    lastMove = 1;
                }else{
                    moveEndTime = millis()+bigturn;
                    move(lspd, 0); // turn right a lot
                    lastMove = 2;
                }
            }else{
                // random walk
                if(millis() < moveEndTime){
                    // just continue moving
                }else{
                    if(lastMove){
                        moveEndTime = millis()+steplength;
                        move(lspd, rspd); // go straight
                        lastMove = 0;
                    }else{
                        if(randomBits & 1){
                            moveEndTime = millis()+smallturn;
                            move(0, rspd); // turn left
                            lastMove = 1;
                        }else{
                            moveEndTime = millis()+smallturn;
                            move(lspd, 0); // turn right
                            lastMove = 2;
                        }
                        randomBits >>= 1;
                    }
                }
            }
            lastSense = 2;
        }
    }
}

void moveTime(uint8_t lspeed, uint8_t rspeed, uint16_t time){
    analogWrite(lmotorpin, lspeed);
    analogWrite(rmotorpin, rspeed);
    delay(time);
    analogWrite(lmotorpin, 0);
    analogWrite(rmotorpin, 0);
}

void move(uint8_t lspeed, uint8_t rspeed){
    analogWrite(lmotorpin, lspeed);
    analogWrite(rmotorpin, rspeed);
}

void stop(){
    analogWrite(lmotorpin, 0);
    analogWrite(rmotorpin, 0);
}

// stores the average of 16 readings as a white value
void senseInit(){
    lwhiteval = 0;
    rwhiteval = 0;
    ledon;
    delay(2);
    for(uint8_t i=0; i<16; i++){
        lwhiteval += analogRead(lsensepin);
        delay(1);
        rwhiteval += analogRead(rsensepin);
        delay(9);
    }
    lwhiteval >>= 4;
    rwhiteval >>= 4;
    ledoff;
}

void flashLED(uint8_t flashes){
    while(flashes){
        flashes--;
        ledon;
        delay(200);
        ledoff;
        if(flashes){ delay(500); }
    }
}

36 comments:

Anonymous said...

hello friend, excellent work, you could make some of these robots, and sell them to me?
my email is: admin@levelnoon.com

Anonymous said...

hello sir,u have done a great work,i would like to try this bot,could you please give the details regarding this, thanks and regards

my mail id is: ravikumar.a.93@gmail.com

Anonymous said...

Could you send me a list of all the things you need to buy? Would be nice of you. :)
j.emmeborn@gmail.com

ME said...

RE: all the above comments -
I'm glad you are interested in this bot. If you want to know the parts used or the "details regarding this" simply read the post. It is all there. If you have a more specific question, I would be happy to answer it.

SWL I/0222/AR (Alessandro) said...

Hello friend,
could you share the FW with me?
thanks

ME said...

@Alessandro -
Sure. I added it to the page above.

Upendra Sachan said...

it is a phototransistor or a photo resistor? as phototransistor has 3 legs and urs only have 2 of them...
sachanster19@gmail.com

ME said...

@Upendra Sachan -
It is a phototransistor. The two legs are the collector and emitter(base is optical). Here is the datasheet: http://www.mouser.com/catalog/specsheets/P_100_AV02-0013EN%20ASDL-6620%20DS%20Dec071.pdf

Cheezbug said...

Most of the parts, (motors, transistors, resistors and sometimes LEDS) can be found in small infrared toy helicopters.

Anonymous said...

IS that a capacitor??? what is the value of that and its connection in the circuit?? Thank you

Anonymous said...

is that a capacitor that i saw in the pictures??? what is the value of that and its connection to the circuit?? Thank you

ME said...

@Anonymous -
Yes, there is a capacitor stuck on the side in the picture. It is 0.1uF and it is across the battery terminals. I guess I left that out of the schematic. My newer designs have a 10uF smd capacitor.

Anonymous said...

Im experiencing a problem making it, did you change any codes that you uploaded or is it the working code. And what is the use of the capacitor?? thank you

ME said...

@Anonymous -
Thank you for trying to make one. This code is pretty old, but I think it should work. What kind of problems are you having? Did you remember to program the fuses(burn the bootloader)?
The capacitor is just to make the power from the battery a little more stable. Honestly it isn't necessary, but it doesn't hurt. But use a higher value than I did here, something in the 1~10uF range.

Anonymous said...

im having a problem in sensing, is it okay to use 2n7002 instead of 2n7002k? and when i change the value of lsp and rspd from 17 to 175 the motor goes faster. because when it is in 17 the motor is very slow. Thank you.

Anonymous said...

im having a problem in sensing, is it okay to use 2n7002 instead of 2n7002k? and when i change the value of lsp and rspd from 17 to 175 the motor goes faster. because when it is in 17 the motor is very slow. Thank you. and by the way how to burn the fuses?? is it in arduino ide?

ME said...

@Anonymous -
Yes, the 2n7002 should work I think.
lspd and rspd are the pwm duty cycle values. Of course 175/255 is larger than 17/255 so it will go faster. You can set it to whatever works best for you.
If you have the boards file from the MIT link in the post, you can connect it to arduino running the ISP sketch, select the correct board, ATtiny85 8MHz, and click "burn bootloader" in one of the menus. That sets up the board to work as expected.
As for sensing, it's difficult to know exactly what's wrong. Experiment with it a bit. See if the expected voltages are coming from your sensor circuits. See what happens when you place it on different surfaces.

Anonymous said...

should i burn the boot loader or upload it or both?? when i burn the boot loader the applicatiion doesnt respond only says burning the boot loader and took a minute. thank you

Anonymous said...

is it okay to use the normal resistors or smd resistors must be used??Thank You

ME said...

@Anonymous -
If you click "upload" it will try to upload your attiny sketch to the arduino. That's not what you want to do. Click "burn bootloader" in one of the menus. Then click "upload using programmer" in another menu to upload the sketch to the ATtiny. The MIT site I linked to has a tutorial about it I think.
You can use any type of resistors you want. The only important thing is the ohm value.

Anonymous said...

IS IT OKAY TO USE CR2032, and how many? because we are having a poblem in operating the two motors. Thank you.

ME said...

@Anonymous -
Yes, you could use CR2032, but the voltage is a little different. I think CR2032 is 3V while LIR2032 is 3.7V. Have you tried changing the pwm values for the motors(lspd, rspd)? Perhaps the values I used were too low for your setup.

Anonymous said...

what software did you use to program it? and how you put the program to the ic?

Shawn The Best said...

so where i have to put the strings and how i can attach the computer and the ATtiny85 without an arduino?

HarryMaster said...

Good Day,
I am Harry from the KS U.K School of robotics. We are willing to pay you £6.00 per robot if you are willing to make 120 units and sell them to us.
Please email kingsonline@gmail.com ASAP.

Yours Sincerely
KS U.K School of robotics
Headmaster

Anonymous said...

Hello, what are the fuse bits for ATtiny85 ?:)

Anonymous said...

hello sir, i am having an error "arduino ISP error" so many of them. how can i fix it?

Anonymous said...

Hello sir, your project very well done, good job. I like to try to do the same project to school, can you send me more details for it? My Gmail is velovskidaniel@Gmail.com

Me said...

@Anonymous -
Sorry for the lack of replies here. I have posted some more details and a list of parts on a Hackaday project page: https://hackaday.io/project/581-tiny-robot-family

Anonymous said...

Dear Sir,
I have not ATTINY85, could I use ATTINY13A for this bot?
Thank in advance!

umbro said...

very good,
Do you use Arduino to program it..?

Unknown said...

Can you give me your smd resistor data sheet ?

Kimball said...

Looking at your schematic, are you sure that is how you wired the phototransistors? I think the load resistor should be from V+ to drain, and the source should be connected to ground.

Kimball said...

One other thing, your code says high is off for the LED, which suggests the pin is sinking current (which is better to do anyways, as the tiny pins can sink more current than source), but the schematic shows the LED being sourced by the pin.

Anonymous said...

I might be wrong and I apologize if so, but I think the pin definition

#define rsensepin 1 //ADC1 pin 7

should be

#define rsensepin 2 //ADC1 pin 7

since ADC1 is on PB2.

I'm not sure since this is my first time using an ATtiny85
and it seemed to work either way.
Nevertheless, a fun project. Thank you !

Matthew C said...

I was able to make one of these and it works great. i just had to modify the code a bit and change the motor speed from 17 to about 65 and the bigturn to 250 the small turn to 100 and steplength to 200, running at 16mhz.
also to the other person that said the pin labeling is wrong, you're right.

mine was working for a day or two and stopped. it would flash and then soon as it sent power to the motors the attiny would reset. so I added the capacitor of 10uf to the two battery termials and that fixed it.
originally i had motors of 4 by 6 mm but they didnt have enough torque to do it so i got some 6 by 10 mm motors and those have sufficient torque.
the only problem ive noticed with the code is that the white level is only calibrated once. this is a slight problem if your track is lit by a lamp on one side. as it is brighter in some spots than others. but if you use a celling light over it then it works like a charm