mbedKart

Overview

By Derek Watson and Silvan Li

mbedKart uses the mbed Shadow Robot to bring Nintendo's ingenious game, Mario Kart, to life. The mbedKart is controlled through Bluetooth, and interacts with its environment to gain various powerups and determine its progress through the map.

Import programmbedKart_sp

single player mbedKart

https://i.imgur.com/oQQms16.jpg

Controls

The controller function in the Adafruit Bluefruit app is used to control the mbedKart. Key bindings are shown as follows:

KeyAction
LeftSteer Left
RightSteer Right
1Pause
2Accelerate
4Reverse

Velocity state is tracked in its own thread alongside speed control. Velocity state can either be coasting, accelerating, or braking. Each state has its own factor that adds/subtracts from the global speed command, e.g. braking has a higher subtraction factor than coasting. Steering is done by applying a multiplier to each wheel. Upon turning right, the speed command for the right wheel is halved, and similarly for left. This allows the robot to steer realistically as opposed to turning in place. All of these factors are tunable for a personalized driving experience.

Gameplay

Under normal circumstances, the mbedKart has a limited max speed, and reaching this maximum speed is not an instantaneous process. Holding down the accelerate button (2) will gradually increase the speed of the kart until it reaches its max speed. Upon release of the accelerate button, the kart will slowly coast to a steady stop. Holding the reverse button (4) will cause the kart to gradually speed up in reverse up to the negative of its max speed. Pausing the game (1) will immediately stop the kart and will allow the kart to instantaneously return to its velocity prior to pausing.

Driving over an area that the RGB sensor detects as blue will give the mbedKart a temporary boost in speed. This boost increases the kart's max speed for 5 seconds. The race is won when the kart drives over an area the RGB sensor detects as red. Upon victory, the kart will immediately stop and perform a victory celebration with its RGB Led.

Hardware

Components

  • Shadow Robot Kit
  • mbed LPC1768
  • RGB LED
  • 3x 180-330 Ohm Resistors
  • TCS34725 RGB Sensor
  • Mini USB Breakout
  • TB6612FNG H-Bridge Motor Driver
  • Adafruit Bluetooth LE UART Module

RGB Sensor

RGB Sensormbed
LEDp30
SDAp28
SCLp27
GNDgnd
VIN3.3v

Since there is no publicly available library for the TCS34725 RGB sensor, mbedKart uses a custom class, rgbSensor. The rgbSensor class is instantiated with rgbSensor(sda, scl, led). Values return by each color's corresponding get_x() is an int that ranges from 0-32767.

rgbSensor.h

//Class to interface with the RGB color sensor over I2C
class rgbSensor
{
protected:
    I2C _i2c;
    DigitalOut _led;
    int sensor_addr;
    int value_C;
    int value_R;
    int value_G;
    int value_B;
public:
    rgbSensor(PinName,PinName,PinName);
    void update();
    int get_C();
    int get_R();
    int get_G();
    int get_B();
};

rgbSensor::rgbSensor(PinName sda, PinName scl, PinName led) :
    _i2c(sda, scl), _led(led){
    value_R = 0;
    value_G = 0;
    value_B = 0; 
    sensor_addr = 41 << 1;
        
    _i2c.frequency(200000);
    
    char id_regval[1] = {146};
    char data[1] = {0};
    _i2c.write(sensor_addr,id_regval,1, true);
    _i2c.read(sensor_addr,data,1,false);
    
    char timing_register[2] = {129,0};
    _i2c.write(sensor_addr,timing_register,2,false);
    
    char control_register[2] = {143,0};
    _i2c.write(sensor_addr,control_register,2,false);
    
    char enable_register[2] = {128,3};
    _i2c.write(sensor_addr,enable_register,2,false);

    _led = 1;
}

void rgbSensor::update()
{
        char clear_reg[1] = {148};
        char clear_data[2] = {0,0};
        _i2c.write(sensor_addr,clear_reg,1, true);
        _i2c.read(sensor_addr,clear_data,2, false);
        
        value_C = ((int)clear_data[1] << 8) | clear_data[0];
        
        char red_reg[1] = {150};
        char red_data[2] = {0,0};
        _i2c.write(sensor_addr,red_reg,1, true);
        _i2c.read(sensor_addr,red_data,2, false);
        
        value_R = ((int)red_data[1] << 8) | red_data[0];
        
        char green_reg[1] = {152};
        char green_data[2] = {0,0};
        _i2c.write(sensor_addr,green_reg,1, true);
        _i2c.read(sensor_addr,green_data,2, false);
        
        value_G = ((int)green_data[1] << 8) | green_data[0];
        
        char blue_reg[1] = {154};
        char blue_data[2] = {0,0};
        _i2c.write(sensor_addr,blue_reg,1, true);
        _i2c.read(sensor_addr,blue_data,2, false);
        
        value_B = ((int)blue_data[1] << 8) | blue_data[0];
}

int rgbSensor::get_C()
{
    return value_C;
}

int rgbSensor::get_R()
{
    return value_R;
}

int rgbSensor::get_G()
{
    return value_G;
}

int rgbSensor::get_B()
{
    return value_B;
}

Motors

Motor Drivermbed
PWMAp22
AIN2p16
AIN1p17
STBY3.3v
BIN1p19
BIN2p20
PWMBp23
GNDgnd
VM5v
VCC3.3v

Import libraryMotor

Control an H-Bridge using a PwmOut (enable) and two DigitalOuts (direction select)

In order to achieve precise and bidirectional control of the wheels, the mbedKart uses a motor H-bridge driver with the Motor library.

Bluetooth

Modulembed
CTSgnd
TXOp14
RXIp13
VIN5v
GNDgnd

The Bluetooth module uses serial TX/RX ports to communicate with the mbed. In order to read the messages, a series of getc() functions are called with their contents read individually.

Video

Potential Multiplayer Options

Although this version of mbedKart is single player only, code for this cart can be modified to support Wi-Fi communication with a central server. The example Python script below runs on an EC2 instance running Ubuntu and can be used as a simple server for multiplayer mbedKart. Karts connect to the server and periodically sends updates while the server keeps track of position within the race and can send hazards occasionally.

mbedKartServer.py

from socket import *
import json
import time

kartList = []
kartID = 0
maxKarts = 2
checkpoints = 5
end = False

def createServer():
    global kartID
    serversocket = socket(AF_INET, SOCK_STREAM)
    serversocket.bind(('localhost',9003))
    serversocket.listen(5)
    while(kartID < maxKarts):
        print('listening')
        (clientsocket, address) = serversocket.accept()
        clientsocket.settimeout(0.1)
        kart = {
                "client": clientsocket,
                "id": kartID,
                "place": 0,
                "progress": 0
                }
        kartList.append(kart)
        kartID = kartID + 1
        print clientsocket.recv(4096)
        clientsocket.send("Hello")
        clientsocket.send(str(kart['id']))
        print(str(len(kartList)) + ' player(s) have joined. Waiting on ' + str(maxKarts - len(kartList)))
    serversocket.close()


def startGame():
    global end, checkpoints
    print('starting game')
    time.sleep(1)
    for k in kartList:
        k['client'].send('S')
    time.sleep(.5)
    while not end:
        for k in kartList:
            try:
                data = k['client'].recv(8)
                if data:
                    print(data + str(k['id']))
                    if data[0] == 'U':
                        k['progress'] = k['progress'] + 1
                        if k['progress'] == checkpoints:
                            end = True
                    if data[0] == 'F':
                        pass #put game logic here
                if end:
                    k['client'].send('E')
                else:
                    k['client'].send('N')
            except:
                pass

def closeServer():
    for k in kartList:
        k['client'].shutdown(SHUT_WR)
        k['client'].close()

if __name__ == "__main__":
    createServer()
    startGame()
    closeServer()


Please log in to post comments.