Zumo

This tutorial is a work in progress, content may be updated and is subject to change.  The current project is not completely beginner friendly, but future updates will make it easier to build and program this project.

In this tutorial we will be building a small robot that is remote controlled with the Educational BoosterPack.  We are going to be using the Zumo system from Pololu. This is a nice little robot. It is not zippy but it is easy to build, simple to control, and is effective at small tasks.

You will need:

  • 2x TI LaunchPads (MSP-EXP430F5529LP is recommended but any model will do fine)
  • 1x Educational BoosterPack
  • 2x CC110L Sub-1GHz RF BoosterPacks
  • 1x Zumo chassis
  • 2x gearmotors
  • 1x Zumo blade (optional)
  • 1x Motor Driver (SN754410 recommended but any way to drive two small DC motors will work)
  • Soldering Iron (for the gear motor wires)
  • Super glue (for the battery holder terminals)

There are two parts to this project: The Robot and the controller. We will address the robot first.

For the robot, we will first need to assemble our zumo chassis. It is pretty straight forward. Open the packaging and screw the laser cut top to the main body with the battery holder. You can also attach the rear wheels and axle. For the front wheels, we need to attach those to the gearmotors. They are keyed to go in one way. Next we should solder on our connection wires to the gearmotor terminals. You can use a male/male black jumper wire and a male/male red jumper wire to make it clear later on. Cut the two wires in half and then use a wire stripper to expose the wire on the cut ends. Solder the cut ends to the gearmotor terminals.

Now put the finished gearmotors into the chassis. Now you can attach the treads that connect the front and back wheels. If you have the Zumo blade, you can attach that now and bend it so that it is at a good angle for bull dozing. You can also put in your battery holder parts. You will need to super glue the end terminals of the battery holder. Make sure they ends can stick up through the laser cut top so we can access them from above later. Also make sure your motor leads are sticking out from the top so we can use them.

Now we can add our LaunchPad, our radio module, and out motor driver to the chassis.

 

Zumo Robot Code

/**
 *  WirelessTest - test transceiver sketch using AIR430Boost FCC driver.
 *  Copyright (C) 2012-2013 Anaren Microwave, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 * 
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  This example demonstrates usage of the AIR430BoostETSI library which uses
 *  the 430Boost-CC110L AIR Module BoosterPack created by Anaren Microwave, Inc.
 *  and available through the TI eStore, for the European Union.
 *
 *  ----------------------------------------------------------------------------
 *
 *  Note: This file is part of AIR430Boost.
 *
 *  ----------------------------------------------------------------------------
 *
 *  Description
 *  ===========
 *
 *  Each radio will send a message consisting of: 1 byte counter, 5 byte static 
 *  text. The counter will count from 0 to 9 and will rollover. Each radio will 
 *  wait in receive mode for approximately one second. Upon receiving data, or 
 *  timeout of one second, the radio receive function will return. If valid data 
 *  was received, the radio's receiverOn() method will return the number of bytes
 *  that were received. In this example, the data can be monitored on the serial 
 *  port (please refer to printTxData() and printRxData() functions).
 *
 *  ----------------------------------------------------------------------------
 * 
 *  This example assumes that two BoosterPacks will be used to showcase the 
 *  wireless radio communication functionality. This same code should be 
 *  programmed to both LaunchPad development kits.
 *
 *  This BoosterPack relies on the SPI hardware peripheral and two additional 
 *  GPIO lines for SPI chip-select and GDO0 for packet handling. They use pins 18 
 *  and 19 respectively. 
 *
 *  In the default configuration, this BoosterPack is not compatible with an 
 *  external crystal oscillator. This can be changed, if necessary, and would
 *  require reconfiguration of the BoosterPack hardware and changes to the 
 *  AIR430BoostFCC library. Refer to the BoosterPack User's Manual if necessary.
 *
 *  For complete information, please refer to the BoosterPack User's Manual available at:
 *  https://www.anaren.com/air/cc110l-air-module-boosterpack-embedded-antenna-module-anaren
 *  
 *  To purchase the 430Boost-CC110L AIR module BoosterPack kit, please visit the TI eStore at:
 *  https://estore.ti.com/430BOOST-CC110L-CC110L-RF-Module-BoosterPack-P2734.aspx
 */

// The AIR430BoostFCC library uses the SPI library internally. Energia does not
// copy the library to the output folder unless it is referenced here.
// The order of includes is also important due to this fact.
#include <SPI.h>
#include <AIR430BoostFCC.h>

/* Sample code for SN754410 Half H Bridge Motor Driver */

// Two motors can be driven by the device
// The driver let's you control the motor in forward and reverse
// Pins 1A, 2A, 3A, 4A are logic pins
// Pins 1Y, 2Y, 3Y, 4Y are where the motors are hooked up
// VCC1 is the controller VCC, VCC2 is the motor power supply
// Pins 1,2EN and 3,4EN are enable pins, these should be set HIGH, can connect to VCC directly
// Use digitalWrite() to do full speed or PWM with analogWrite()


#define MOTOR_INPUT_1A 9
#define MOTOR_INPUT_2A 10
#define MOTOR_INPUT_3A 11
#define MOTOR_INPUT_4A 12
#define MOTOR_INPUT_EN1 34
#define MOTOR_INPUT_EN2 34
#define MOTOR_INPUT_EN3 35
#define MOTOR_INPUT_EN4 35

// -----------------------------------------------------------------------------
/**
 *  Global data
 */

// Data to write to radio TX FIFO (60 bytes MAX.)
unsigned char txData[6] = { 'S', 'A', 'i', 'r', '!', '\0' };    

// Data to read from radio RX FIFO (60 bytes MAX.)
unsigned char rxData[6] = { 'S', 'S', 'S', '\0', '\0', '\0' };


// -----------------------------------------------------------------------------
// Debug print functions

void printTxData()
{
  Serial.print("TX (DATA): ");
  Serial.println((char*)txData); 
}

void printRxData()
{
  /**
   *  The following illustrates various information that can be obtained when
   *  receiving a message. This includes: the received data and associated 
   *  status information (RSSI, LQI, and CRC_OK bit).
   */
  Serial.print("RX (DATA, RSSI, LQI, CRCBIT): ");
  Serial.print("(");
  Serial.print((char*)rxData);
  Serial.print(", ");
  Serial.print(Radio.getRssi());
  Serial.print(", ");
  Serial.print(Radio.getLqi());
  Serial.print(", ");
  Serial.print(Radio.getCrcBit());
  Serial.println(")");
}

// -----------------------------------------------------------------------------
// Main example

void setup()
{
  // The radio library uses the SPI library internally, this call initializes
  // SPI/CSn and GDO0 lines. Also setup initial address, channel, and TX power.
  Radio.begin(0x01, CHANNEL_4, POWER_MAX);

  // Setup serial for debug printing.
  Serial.begin(9600);
  
  pinMode(MOTOR_INPUT_1A, OUTPUT);
  pinMode(MOTOR_INPUT_2A, OUTPUT);
  pinMode(MOTOR_INPUT_3A, OUTPUT);
  pinMode(MOTOR_INPUT_4A, OUTPUT);
  pinMode(MOTOR_INPUT_EN1, OUTPUT);
  pinMode(MOTOR_INPUT_EN4, OUTPUT);
  
  digitalWrite(MOTOR_INPUT_1A, LOW);
  digitalWrite(MOTOR_INPUT_2A, LOW);
  digitalWrite(MOTOR_INPUT_3A, LOW);
  digitalWrite(MOTOR_INPUT_4A, LOW);
  digitalWrite(MOTOR_INPUT_EN1, HIGH);
  digitalWrite(MOTOR_INPUT_EN4, HIGH);

  Serial.println("Robot is listening...");
  /**
   *  Setup LED for example demonstration purposes.
   *
   *  Note: Set radio first to ensure that GDO2 line isn't being driven by the 
   *  MCU as it is an output from the radio.
   */
  pinMode(RED_LED, OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  digitalWrite(RED_LED, LOW);   // set the LED on
  digitalWrite(GREEN_LED, LOW); //
}

void loop()
{
  
  digitalWrite(GREEN_LED, HIGH);
  delay(20);
  digitalWrite(GREEN_LED, LOW);
  // Turn on the receiver and listen for incoming data. Timeout after 1 seconds.
  // The receiverOn() method returns the number of bytes copied to rxData.
  if (Radio.receiverOn(rxData, sizeof(rxData), 1000) > 0)
  {
    /**
     *  Data has been received and has been copied to the rxData buffer provided
     *  to the receiverOn() method. At this point, rxData is available. See
     *  printRxData() for more information.
     */
    digitalWrite(RED_LED, HIGH);
    printRxData();                  // RX debug information
    if(rxData[0] == 'F'){ // motors forward
      digitalWrite(MOTOR_INPUT_1A, LOW);   // set leg 1 of the H-bridge low
      digitalWrite(MOTOR_INPUT_2A, HIGH);  // set leg 2 of the H-bridge high
      digitalWrite(MOTOR_INPUT_3A, LOW);   // set leg 1 of the H-bridge low
      digitalWrite(MOTOR_INPUT_4A, HIGH);  // set leg 2 of the H-bridge high
    }
    else if(rxData[0] == 'L'){ // pivot left
      digitalWrite(MOTOR_INPUT_1A, LOW);   // set leg 1 of the H-bridge low
      digitalWrite(MOTOR_INPUT_2A, HIGH);  // set leg 2 of the H-bridge high
      digitalWrite(MOTOR_INPUT_3A, HIGH);   // set leg 1 of the H-bridge low
      digitalWrite(MOTOR_INPUT_4A, LOW);  // set leg 2 of the H-bridge high
    }
    else if(rxData[0] == 'R'){ // pivot right
      digitalWrite(MOTOR_INPUT_1A, HIGH);   // set leg 1 of the H-bridge low
      digitalWrite(MOTOR_INPUT_2A, LOW);  // set leg 2 of the H-bridge high
      digitalWrite(MOTOR_INPUT_3A, LOW);   // set leg 1 of the H-bridge low
      digitalWrite(MOTOR_INPUT_4A, HIGH);  // set leg 2 of the H-bridge high
    }
    else if(rxData[0] == 'B'){ // motors backward
      digitalWrite(MOTOR_INPUT_1A, HIGH);  // set leg 1 of the H-bridge high
      digitalWrite(MOTOR_INPUT_2A, LOW);   // set leg 2 of the H-bridge low
      digitalWrite(MOTOR_INPUT_3A, HIGH);  // set leg 1 of the H-bridge high
      digitalWrite(MOTOR_INPUT_4A, LOW);   // set leg 2 of the H-bridge low
    }
    else{ // motors stop
      digitalWrite(MOTOR_INPUT_1A, LOW);  // set leg 1 of the H-bridge high
      digitalWrite(MOTOR_INPUT_2A, LOW);   // set leg 2 of the H-bridge low
      digitalWrite(MOTOR_INPUT_3A, LOW);  // set leg 1 of the H-bridge high
      digitalWrite(MOTOR_INPUT_4A, LOW);   // set leg 2 of the H-bridge low
    }
  }
  digitalWrite(RED_LED, LOW);
}

 

In this section we will talk about how to build the controller. You will need a LaunchPad, an Educational BoosterPack MK II, a CC110L BoosterPack.

 

Robot Wireless Controller

/**
 *  WirelessTest - test transceiver sketch using AIR430Boost FCC driver.
 *  Copyright (C) 2012-2013 Anaren Microwave, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 * 
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  This example demonstrates usage of the AIR430BoostETSI library which uses
 *  the 430Boost-CC110L AIR Module BoosterPack created by Anaren Microwave, Inc.
 *  and available through the TI eStore, for the European Union.
 *
 *  ----------------------------------------------------------------------------
 *
 *  Note: This file is part of AIR430Boost.
 *
 *  ----------------------------------------------------------------------------
 *
 *  Description
 *  ===========
 *
 *  Each radio will send a message consisting of: 1 byte counter, 5 byte static 
 *  text. The counter will count from 0 to 9 and will rollover. Each radio will 
 *  wait in receive mode for approximately one second. Upon receiving data, or 
 *  timeout of one second, the radio receive function will return. If valid data 
 *  was received, the radio's receiverOn() method will return the number of bytes
 *  that were received. In this example, the data can be monitored on the serial 
 *  port (please refer to printTxData() and printRxData() functions).
 *
 *  ----------------------------------------------------------------------------
 * 
 *  This example assumes that two BoosterPacks will be used to showcase the 
 *  wireless radio communication functionality. This same code should be 
 *  programmed to both LaunchPad development kits.
 *
 *  This BoosterPack relies on the SPI hardware peripheral and two additional 
 *  GPIO lines for SPI chip-select and GDO0 for packet handling. They use pins 18 
 *  and 19 respectively. 
 *
 *  In the default configuration, this BoosterPack is not compatible with an 
 *  external crystal oscillator. This can be changed, if necessary, and would
 *  require reconfiguration of the BoosterPack hardware and changes to the 
 *  AIR430BoostFCC library. Refer to the BoosterPack User's Manual if necessary.
 *
 *  For complete information, please refer to the BoosterPack User's Manual available at:
 *  https://www.anaren.com/air/cc110l-air-module-boosterpack-embedded-antenna-module-anaren
 *  
 *  To purchase the 430Boost-CC110L AIR module BoosterPack kit, please visit the TI eStore at:
 *  https://estore.ti.com/430BOOST-CC110L-CC110L-RF-Module-BoosterPack-P2734.aspx
 */

// The AIR430BoostFCC library uses the SPI library internally. Energia does not
// copy the library to the output folder unless it is referenced here.
// The order of includes is also important due to this fact.
#include 
#include 

#define FORWARD 0xFF
#define LEFT 0xF0
#define RIGHT 0x0F
#define STOP 0x00

//MODE
#define JOY 0x00
#define ACC 0x01
#define FIRST 0x00
#define SECOND 0x01

const int RGB_BLUE = 37;
const int RGB_GREEN = 38;
const int RGB_RED = 39;
const int accY = A1;  // Forward >= 2500
const int accX = A0;  // Left <= 1700  ||  Right >=  2350
const int joyX = A5;
const int joyY = A3;
// -----------------------------------------------------------------------------
/**
 *  Global data
 */

// Data to write to radio TX FIFO (60 bytes MAX.)
unsigned char txData[6] = { 'S', '\0', '\0', '\0', '\0', '\0' };
unsigned int xPosition = 0;
unsigned int yPosition = 0;
unsigned char lastPosition = 0;
int mode = JOY;
int channel = FIRST;
// -----------------------------------------------------------------------------
// Debug print functions

void printTxData()
{
  Serial.print("TX (DATA): ");
  Serial.println((char*)txData); 
}

// -----------------------------------------------------------------------------
// Main example

void setup()
{
  Serial.begin(9600);
  delay(100);
  // The radio library uses the SPI library internally, this call initializes
  // SPI/CSn and GDO0 lines. Also setup initial address, channel, and TX power.
  Radio.begin(0x02, CHANNEL_4, POWER_MAX);

  pinMode(32, INPUT_PULLUP);  // Switch A (Bottom)
  pinMode(33, INPUT_PULLUP);  // Switch B (Top)
  pinMode(RGB_BLUE, OUTPUT);
  pinMode(RGB_GREEN, OUTPUT);
  pinMode(RGB_RED, OUTPUT);
  pinMode(RED_LED, OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  digitalWrite(RED_LED, LOW);
  digitalWrite(GREEN_LED, LOW);

  Serial.println("JOY MODE // GREEN");
  digitalWrite(RGB_GREEN, HIGH);
  digitalWrite(RGB_BLUE, LOW);
  
  // Setup serial for debug printing.

  
  /**
   *  Setup LED for example demonstration purposes.
   *
   *  Note: Set radio first to ensure that GDO2 line isn't being driven by the 
   *  MCU as it is an output from the radio.
   */
  Serial.println("ROBOT CONTROLLER");
  pinMode(RED_LED, OUTPUT);
  digitalWrite(RED_LED, LOW);   // set the LED on
}

void loop()
{
  
  int button32State = 0;
  int button33State = 0;
  button32State = digitalRead(32);
  button33State = digitalRead(33);
  if(button32State == 0){
    delay(150);
    if(mode == JOY){
      mode = ACC;
      Serial.println("ACC MODE // BLUE");
      digitalWrite(RGB_BLUE, HIGH);
      digitalWrite(RGB_GREEN, LOW);

    } else{
      mode = JOY;
      Serial.println("JOY MODE // GREEN");
      digitalWrite(RGB_GREEN, HIGH);
      digitalWrite(RGB_BLUE, LOW);

    }
  }
  if(button33State == 0){
    delay(150);
    Serial.println("Switching Channels");
    if (channel == FIRST) {
      channel = SECOND;
      Radio.end();
      Radio.begin(0x01, CHANNEL_4, POWER_MAX);
    } else {
      channel = FIRST;
      Radio.end();
      Radio.begin(0x01, CHANNEL_1, POWER_MAX);
    }
    digitalWrite(RGB_BLUE, LOW);
    digitalWrite(RGB_GREEN, LOW);
    digitalWrite(RGB_RED, HIGH);
    delay(300);
    digitalWrite(RGB_RED,LOW);
    if(mode == JOY){
      Serial.println("JOY MODE // GREEN");
      digitalWrite(RGB_GREEN, HIGH);
      digitalWrite(RGB_BLUE, LOW);

    } else{
     
      Serial.println("ACC MODE // BLUE");
      digitalWrite(RGB_BLUE, HIGH);
      digitalWrite(RGB_GREEN, LOW);
    }
  
  }
  if(mode == JOY){
    xPosition = analogRead(joyX);
    yPosition = analogRead(joyY);
    Serial.print("JOY_X = ");  // LEFT <= 100  |||  RIGHT >= 4000
    Serial.print(xPosition);
    Serial.print(" | JOY_Y = "); // UP >= 4000  |||  DOWN <= 100     Serial.println(yPosition);     lastPosition = txData[0];     if(yPosition >= 4000){ // FORWARD!
      txData[0] = 'F';
    }
    else if(xPosition >= 4000){ // RIGHT
      txData[0] = 'R';
    }
    else if(xPosition <= 100){  // LEFT
      txData[0] = 'L';
    }
    else if(yPosition <= 100){  // LEFT
      txData[0] = 'B';
    }
    else{  // STOP
      txData[0] = 'S';
    }
  }
  else{
    xPosition = analogRead(accX);
    yPosition = analogRead(accY);
    Serial.print("accX = ");  // LEFT <= 100  |||  RIGHT >= 4000
    Serial.print(xPosition);
    Serial.print(" | accY = "); // UP >= 4000  |||  DOWN <= 100     Serial.println(yPosition);     lastPosition = txData[0];     if(yPosition >= 2500){ // FORWARD!
      txData[0] = 'F';
    }
    else if(xPosition >= 2350){ // RIGHT
      txData[0] = 'R';
    }
    else if(xPosition <= 1700){  // LEFT
      txData[0] = 'L';
    }
    else if(yPosition <= 1700){  // LEFT
      txData[0] = 'B';
    }
    else{  // STOP
      txData[0] = 'S';
    }
  }

  // Load the txData into the radio TX FIFO and transmit it to the broadcast
  // address.
  if(lastPosition != txData[0]){
    Radio.transmit(ADDRESS_BROADCAST, txData, 6);
    printTxData();                    // TX debug information
    digitalWrite(RED_LED, HIGH);
  }
  else {
    digitalWrite(RED_LED, LOW);
  }
  
}

Hopefully your final project looks something like this!