The controller for the robot is a chipKIT Max32, which is an arduino-compatible board with a PIC32 microcontroller on board. The specific microcontroller on the board is a PIC32MX795F512, which is relevant when looking for specific hardware features such as timers and PWM.

Power comes from a 7.4V Li-ion battery.

Flashing a program

The trace under JP5 has been deliberately cut on the board. This jumper connects the DTR line of the RS232 interface (from the FTDI232RQ chip that tunnels RS232 over USB), to the reset pin of the microcontroller. The flash programmer normally uses this line to send the chip into the bootloader, so its code can be changed.

Unfortunately, this line is also unconditionally asserted when plugged into a computer, making it impossible to attach to a long-running program. The fact that this is a problem at all is a design flaw in the Max32 - were the CTS line used instead, there would be no problem.

To reprogram the board then, JP5 must be temporarily closed. This could be done with a jumper, but this requires that the top layer of circuit board be removed. In practive, this is best done using a screwdriver to short the two pins.


Do not reprogram the board while the battery is attached! Shorting JP5 is clumsy, and has a risk of shorting other parts of the board. Your USB port is probably protected against this, but the battery may do unspeakably bad things.


There are a pair of motors on the unicycle, driver with some custom and unfortunately poorly-documented hardware. The interface to the microcontroller is a pair of pwm lines, one for the forward direction, and one for the reverse direction.

Each motor is a Maxon 110134 with a Maxon 134158 gearbox attached. These motors also have attached Encoders.


void setMotorTurntable(float cmd)

Set the speed of the turntable.

  • cmd: The fraction of maximum speed, in [-1 1]. Positive is counter-clockwise around the positive Z axis

void setMotorWheel(float cmd)

Set the speed of the wheel

  • cmd: The fraction of maximum speed, in [-1 1].

void setupMotors()

Initialize the timers and PWM needed for the motors.

void beep(float freq, int duration)

Play a tone, using the turntable motor.

Not all frequencies resonate well. The built in <ToneNotes.h> is handy for turning note names to frequencies. Octaves 6 and 7 are loud and audible.

  • freq: The frequency in Hz
  • duration: The duration in milliseconds


There is a limit switch attached to the top of the robot, for use as basic human input. This switch must be connected to a CN pin, as only those pins support pull-up resistors.


The robot has a combined accelerometer and gyro board that is sold by Sparkfun. The accelerometer is an ADXL345, and the gyroscope is an ITG-3200.

Both of these sensors use the I2C protocol - in fact, they share a bus. Unfortunately, the builtin arduino Wire interface that implements this protocol does not appear to work on our microcontroller board.


void gyroAccelSetup()

Initialize the connection to the accelerometer and gyro.

template <typename T>
Vector3<T> chipToRobotFrame(Vector3<T> v_chip)

Convert from the chip coordinate frame to the robot frame.

The robot frame has x pointing forwards (the side with the microcontroller) z pointing left y pointing up

template <typename T>
Vector3<float> gyroRawToSI(Vector3<T> raw)

Convert from the raw chip reading to radians per second, in the robot frame.

template <typename T>
Vector3<float> accRawToSI(Vector3<T> raw)

Convert from the raw chip reading to meters per second squared, in the robot frame.

Vector3<int16_t> accelReadRaw()

Read the raw values of the accelerometer, in internal frame and units.

Vector3<float> accelRead()

Get the acceleration in m s^-2, in the robot frame.

Vector3<int16_t> gyroReadRaw()

Read the raw values of the gyroscope, without subtracting initial values.

Vector3<float> gyroCalibrate(int N)

calibrate the offset for the gyro. Returns the stdev of each component

Vector3<float> gyroRead()

Get the angular velocity in the robot frame.

quat accelOrient(Vector3<float> acc)

Get the robot orientation based on the accelerometer reading. Only accurate when static

quat accelOrient()


The robot has an encoder on each motor. These go via some circuitry on the board that convert them into two lines - a tick, and a direction.

We count the ticks using the builtin hardware timers, but in order to deal with the direction reversing, we have to monitor the direction pin. We use the “change notifier” hardware to fire an interrupt whenever these pins change, and correct the sign accordingly.

Each encoder is a Maxon 201937, “Encoder MR, Type M, 512 CPT, 2 Channels, with Line Driver”.


void setupEncoders()

Initialize the hardware required by the encoders.

void resetEncoders()

Reset the counts of the encoders.

wrapping<uint16_t> getTTangle()

Get the angle of the turntable, in encoder ticks. Increases with counter-clockwise in the vertical axis

wrapping<uint16_t> getWangle()

Get the angle of the wheel, in encoder ticks. Increases with forwards motion