Blog

October 12, 2015 | Electronics |

BLEBot - nRF51822 based BLE Robot

BLEBot

Introduction

In this project, we will build a two-wheeled robot based on the Nordic nRF51822 BLE SoC. Motor control for the two wheels of the robot will be done using an L293D chip. The robot can be controlled using any mobile device that has BLE, and when not connected, it switches to an autonomous mode, avoiding obstacles using an HC-SR04 ultrasonic sensor.

Background

Before you read further, you might want to look through some of my previous articles on nRF51822 programming, since we’re going to use similar concepts and development setup here.

Hardware & Connections

Here’s the list of hardware you need for this project:

  1. An nRF51822 Module.
  2. An nRF51822 Module Adapter (optional).
  3. L293D motor driver IC.
  4. Two wheel robot chassis with two motors.
  5. HC-SR05 ultrasonic sensor.
  6. A regulator IC like LM7805.
  7. A LiPo battery or similar which can supply enough voltage and current for the motors and the module.

The type of nRF51822 module I used is made in China, and available at sites like aliexpress, and they come with two 2x9 2 mm pitch headers. These are awfully hard to use, so I (in collaboration with my friend Sandeep) designed an adapter to make them breadboard friendly. These are optional for this project, but they are available for purchase on Tindie if you would like to check them out. In addition to making the module easy to use, our adapter also has a built-in LED and a voltage regulator.

Here’s how I hooked up the L293D and the nRF51822. (These connections are consistent with my code. If you do them differently, update the code to match.)

nRF51822 L293D Other
P0.01 1 N/A
P0.02 2 N/A
P0.03 7 N/A
P0.04 9 N/A
P0.05 15 N/A
P0.06 10 N/A
P0.07 N/A HC-SR04 Trig
P0.23 N/A HC-SR04 Echo via resistor divider
P0.21 N/A LED
GND N/A GND
VDD N/A 3.3 V
N/A 4, 5, 12, 13 GND
N/A 3, 6 motor#1
N/A 11, 14 motor#2
N/A 8, 16 VCC

Note above that HC-SR05 runs on 5V, and nRF51822 on 3.3 V. So you need to ensure that the Echo signal from HC-SR05 is reduced from 5V TTL to 3.3V TTL, and the simplest way to do that is using a resistor divider, as explained in my previous article on using HC-SR04 with nRF51822.

For power supply, I used 4 x 1.5 V alkaline batteries. I supplied 5V to the L293D using a 7805 regulator, and since my nRF51822 adapter already has a built in regulator (and an LED), so I powered it separately from a 9V battery. I recommend that you use a good capacity 7.4 V LiPo battery to power this project.

nRF51822 Chip Versions & SDK

You need to ensure that the chip version, the Nordic nRF51 SDK, and the SoftDevice versions are compatible. The two documents you need to check this are the Product Anomaly Notice and the nRF51 Series Compatibility Matrix - both available from Nordic website or a web search.

From these documents, here’s how you identify the chip:

nrf51-chip-markings

In my case, the chip on the module says QFACA2 and 1513AN. So it has 256 kB flash, 32 kB RAM, and it was made in 2015. This information is very important, and the ld file needs to be consistent with the chip. Here are the contents of my ld file:

/* Linker script to configure memory regions. */

SEARCH_DIR(.)
GROUP(-lgcc -lc -lnosys)

MEMORY
{
  FLASH (rx) : ORIGIN = 0x18000, LENGTH = 0x28000
  RAM (rwx) :  ORIGIN = 0x20002000, LENGTH = 0x6000
}

INCLUDE "gcc_nrf51_common.ld"

Here’s how you identify the chip revisions:

nrf51-chip-versions

And here’s a graphic that shows the SDK compatibility:

nrf51-SDK-compatibility

In my case, I am using the Nordic nRF51 SDK version 8.1.0 for this project.

The Code

I’ll go through the highlights of the code here, but you’ll need to go through the github link in Downloads below for the full picture. In addition to the Nordic SDK files, the code for BLEBot is organized in these files:

  • main.c
  • ble_int.h
  • ble_init.c
  • distance.h
  • distance.c

The Main Loop

Here is the main loop of BLEBot:

while(1) {
        if(is_connected()) {

            // stop if coming from an unconnected state
            if(!prevStateConnected) {
                stop();
            }

            // execute command if any 
            if(bbEvent.pending) {
                handle_bbevent(&bbEvent);
            }

            // flash LED twice quick
            nrf_gpio_pin_set(pinLED);
            nrf_delay_ms(100);
            nrf_gpio_pin_clear(pinLED);
            nrf_delay_ms(100);
            nrf_gpio_pin_set(pinLED);
            nrf_delay_ms(100);
            nrf_gpio_pin_clear(pinLED);
            nrf_delay_ms(100);

            prevStateConnected = true;
        }
        else {            

            // start moving if previous state was connected
            if (prevStateConnected) {
                set_dir(true);
                set_speed(0, 80);
                set_speed(1, 80);
            }

            // move robot autonomously
            auto_move();

            // flash LED once
            nrf_gpio_pin_set(pinLED);
            nrf_delay_ms(500);
            nrf_gpio_pin_clear(pinLED);
            nrf_delay_ms(500);

            prevStateConnected = false;
        }
    }

In the above loop, if the BLE connection is active, the robot follows instructions sent via the Nordic UART Service (NUS) from the mobile device. In this mode, it moves in reponse to left, right, stop, reverse, and start. I use the Nordic nRFToolBox app for using NUS. I also flash the LED connected to P0.21 twice quickly in each iteration of the loop.

Here’s the NUS data handler that takes action based on data that comes in via BLE.

// Function for handling the data from the Nordic UART Service.
static void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, 
                             uint16_t length)
{
  // clear events
  bbEvent.pending = false;

  if (strstr((char*)(p_data), REWIND)) {
    bbEvent.pending = true;
    bbEvent.event = eBBEvent_Left;
  }
  else if (strstr((char*)(p_data), FORWARD)) {
    bbEvent.pending = true;
    bbEvent.event = eBBEvent_Right;
  }
  else if (strstr((char*)(p_data), STOP)) {
    bbEvent.pending = true;
    bbEvent.event = eBBEvent_Stop;
  }
  else if (strstr((char*)(p_data), PLAY)) {
    bbEvent.pending = true;
    bbEvent.event = eBBEvent_Start;
    bbEvent.data = 80;
  }
  else if (strstr((char*)(p_data), SHUFFLE)) {
    bbEvent.pending = true;
    bbEvent.event = eBBEvent_Reverse;
  }
}

In the above code, the motor states are changed based on the commands that come in. (These strings are tailored to the Nordic nRF Toolbox app, but you can change them to suite your mobile app.) It’s not a good idea to do any heavy lifting from BLE event callbacks, so all I do when I get an event is set a pending flag and the event type. The actual handling is done in the main loop, as you saw before.

Here is the data structure used for events:

// events
typedef enum _BBEventType {
    eBBEvent_Start,
    eBBEvent_Stop,
    eBBEvent_Reverse,
    eBBEvent_Left,
    eBBEvent_Right,
} BBEventType;
// structure handle pending events
typedef struct _BBEvent
{
    bool pending;
    BBEventType event;
    int data;
} BBEvent;

BBEvent bbEvent;

The idea is that you can set a one-shot pending flag and the event type, which can be checked in the main loop and handled.

Here is the handler code for motion control.

// handle event
void handle_bbevent(BBEvent* bbEvent)
{
    switch(bbEvent->event) {
        case eBBEvent_Start:
        {
            set_dir(curr_dir);
            set_speed(0, bbEvent->data);
            set_speed(1, bbEvent->data);
        }
        break;

        case eBBEvent_Stop:
        {
            stop();
        }
        break;

        case eBBEvent_Left:
        {
            turn(false, 500);
        }
        break;

        case eBBEvent_Right:
        {
            turn(true, 500);
        }
        break;

        case eBBEvent_Reverse:
        {
            set_dir(!curr_dir);
        }
        break;

        default:
            break;
    }

    // clear 
    bbEvent->pending = false;
}

The motion of the motors is controlled by sending the correct signal to the L293D (The D indicates that the IC has built-in flyback diodes.), which is configured as a dual H-bridge driver, controlling both the direction and speed of two connected motors. The speed is controlled by changing the PWM duty cycle on the A pins, and the direction of rotation is by setting the EN pins to HIGH or LOW. For example, this is how you set the direction of motion:

void set_dir(bool forward)
{
  if(forward) {
    // set direction A
    nrf_gpio_pin_set(pinIN1);
    nrf_gpio_pin_clear(pinIN2);
    // set direction B
    nrf_gpio_pin_set(pinIN3);
    nrf_gpio_pin_clear(pinIN4);
  }
  else {
     // set direction A
    nrf_gpio_pin_clear(pinIN1);
    nrf_gpio_pin_set(pinIN2);
    // set direction B
    nrf_gpio_pin_clear(pinIN3);
    nrf_gpio_pin_set(pinIN4);
  }
  curr_dir = forward;
}

Above, we use the GPIO pins to set the direction of the motors. Here’s how you set the motor speeds:

void set_speed(int motor, uint8_t speed)
{
  // error check
  if (motor < 0 || motor > 1)
    return;

  // set speed
  while (app_pwm_channel_duty_set(&PWM1, motor, speed) == NRF_ERROR_BUSY);
  motor_speeds[motor] = speed; 

  stopped = false;
}

To set speed, you just set the PWM duty cycle. And here’s how you turn:

void turn(bool left, int tms)
{
  if(left) {
    // stop motor 0
    int tmp = motor_speeds[0];
    set_speed(0, 0);
    set_speed(1, 50);
    // wait
    nrf_delay_ms(tms);
    // reset 
    set_speed(0, tmp);
    set_speed(1, tmp);
  }
  else {
    // stop motor 1
    int tmp = motor_speeds[1];
    set_speed(1, 0);
    set_speed(0, 50);
    // wait
    nrf_delay_ms(tms);
    // reset 
    set_speed(0, tmp);
    set_speed(1, tmp);
  }
}

To turn, you just reduce the speed of one motor for the specified time in milliseconds. And here’s how you stop:

void stop()
{
  // set direction A
  nrf_gpio_pin_set(pinIN1);
  nrf_gpio_pin_set(pinIN2);
    
  // set direction B
  nrf_gpio_pin_set(pinIN3);
  nrf_gpio_pin_set(pinIN4);

  stopped = true;
}

The above methods are all you need to drive the robot around.

If BLE connection is inactive, the robot goes into an autonomous mode using the HC-SR04 ultrasonic sensor to detect obstacles. It uses a very simple algorithm: if it detects an object at a distance less than a certain threshold, it stops, reverses, turns left, and continues on. In this mode, I also flash the LED slower, so it’s easy to see whether the robot is connected via BLE or not.

Here’s the simple logic that is used to move the robot autonomously.

void auto_move()
{
    // get HC-SR04 distance
    float dist = 1.0;
    if(getDistance(&dist)) {

        // obstacle avoidance
        if (dist < 15) {
         
            // stop 
            stop();

            // reverse 
            set_dir(false);
            set_speed(0, 50);
            set_speed(1, 50);
            nrf_delay_ms(1000);

            // turn left 
            turn(true, 500);

            // go
            set_dir(true);
            set_speed(0, 80);
            set_speed(1, 80);
        }
    }
}

In the above code, if the distance from the ultrasonic sensor is less than a certain threshold, we stop the motors, reverse, turn, and then continue on forward. Nothing too fancy, but it does work. I have written previously on interfacing the nRF51822 with the HC-SR04 ultrasonic sensor, so I won’t repeat it here. The distance computation code is in distance.c.

Uploading Code to the nRF51822

To program the nRF51822 module, you need to use an SWD programmer. I can’t go into details here, but if you have the Nordic nRF51-DK, you can read my article on nRF51-DK external SWD programming to get started. You can use other tools to do the job also. For example, here’s an nRF51822 project that uses an st-link programmer.

In Action

Here’s BLeBot in action:

And here’s a view of some of the signals in BLEBot (Saleae Logic 8).

blebot-saleae

I think it’s quite impressive that this tiny Nordic SoC can handle an ultrasonic sensor, control two motors, blink an LED, and handle BLE communications at the same time.

Downloads

You can get the complete source code for this project here:

https://github.com/electronut/blebot


Consulting

Need help with a hardware project or product? Drop us an email at info@electronut.in. We offer consulting services on AVR and Nordic nRF BLE - hardware design, firmware development, prototyping, PCB design/assembly, sourcing and manufacturing. We can help you bring your product to market!


Questions/Comments

We love hearing from our readers. Email us at info@electronut.in for questions or comments on this article. If you found this article useful, please support us by buying some of our hardware products.


Please sign up for updates

Once in a while, we will send you an email update on the latest Electronut Labs projects and products. Your email address will never be shared or abused, ever.

2016 Electronut Labs. All rights reserved.