Project tutorial

Lego Stepperbot © LGPL

Play around with Lego, Arduino, stepper motors, IR, and piezo beepers! Blip, blop!

  • 1,319 views
  • 0 comments
  • 5 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
×1
TSOP 4838 IR-Receiver
×1
Generic Piezo buzzer/speaker
×1
28BYJ-48 Stepper Motor with Driver
×2
Tens70
9V battery (generic)
×1
Generic IR-remote control
×1

About this project

Once upon a time (2015) I was playing around with Lego Mindstorms together with my kids. Inspired by the successes of a friend to inspire his kids, I thought building sumo wrestling robots would be the right thing to do.

There was a problem though. We only had one Lego Mindstorms kit, and it takes two to tango. So, I went in to "dad can build one" mode. I challenged my kids to build and program the Lego robot and I would kick their butt in sumo wrestling with my soon to be built bot! Long story short: my bot lost miserably, it was severely out-powered and pushed into its misery outside of the ring. Kids loved to make fun of how slow my bot was...

Well, since I had build the needed parts and actually had a robotic platform up and running, I thought I use it for something else. I had seen a cute little robot (can't remember what it was called) in a robotics workshop for kids that would allow you to "program" the robot by entering a sequence of moves and then have the robot follow them. I thought this would be a good new life for the sad sumo!

This is how we roll.

Stepper Motor Lego Brick

Most important piece of hardware is the stepper motor Lego brick. I literary had dozens of 28BYJ-48 stepper motors laying around because of another project. So of course I 3D-printed Lego adapters for them! As with any attempt at printing Lego you can get it ok-ish but never to fit as nicely as the originals.

I wanted to make the stepper brick compact and versatile in true Lego spirit. I would say it turned out ok, but not as well as I had hoped. At a footprint of 6x8 it is a lot bigger than desired, limiting the use to quite large Lego builds. Integrating it into a build is fairly convenient thanks to the holes on the sides allowing Lego pin connectors to get a solid interface with other bricks.

I printed the brick in ABS plastic on my old Makerbot Thing-O-Matic, and apart from some bent corners it is quite ok.

IR Interface FTW

Using IR as an interface for the Arduino Uno is just fantastic. Seriously it's great. I kid you not, best thing, since sliced bread, or cheese, you get it. Best. Thing. Ever.

Let me put it this way:

  • Cheaper than mechanical buttons (you get 10 receivers for $5 on ebay)
  • You can plug the TSOP4838 right into the Arduino board by spreading its legs a little and using A0 pin as input.
  • I bet you have at least a few IR-remotes laying round to repurpose or borrow!

I Wanna Hear You MAKE SOME NOISE!

Having your build emit sounds is a must. Why?

  • What would R2D2 be without sounds?
  • It I GREAT to have audible feedback when pressing buttons to you know that you actually pressed the button.
  • It is trivial to do, just connect a buzzer to GND and an IO-pin.

As for making the actual sounds you can use the existing "tone" functions that are part of the Arduino standard libraries, or you can roll your own code. In this project I wrote my own code for making the beep because the library routine uses a timer that was also used by the IR library. As can be seen in the code, it's not really rocket science.

Conclusions?

Well, it's always fun playing with Lego. Building things with Arduino is also fun. Not to mention robotics, that's fun as well. Oh, and 3D-printing stuff. And making things sound like R2D2!

I think I might want to build a more compact Lego motor brick at some point. Would be nice to be able to build a small Lego robot. Like a 4x8 Lego car driving down the Lego streets. That would be nice...

UPDATE: Made a Smaller Version!

I couldn't keep myself from making the robot more compact. Same robot, smaller footprint.

The smaller bot was made smaller by reducing the size of the stepper-to-Lego-adapters. The new adapters are 6x4. To make the axis stable it was offset from the motor axis and two 8-tooth gears was added. Since one of the gears needed to go directly onto the motor, I had to make it (and while at it I made the other one as well). 3D-printing a small Lego gear is more than my printer will handle, so I cut them with laser from acrylic instead (yes, I am a lucky bastard that have a laser).

The smaller adapters no longer house the drivers for the steppers, so they had to be somewhere else. Digging in my pile of unused stuff, I found a Nano to use instead of the Uno. Quite conveniently you can solder some ULN2803 driver IC:s right onto the edges of the Nano board. Bending and cutting some legs of course... IR-receiver was soldered to D13-3.3V-A0, and D13 driven low to act as ground :-). The buzzer got to live on the back of the board, soldered to D10-D12. I'd say this counts as a "proper" hack.

Conclusions for this smaller bot? It's smaller. It was fun. It works. It breaks. Don't make gears from acrylic... Need to re-design to not use such small fragile gears.

Code

LegoStepperBotC/C++
The code for the stepper bot!
#include <IRremote.h>

IRrecv irrecv(A0);
decode_results results;

//delay between subsequent IR reads, to avoid getting muliple presses
#define IR_DELAY 250  

//last ir decoded, micros
long lastIR = 0; 

#define MOVE_STEPS 2000
#define TURN_STEPS 3000
#define STEP_DELAY 1000

//I can never make up my mind on the step sequence. Both works obviously, need to measure them properly some day to see what really works best.
byte stepSequence[8] = {B1100, B1100, B0110, B0110, B0011, B0011, B1001, B1001};  
//byte stepSequence[8] = {B1000, B1100, B0100, B0110, B0010, B0011, B0001, B1001};

byte leftPins[4]  = {2, 3, 4, 5};
byte rightPins[4] = {6, 7, 8, 9};

//variables keeping track of current position of steppers
long currentLeft=0, currentRight=0;

//steps left to step, positive or negative
long stepLeft=0, stepRight=0;

//when was the last step done, micros
long lastStepChange = 0; 

//this is where the piezo beeper sits
#define TONE_PIN 11

void setup() {
    for(int pin=0 ; pin<4 ; pin++) {
      pinMode(leftPins[pin], OUTPUT);
      pinMode(rightPins[pin], OUTPUT);
    } 

  irrecv.enableIRIn(); 
  
  Serial.begin(9600);
  Serial.println("Hello Serial!");

  pinMode(TONE_PIN, OUTPUT);  
}

/*
 * The stepping function. Will step _one_ step in the direction specified by stepsToGo and update variables accordingly
 */
void step(long* stepsToGo, long *currPos, byte *pins) {
  
  if(*stepsToGo != 0) {
      if(*stepsToGo > 0) {
         (*stepsToGo)--;
         (*currPos)++; 
      }
      else {
         (*stepsToGo)++;
         (*currPos)--;                
      }
  
      byte mask = stepSequence[*currPos & 0x7];
      for(int bit=0 ; bit<4 ; bit++) {        
        digitalWrite(pins[bit], mask & (B1000 >> bit));
      }
      lastStepChange = micros();     
  }
  else {    
      //switch off all pins to save power when idle
      for(int bit=0 ; bit<4 ; bit++) {        
        digitalWrite(pins[bit], 0);
      }
  }
}

//some nice frequencies for sound
int tones[] = {262, 294, 330, 349};

//moves are stored in the "moves" queue
typedef enum {FWD, BACK, LEFT, RIGHT} move;
move moves[100];
int nMoves = 0, currMove = 0;

bool run = false;

/*
 * Simplistic tone generation function. Have this to avoid using timers that conflict with other libraries. 
 * f is frequency in hz
 * d is duration in milliseconds
 */
void myTone(int f, int d) {
  bool state = 0;
  unsigned long now = micros();
  unsigned long stopAt = now + d*(long)1000;
  unsigned long dt = 1000000 / (2*(long)f);  //dt is for the double frequency  
  unsigned long nextChange = now+dt;

  while (stopAt > micros()) {
    if(micros() > nextChange) {
      digitalWrite(TONE_PIN, state ? HIGH : LOW);
      nextChange += dt;
      state = !state;
    }    
  }
  digitalWrite(TONE_PIN, LOW); //end on a low (assuming beeper is connected to GND on the other end)
}

/*
 * The allmighty loop function.
 * Does three things:
 * - step the steppers (one step at a time).
 * - update how steppers should run according to move queue.
 * - read IR to program the move queue and start/stop things.
 */
void loop() {
   if(micros()-lastStepChange > STEP_DELAY) {
      step(&stepLeft, &currentLeft, leftPins);
      step(&stepRight, &currentRight, rightPins);
   }     

   if(run) {
      if(currMove == nMoves) {
        nMoves = currMove = 0;         
        run = false;        
      }
      else if(currMove < nMoves && stepLeft == 0 && stepRight == 0) {
        switch(moves[currMove++]) {
          case FWD:
             Serial.println("FWD");
             stepLeft += MOVE_STEPS;
             stepRight += MOVE_STEPS;         
             break;         
           case BACK:
             Serial.println("BACK");
             stepLeft -= MOVE_STEPS;
             stepRight -= MOVE_STEPS;         
             break;         
           case LEFT:
             Serial.println("LEFT");
             stepLeft += TURN_STEPS;
             stepRight -= TURN_STEPS;         
             break;         
           case RIGHT:
             Serial.println("RIGHT");
             stepLeft -= TURN_STEPS;
             stepRight += TURN_STEPS;         
             break;         
        }
      }      
   }
   
  if (micros()-lastIR > IR_DELAY && irrecv.decode(&results)) {         
    switch(results.value) {
       case 0x1FE10EF: moves[nMoves++] = FWD; myTone(tones[FWD], 100); break; 
       case 0x1FEB04F: moves[nMoves++] = BACK; myTone(tones[BACK], 100); break; 
       case 0x1FE50AF: moves[nMoves++] = LEFT; myTone(tones[LEFT], 100); break; 
       case 0x1FEF807: moves[nMoves++] = RIGHT; myTone(tones[RIGHT], 100); break; 
       case 0x1FE7887: //start running! 
            run=true;
            Serial.print("Start! moves="); 
            Serial.println(nMoves);
            //play through the sounds of the queue, just because blip blop...
            for(int i=0; i<nMoves; i++) {
              myTone(tones[moves[i]], 100);
              delay(50);
            }
            break; 
       case 0x1FE48B7: //Stop all and clear queue
            nMoves=currMove=0;
            stepLeft=stepRight=0;
            run=false;
            break; 
       default:
         //this printout will give you the hex-values for the keys on your remote, just update the cases above with your values
         Serial.println(results.value, HEX);
     }
    irrecv.resume(); 
    lastIR = micros();     
  }

}

Custom parts and enclosures

Lego Stepper Mount
Lego axis adapter
adapter from lego axis to stepper motor axis
Stepper Mount source
OpenSCAD code for the stepper mount lego piece. Also functions for printing other basic lego compatible pieces.
lego_stepper_frame_bVZCqW4d2d.scad
Axis adapter source
lego_stepper_axis_zjzwiD6EbQ.scad

Schematics

Schematics
Hand drawn schematic, not even a ruler. Enjoy!
Img 0196 ash9oewagy

Comments

Similar projects you might like

Arduino Bluetooth Basic Tutorial

by Mayoogh Girish

  • 455,654 views
  • 42 comments
  • 240 respects

Home Automation Using Raspberry Pi 2 And Windows 10 IoT

Project tutorial by Anurag S. Vasanwala

  • 285,671 views
  • 95 comments
  • 672 respects

Security Access Using RFID Reader

by Aritro Mukherjee

  • 230,828 views
  • 38 comments
  • 241 respects

OpenCat

Project in progress by Team Petoi

  • 197,272 views
  • 154 comments
  • 1,372 respects
Add projectSign up / Login