Software apps and online services

Story

;

;

1 / 4

I was getting ready to join a small team of folks in conducting a Maker Experience for a group of High School students in Oakland, CA. Maker can mean a lot of things – for us it meant a focus on thinking about real world problems that can be simulated or solved via the Arduino with sensors and actuators. We had chosen the Arduino 101 along with the Grove Starter Kit for Arduino as our platform and were looking for a capstone project that would reinforce lots of the concepts we had been teaching as well as being memorable and fun. Through my network, I found some awesome Rover kits that were based on Edison and used Wi-Fi - it was the Seeedstudio Skeleton Bot - a 4WD mobile robotic platform. This platform has been around for a few years, but they were really high performance and very cool, so I decided to try to convert the Rover kits to use Arduino 101 and Bluetooth Low Energy communication. We had a great project for our Maker Experience! This tutorial describes the build/conversion of that Edison based 4WD kit to use Arduino 101.

There are quite a few Arduino based rovers on the internet - so what do I hope to add with this tutorial?

1. A practical example using the Arduino 101. With built-in wireless Bluetooth Low Energy communications (BLE) and a 6-axis Inertial Measurement Unit (IMU) - it feels like the Arduino 101 is made for motion! However, I could not find many real projects that put these new capabilities to use. This tutorial utilizes both the Arduino 101 BLE and IMU features in a useful way.

2. I also highlight the use of the Grove Starter Kit for Arduino, since it allows me to quickly add sensors and actuators (from a large catalog of inexpensive sensors) without the need to breadboard, which is more difficult and time consuming for me. This is a personal preference and not intended as a "knock" on those who are skilled at breadboarding.

3. My Arduino code is very modular - so hopefully it is usable even if you don't use the same motors, motor controller or the grove kit.

All software and schematics are provided. First, I describe building a basic 4WD Rover - Arduino based motor control taking commands via BLE from a smartphone - Android or IOS. In testing, I crashed the Rover a few times and found that trying to pick it up after I "turtled" it, with its knobby tires spinning fast, wasn't very safe. I decided to add a routine to detect crashes and turn the motors off when I crashed. I also have a few ideas for improving the rover and I will outline some of those at the end.

During the build, I benefited so much from public sample code and tips, I wanted to share what I learned in the hope that others can benefit from my work.

The high level steps for this tutorial are as follows:

1. Assemble the Rover “Rolling Chassis”

2. Electrical Assembly

3. Testing motor control with the Arduino 101

4. Building the BLE Remote Control – covered in a separate Instructable

I have included several pictures of the Rover kit at the completion of this step. Mounting the wheels at this step versus later is a matter of preference. I feel it is easier to test with the wheels off. But with the wheels on, the rover can sit on a block or box – allowing the wheels to spin freely - this works just as well.

Step 2: Electrical Assembly:

;

;

1 / 4

My DC motors came with wires already attached as can be observed in the previous step. I attached the wires from the left motors to Motor Driver 1 and from the right motors to Motor Driver 2 as can be seen in the schematic and photo. Securing the two sets of wires in the small screw-down connectors was challenging.

I assembled the power cable next. The different pieces of the power cable came with the kit and provide an ingenious way of connecting an RC car battery to the rover. I assembled it as shown.

As mentioned, the Lithium Polymer battery being used is a typical Radio Controlled car battery. It is a 2S battery – meaning is puts two of the 18650 battery elements in series. 18650s are rated at 3.7 volts, so having two in series, yields a 7.4 volt input for the motor driver.

In terms of output, the Grove I2C motor driver integrates an L298P dual H-bridge capable of delivering 2A per motor driver. It is also capable of driving DC motors in both directions and controlling speed and direction of each of the motor drivers independently. One additional feature of the Grove I2C Motor Driver board is that it has an onboard 5V regulator which can be used to power the Arduino through the I2C bus. I experimented and found that this worked well - the Arduino did not need to be powered by the barrel jack (or USB cable) when the battery was attached to the motor driver board. For more information on the this motor driver, please see the wiki entry at: http://www.seeedstudio.com/wiki/Grove_-_I2C_Motor_Driver_V1.3

I made sure to attach a grove cable and to feed the battery connector through a slot in the top acrylic plate before screwing it in place.

Step 3: Testing Motor Control

In order to ensure that the wiring was done correctly, I needed some simple Arduino code to spin the 4 motors at different speeds and different directions. The left motor in the front and the left motor in the back should spin identically. The same is true of the pair of motors on the right.

I made a couple of modifications to adjust the speed of the motors in the loop. The code I used to test is attached.

Ensure that each motor turns, at three different speeds and in both directions. If that is not what you observe, check the wiring and retest until correct. I have attached a short video showing a successful motor test.

Step 4: Building the BLE Remote Control

Building the Arduino 101 BLE Rover Remote Control is covered in a different tutorial here.

Step 5: Programming the Arduino 101 for a “Base Model” Rover

I start with a "Base Model" rover code - no frills, minimal options. It must contain:

Rover control code for translating commands into actions for the motors

BLE communications code to connect the smartphone to the Arduino.

I structure my code in a traditional way:

Include libraries and declare global variables

Setup() function – for code intializing hardware - needed to run once

Loop() function – for code that runs repeatedly

User defined functions – for any chunk of code longer than a few lines in the loop

I find that creating user defined functions allows me to keep my code readable and structured – less chance of me writing spaghetti code; that is hard to understand and hard to debug.

The Base model code is a combination of 2 functions for controlling the DC motors – MotorSpeedSetAB and combined with the Rover state machine from deba168’s tutorial: Smartphone Controlled Arduino Rover, which I placed in a user defined function called RoverControl.

For communication, I modified the CallbackLED sample that showcases the use of Bluetooth LE on the Arduino 101. The main modifications were to define the UART profile (specific BLE characteristics and UUID’s) that the nRF Toolbox application expects to see. This is the application where I defined the remote control on the smartphone. There are several online tutorials on Bluetooth Low Energy (BLE) – I will not go into theoretical depth to explain its inner workings here. Let me walk you through each section of code briefly!

Section 1 (include and declare):

include the wire.h library to initialize the I2C bus

include the CurieBLE.h library to enable Bluetooth LE communications

declare variables defining D2 for the Blue LED, state to hold the RoverControl command char, vSpeedSet to hold the char for speed (0-100)

create a BLEPeripheral instance, create a BLEService for the UART profile, create a rx and tx characteristics for the service

Section 2 (setup function):

initialize the I2C bus and serial link

define D2 as an output – this is the Grove LED pin – blue LED indicating a Bluetooth connection or not

setup several items related to BLE – LocalName, add the tx and rx characteristics to the BLE service, define event handlers for connect, disconnect and rxCharacteristic written, then start advertising the BLE service

initialize the DC motors as “off” (both + and – pins low for both motors), DC motor speed set at 50%

Section 3 (loop function):

Here are all 4 lines of code in the loop() function:

blePeripheral.poll(); // check if the rxCharacteristic is written
if(state != prev_state) { // check if the value from the remote control is new?
RoverControl(state); // function to respond to control state changes - direction and/or speed
prev_state = state; // keep a little history

I only want to respond to a command from the Remote Control if it is different from the command I am currently executing.

The BLE connect handler turns the LED on when connected and the BLE disconnect handler turns the LED off when disconnected. All the code is heavily documented and available for download.

Step 6: Adding a “Crash Detect” safety option

;

;

1 / 3

As I mentioned previously, I was inspired to add this feature after crashing the rover a few times. Either it would be up against a wall trying to push its gear motors into an immovable object, or it would be on its “back”, knobby wheels spinning quickly as it rocked back and forth – not what I consider to be safe to pick up without caution. Reducing “wear and tear” on the motors and reducing a need for first aid – both are a win.

I got the implementation idea from the Shock Detect sample for the Arduino 101. This is part of the Inertial Measurement Unit (IMU) functionality. After including the library, you initialize the IMU and attach an interrupt to the IMU and set it to trigger on “shock” in the setup() function. You set the peak and duration of a shock you want to detect and the Arduino 101 generates an interrupt, letting you take action in an event handler.

From a hardware perspective, I added the Grove Button to D2 and the Grove RGB LCD display to an I2C port on the Grove Shield. It should look like the pictures for this step.

In order to utilize the display, I include its library, initialize it in setup() and then write out the motor speed setting to the display as part of the RoverControl() function.

The interrupt service routine in the CurieIMU Shock Detect sample prints the orientation of the shock to the Serial Monitor. In my routine, I turn the motors off, change the RGB LDC display to have a RED backlight and writes a crash message to the LCD. I also set ‘crash’ - a Boolean variable that effectively blocks the rover from responding to new remote control commands until a reset button is pressed. The simple Loop() code now looks like the following:

if (crash == false) { //rover is operating normally
previous block of 4 lines of code – used in normal operation
}
else { //rover is in a crashed state
if (digitalRead(resetPin) == HIGH) { // look for reset condition - this occurs from a push button after a crash
CrashRecovery(); // function to recover from crash, start operating normally again
}
}

The CrashRecovery() function is called when the Grove button mounted at the back of the rover is pushed. It is responsible for resetting the display backlight to Green and the LCD message displays the motor power setting again. The motors direction is set to ‘off’, the state is set to stop (‘c’) and speed is set to half power again. Finally, the crash variable to false for normal operation.

For Shock detection, I found that the initial setting of 1500 mg was too sensitive and indicated a crash in normal operation when changing speed or direction. With a little experimentation I was able to find a reasonable setting that didn’t generate false positives and still triggered in a crash.

The full sketch is attached.

Step 7: Final Thoughts and Future Enhancements

This project worked out really well as a capstone project for our Maker Experience in Oakland. Our students were successful in building, testing and enjoying the rover.

I had a great time researching and building this rover. I learned a TON! As can be expected, not everything worked as expected the first time I tried. I learned way more when things didn't work, than when they did.

There are some things that worked really well on the Rover:

1. The BLE remote control is reliable and simple to create

2. The DC gear motors are high performance and work well

3. Using the Grove Starter kit made feature enhancements to the rover easy to create

1. The I2C Grove Motor Driver would occasionally lock up and require a reset via small button on the board - only seen during code changes and debugging

2. The rapid direction and speed changes seems to be hard on the DC motors (a couple failures seen) - I should "smooth" out motor speed ramps and direction changes.

In addition to enhancements related to speed ramp smoothing I would like to add a range sensing in the front and rear to enable a "brake assist" to avoid collisions. Perhaps I will tackle these in my next tutorial.

Schematics

Rover Base Model schematic

Rover Final Schematic

Code

Rover Base Model Code

C/C++

/* * Arduino 101 based 4WD Rover code developed utilizing: * - an Arduino 101 microcontroller board - using the Bluetooth LE radio (BLE), and the 6-axis Inertial Measurement Unit (IMU) * - motors and chassis from Skeleton Bot - 4WD hercules mobile robotic platform from Seeedstudio - early version * - Grove I2C Motor Driver Board * - Grove Starter Kit for Arduino from Seeedstudio - Grove Button, Grove I2C RGB LCD Display, Grove LED Socket, * - HC-SR04 Ultrasonic sensor, wired via a small breadboard * * Most of this project's code is derived from other Arduino samples found * on www.instructables.com and www.github.com * * Much respect and appreciation for the "Makers" who have come before me * Lots of knowledge gained from their code and hope my example here is useful to others * * the integration and other bits of original code were written by: * Author: Dave Shade * * it is available for use under the GNU Lesser General Public License * see below for details * * *** Significant pieces of code used from the following: *** * * Code included for the Grove I2C Motor Driver from: * Grove - I2C motor driver demo v1.0 * by: http://www.seeedstudio.com * * Code for the RoverControl function taken from the Basic_Robt.ino Sketch: * http://www.instructables.com/id/Smartphone-Controlled-Arduino-Rover/ * by deba168 * * Code for the Crash Detection derived from the Arduino 101 sample: ShockDetect * Copyright (c) 2015 Intel Corporation. All rights reserved. * Code for the BlueTooth Remote Control derived from the Arduino 101 sample: CallbackLED * Copyright (c) 2015 Intel Corporation. All rights reserved. * * Specifics for the BLE UART characteristics - to enable the use of the Nordic Semiconductor UART applicaion * found at: https://www.nordicsemi.com/eng/Products/Nordic-mobile-Apps/nRF-UART-App2 * * Code included from various Grove examples like: * Grove RGB LCD display, Grove Button, Grove LED and they are: * 2013 Copyright (c) Seeed Technology Inc. All right reserved. * * All code utilized is covered under the license agreement described below: * * This demo code 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 St, Fifth Floor, Boston, MA 02110-1301 USA **/#include<Wire.h> // Library for the initializing the I2C bus.#include<CurieBLE.h> // CurieBLE library - pre-installed when Arduino 101 is selected in Arduino IDE 1.6.7 or later //I2C Grove Motor Driver defines//Motor controller defines#define MotorSpeedSet 0x82#define PWMFrequenceSet 0x84#define DirectionSet 0xaa#define MotorSetA 0xa1#define MotorSetB 0xa5#define Nothing 0x01#define EnableStepper 0x1a#define UnenableStepper 0x1b#define Stepernu 0x1c#define I2CMotorDriverAdd 0x0f // Set the address of the I2CMotorDriver// Variables for allocating the Digital I/O pins of the Arduino//const int resetPin = 2; // D2 used by Grove Button - reset from crash stateconstintledPin=3;// D3 used by Grove LED socket - showing bluetooth connected//const int servoPin = 5; // D5 used by the servo//const int ultrasonic_back = 6; // D6 used by the Back ultrasonic sensor// D7 used by the Back ultrsonic sensor//const int ultrasonic_front = 8; // D8 used by the Front ultrasonic sensor// D9 used by the Front ultrasonic sensor// 0x0f is the I2C address used by Grove Motor Driver board// 0x3e is an I2C address used by the Grove RGB LCD display (7c>>1)// 0x62 is an I2C address used by the Grove RGB LCD display (c4>>1)charvSpeedSet=50;// this variable holds the speed setting from the remote control - default speed is halfcharvSpeedLimit=50;// this variable holds the limited value of the speed based on sensor valuescharstate='c';// initial state is stopcharprev_state='c';BLEPeripheralblePeripheral;// BLE Peripheral Device (the board you're programming)// ==== create Nordic Semiconductor BLE UART service =========// Must use these UUIDs and BLE characteristicsBLEServiceuartService=BLEService("6E400001B5A3F393E0A9E50E24DCCA9E");// create characteristicsBLECharacteristicrxCharacteristic=BLECharacteristic("6E400002B5A3F393E0A9E50E24DCCA9E",BLEWriteWithoutResponse,20);// == TX on central (android app)BLECharacteristictxCharacteristic=BLECharacteristic("6E400003B5A3F393E0A9E50E24DCCA9E",BLENotify,20);// == RX on central (android app)// Setup function - run once at reset or power-onvoidsetup(){Wire.begin();// join i2c bus (address optional for master)delay(100);Serial.begin(115200);// initialize the Reset button on D2 and the Blue Grove LED on D3pinMode(ledPin,OUTPUT);// use the LED on pin 3 as an output// set advertised local name and service UUID:blePeripheral.setLocalName("4WD_RV");//make unique nameblePeripheral.setAdvertisedServiceUuid(uartService.uuid());// add service and characteristic:blePeripheral.addAttribute(uartService);blePeripheral.addAttribute(rxCharacteristic);blePeripheral.addAttribute(txCharacteristic);// assign event handlers for connected, disconnected to peripheralblePeripheral.setEventHandler(BLEConnected,blePeripheralConnectHandler);blePeripheral.setEventHandler(BLEDisconnected,blePeripheralDisconnectHandler);// assign event handler for characteristicrxCharacteristic.setEventHandler(BLEWritten,rxCharacteristicWritten);// begin advertising BLE service:blePeripheral.begin();//Serial.println(("Bluetooth device active, waiting for connections...")); // for debugging// Initialize the motor controllers MotorDirectionSet(0b0000);//0b0000 stopped delay(100);MotorSpeedSetAB(vSpeedSet,vSpeedSet);// on a scale of 1 to 100delay(100);}// end setupvoidloop(){/* Key sections of the loop * Check if we are in a "crash" condition * if not crashed: * poll for a new value written into state by the rxCharacteristic * check if there is something new in "state" * if yes call rovercontrol * if no call speedcontrol * if crashed: * look for a reset button press */blePeripheral.poll();if(state!=prev_state){// check if the value from the remote control new or the same as the previous one?RoverControl(state);// function to respond to control state changes - direction and/or speed//Serial.println(char(state));prev_state=state;}}voidblePeripheralConnectHandler(BLECentral&central){// central connected event handlerSerial.print("Connected event, central: ");Serial.println(central.address());digitalWrite(ledPin,HIGH);//Serial.println("LED on");}voidblePeripheralDisconnectHandler(BLECentral&central){// central disconnected event handlerSerial.print("Disconnected event, central: ");Serial.println(central.address());digitalWrite(ledPin,LOW);//Serial.println("LED off");state='c';RoverControl(state);// stop rover on BLE disconnect - should reduce runaway rover incidents//Serial.println(char(state));delay(100);}voidrxCharacteristicWritten(BLECentral&central,BLECharacteristic&characteristic){// central wrote new value to characteristic, update LED//Serial.print("Characteristic event, written: ");if(characteristic.value()){// NULL pointer checkstate=*characteristic.value();// de-reference to get first byte//Serial.println(char(state));}}// Functions to set the 2 DC motor's speed: motorSpeedA: the DC motor A speed; should be 0~100, motorSpeedB: the DC motor B speed; should be 0~100;voidMotorSpeedSetAB(unsignedcharMotorSpeedA,unsignedcharMotorSpeedB){MotorSpeedA=map(MotorSpeedA,0,100,0,255);MotorSpeedB=map(MotorSpeedB,0,100,0,255);Wire.beginTransmission(I2CMotorDriverAdd);// transmit to device I2CMotorDriverAddWire.write(MotorSpeedSet);// set pwm header Wire.write(MotorSpeedA);// send pwma Wire.write(MotorSpeedB);// send pwmb Wire.endTransmission();// stop transmitting}// set the direction of DC motor. voidMotorDirectionSet(unsignedcharDirection){// Adjust the direction of the motors 0b0000 I4 I3 I2 I1Wire.beginTransmission(I2CMotorDriverAdd);// transmit to device I2CMotorDriverAddWire.write(DirectionSet);// Direction control headerWire.write(Direction);// send direction control informationWire.write(Nothing);// need to send this byte as the third byte(no meaning) Wire.endTransmission();// stop transmitting }voidRoverControl(charstate){/* * Respond to changes in direction - forward, left, stop, right, backward from Bluetooth LE Remote Control * * With a new "state" from the remote control - check for action to take * a - set gear to go forward (drive) * b - set gear to turn left and go forward * c - set gear to stop (park) * d - set gear to turn right and go forward * e - set gear to go backward (reverse) * 0 - set speed to 0 - not implemented in the remote control * 1 - set speed to 25% * 2 - set speed to 50% * 3 - set speed to 75% * 4 - set speed to 100% */if(state=='a'){MotorDirectionSet(0b1001);}//0b1001 // If state is equal with letter 'a', Motors Rotating in the forward direction, rover will go forward elseif(state=='b'){MotorDirectionSet(0b0001);}//0b0001 // If state is equal with letter 'b', right motors rotating forward, left motors off, , rover will turn leftelseif(state=='d'){MotorDirectionSet(0b1000);}//0b1000 // If state is equal with letter 'd', left motors rotating forward, right motors off, rover will turn rightelseif(state=='c'){MotorDirectionSet(0b0000);}//0b0000 // If state is equal with letter 'c', Motors off - stop the roverelseif(state=='e'){MotorDirectionSet(0b0110);}//0b0110 // If state is equal with letter 'e', rover will go backward, both motors rotating backwardelseif(state=='0'){// Change speed if state is equal from 0 to 4. Values must be from 0 to 100 - this is mapped to 0 to 255 (PWM) by the MotorSpeedSet functionvSpeedSet=0;}elseif(state=='1'){vSpeedSet=25;}elseif(state=='2'){vSpeedSet=50;}elseif(state=='3'){vSpeedSet=75;}elseif(state=='4'){vSpeedSet=100;}delay(10);vSpeedLimit=vSpeedSet;MotorSpeedSetAB(vSpeedSet,vSpeedSet);// set motor speed based on changes on a scale of 1 to 100}

Rover Option Model Code

C/C++

/* * Arduino 101 based 4WD Rover code developed utilizing: * - an Arduino 101 microcontroller board - using the Bluetooth LE radio (BLE), and the 6-axis Inertial Measurement Unit (IMU) * - motors and chassis from Skeleton Bot - 4WD hercules mobile robotic platform from Seeedstudio - early version * - Grove I2C Motor Driver Board * - Grove Starter Kit for Arduino from Seeedstudio - Grove Button, Grove I2C RGB LCD Display, Grove LED Socket, * - HC-SR04 Ultrasonic sensor, wired via a small breadboard * * Most of this project's code is derived from other Arduino samples found * on www.instructables.com and www.github.com * * Much respect and appreciation for the "Makers" who have come before me * Lots of knowledge gained from their code and hope my example here is useful to others * * the integration and other bits of original code were written by: * Author: Dave Shade * * it is available for use under the GNU Lesser General Public License * see below for details * * *** Significant pieces of code used from the following: *** * * Code included for the Grove I2C Motor Driver from: * Grove - I2C motor driver demo v1.0 * by: http://www.seeedstudio.com * * Code for the RoverControl function taken from the Basic_Robt.ino Sketch: * http://www.instructables.com/id/Smartphone-Controlled-Arduino-Rover/ * by deba168 * * Code for the Crash Detection derived from the Arduino 101 sample: ShockDetect * Copyright (c) 2015 Intel Corporation. All rights reserved. * Code for the BlueTooth Remote Control derived from the Arduino 101 sample: CallbackLED * Copyright (c) 2015 Intel Corporation. All rights reserved. * * Specifics for the BLE UART characteristics - to enable the use of the Nordic Semiconductor UART applicaion * found at: https://www.nordicsemi.com/eng/Products/Nordic-mobile-Apps/nRF-UART-App2 * * Code included from various Grove examples like: * Grove RGB LCD display, Grove Button, Grove LED and they are: * 2013 Copyright (c) Seeed Technology Inc. All right reserved. * * All code utilized is covered under the license agreement described below: * * This demo code 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 St, Fifth Floor, Boston, MA 02110-1301 USA **/#include<Wire.h> // Library for the initializing the I2C bus.#include"rgb_lcd.h" // Grove library for the I2C RGB LCD Display is found at: https://github.com/Seeed-Studio/Sketchbook_Starter_Kit_V2.0#include"CurieIMU.h" // CurieIMU library - pre-installed when Arduino 101 is selected in Arduino IDE 1.6.7 or later #include<CurieBLE.h> // CurieBLE library - pre-installed when Arduino 101 is selected in Arduino IDE 1.6.7 or later //I2C Grove Motor Driver defines//Motor controller defines#define MotorSpeedSet 0x82#define PWMFrequenceSet 0x84#define DirectionSet 0xaa#define MotorSetA 0xa1#define MotorSetB 0xa5#define Nothing 0x01#define EnableStepper 0x1a#define UnenableStepper 0x1b#define Stepernu 0x1c#define I2CMotorDriverAdd 0x0f // Set the address of the I2CMotorDriver// Variables for allocating the Digital I/O pins of the ArduinoconstintresetPin=2;// D2 used by Grove Button - reset from crash stateconstintledPin=3;// D3 used by Grove LED socket - showing bluetooth connected//const int servoPin = 5; // D5 used by the servo//const int ultrasonic_back = 6; // D6 used by the Back ultrasonic sensor// D7 used by the Back ultrsonic sensor//const int ultrasonic_front = 8; // D8 used by the Front ultrasonic sensor// D9 used by the Front ultrasonic sensor// 0x0f is the I2C address used by Grove Motor Driver board// 0x3e is an I2C address used by the Grove RGB LCD display (7c>>1)// 0x62 is an I2C address used by the Grove RGB LCD display (c4>>1)// Variables for the Grove RGB LCD Display - connected to the I2C bus// common RGB values: Red=255,0,0 Green=0,255,0 Blue=0,0,255 Purple=255,0,255 Yellow=255,255,0 Aqua=0,255,255 White=255,255,255 Off=0,0,0intcolorR=0;intcolorG=255;intcolorB=0;charvSpeedSet=50;// this variable holds the speed setting from the remote control - default speed is halfcharvSpeedLimit=50;// this variable holds the limited value of the speed based on sensor valuesboolcrash=false;// initial crash state is false (operating)charstate='c';// initial state is stopcharprev_state='c';// Creating the instance of the LCD displayrgb_lcdlcd;BLEPeripheralblePeripheral;// BLE Peripheral Device (the board you're programming)// ==== create Nordic Semiconductor BLE UART service =========// Must use these UUIDs and BLE characteristicsBLEServiceuartService=BLEService("6E400001B5A3F393E0A9E50E24DCCA9E");// create characteristicsBLECharacteristicrxCharacteristic=BLECharacteristic("6E400002B5A3F393E0A9E50E24DCCA9E",BLEWriteWithoutResponse,20);// == TX on central (android app)BLECharacteristictxCharacteristic=BLECharacteristic("6E400003B5A3F393E0A9E50E24DCCA9E",BLENotify,20);// == RX on central (android app)// Setup function - run once at reset or power-onvoidsetup(){Wire.begin();// join i2c bus (address optional for master)delay(100);Serial.begin(115200);// initialize the Reset button on D2 and the Blue Grove LED on D3pinMode(resetPin,INPUT);//use the switch which has a pull-up resistor aand connects to groundpinMode(ledPin,OUTPUT);// use the LED on pin 3 as an output// set advertised local name and service UUID:blePeripheral.setLocalName("4WD_RV");//make unique nameblePeripheral.setAdvertisedServiceUuid(uartService.uuid());// add service and characteristic:blePeripheral.addAttribute(uartService);blePeripheral.addAttribute(rxCharacteristic);blePeripheral.addAttribute(txCharacteristic);// assign event handlers for connected, disconnected to peripheralblePeripheral.setEventHandler(BLEConnected,blePeripheralConnectHandler);blePeripheral.setEventHandler(BLEDisconnected,blePeripheralDisconnectHandler);// assign event handler for characteristicrxCharacteristic.setEventHandler(BLEWritten,rxCharacteristicWritten);// begin advertising BLE service:blePeripheral.begin();//Serial.println(("Bluetooth device active, waiting for connections...")); // for debugging/* Initialise the IMU and create an Interrupt service routine to deal with a shock - indicating a crash * Shutting off the electric motors after a big shock can help to minimize damage to rover motors and circuitry */CurieIMU.begin();CurieIMU.attachInterrupt(CrashEventCallback);// Enable Shock Detection /* * The Threshold value should be adjusted through some experimentation * so that crashes are detected at reasonable shock levels * this is to ensure the safety of the operator and to minimize stress on the motors * typical values for the function are from 1500 - 2000 * there were updates to the Arduino 101 firmware and CurieIMU library in July 2016 * please ensure you have the latest version using the board manager of the Arduino IDE * */CurieIMU.setDetectionThreshold(CURIE_IMU_SHOCK,1900);// 2.0g = 2000mg; 1.5g = 1500mg, etc.CurieIMU.setDetectionDuration(CURIE_IMU_SHOCK,50);// 50msCurieIMU.interrupts(CURIE_IMU_SHOCK);//Serial.println("IMU initialization complete, waiting for events..."); // for debugging//Serial.println("LCD setup begin"); // for debugging// set up the grove RGB LCD's number of columns and rows:lcd.begin(16,2);lcd.setRGB(colorR,colorG,colorB);// Print message headers to the LCD.lcd.setCursor(0,0);// cursor to home position - it starts there...lcd.print("motor speed % =");//lcd.setCursor(0, 1);//lcd.print("speed = ");// Initialize the motor controllers MotorDirectionSet(0b0000);//0b0000 stopped delay(100);MotorSpeedSetAB(vSpeedSet,vSpeedSet);// on a scale of 1 to 100delay(100);}// end setupvoidloop(){/* Key sections of the loop * Check if we are in a "crash" condition * if not crashed: * poll for a new value written into state by the rxCharacteristic * check if there is something new in "state" * if yes call rovercontrol * if no call speedcontrol * if crashed: * look for a reset button press */if(crash==false){//rover is operating normallyblePeripheral.poll();if(state!=prev_state){// check if the value from the remote control new or the same as the previous one?RoverControl(state);// function to respond to control state changes - direction and/or speed//Serial.println(char(state));prev_state=state;}}else{//rover is in a crashed stateif(digitalRead(resetPin)==HIGH){// look for reset condition - this occurs from a push button after a crashCrashRecovery();// function to recover from crash, start operating normally again}}}voidblePeripheralConnectHandler(BLECentral&central){// central connected event handlerSerial.print("Connected event, central: ");Serial.println(central.address());digitalWrite(ledPin,HIGH);//Serial.println("LED on");}voidblePeripheralDisconnectHandler(BLECentral&central){// central disconnected event handlerSerial.print("Disconnected event, central: ");Serial.println(central.address());digitalWrite(ledPin,LOW);//Serial.println("LED off");state='c';RoverControl(state);// stop rover on BLE disconnect - should reduce runaway rover incidents//Serial.println(char(state));delay(100);}voidrxCharacteristicWritten(BLECentral&central,BLECharacteristic&characteristic){// central wrote new value to characteristic, update LED//Serial.print("Characteristic event, written: ");if(characteristic.value()){// NULL pointer checkstate=*characteristic.value();// de-reference to get first byte//Serial.println(char(state));}}// Functions to set the 2 DC motor's speed: motorSpeedA: the DC motor A speed; should be 0~100, motorSpeedB: the DC motor B speed; should be 0~100;voidMotorSpeedSetAB(unsignedcharMotorSpeedA,unsignedcharMotorSpeedB){MotorSpeedA=map(MotorSpeedA,0,100,0,255);MotorSpeedB=map(MotorSpeedB,0,100,0,255);Wire.beginTransmission(I2CMotorDriverAdd);// transmit to device I2CMotorDriverAddWire.write(MotorSpeedSet);// set pwm header Wire.write(MotorSpeedA);// send pwma Wire.write(MotorSpeedB);// send pwmb Wire.endTransmission();// stop transmitting}// set the direction of DC motor. voidMotorDirectionSet(unsignedcharDirection){// Adjust the direction of the motors 0b0000 I4 I3 I2 I1Wire.beginTransmission(I2CMotorDriverAdd);// transmit to device I2CMotorDriverAddWire.write(DirectionSet);// Direction control headerWire.write(Direction);// send direction control informationWire.write(Nothing);// need to send this byte as the third byte(no meaning) Wire.endTransmission();// stop transmitting }voidRoverControl(charstate){/* * Respond to changes in direction - forward, left, stop, right, backward from Bluetooth LE Remote Control * * With a new "state" from the remote control - check for action to take * a - set gear to go forward (drive) * b - set gear to turn left and go forward * c - set gear to stop (park) * d - set gear to turn right and go forward * e - set gear to go backward (reverse) * 0 - set speed to 0 - not implemented in the remote control * 1 - set speed to 25% * 2 - set speed to 50% * 3 - set speed to 75% * 4 - set speed to 100% */if(state=='a'){MotorDirectionSet(0b1001);}//0b1001 // If state is equal with letter 'a', Motors Rotating in the forward direction, rover will go forward elseif(state=='b'){MotorDirectionSet(0b0001);}//0b0001 // If state is equal with letter 'b', right motors rotating forward, left motors off, , rover will turn leftelseif(state=='d'){MotorDirectionSet(0b1000);}//0b1000 // If state is equal with letter 'd', left motors rotating forward, right motors off, rover will turn rightelseif(state=='c'){MotorDirectionSet(0b0000);}//0b0000 // If state is equal with letter 'c', Motors off - stop the roverelseif(state=='e'){MotorDirectionSet(0b0110);}//0b0110 // If state is equal with letter 'e', rover will go backward, both motors rotating backwardelseif(state=='0'){// Change speed if state is equal from 0 to 4. Values must be from 0 to 100 - this is mapped to 0 to 255 (PWM) by the MotorSpeedSet functionvSpeedSet=0;}elseif(state=='1'){vSpeedSet=25;}elseif(state=='2'){vSpeedSet=50;}elseif(state=='3'){vSpeedSet=75;}elseif(state=='4'){vSpeedSet=100;}delay(10);vSpeedLimit=vSpeedSet;MotorSpeedSetAB(vSpeedSet,vSpeedSet);// set motor speed based on changes on a scale of 1 to 100lcd.setCursor(0,1);lcd.print(" ");lcd.setCursor(0,1);lcd.print(vSpeedSet);delay(10);}// function to reset the Rover after a crash - called when the reset push button is pushedvoidCrashRecovery(void){MotorDirectionSet(0b0000);//0b0000 motor stopvSpeedSet=50;vSpeedLimit=vSpeedSet;MotorSpeedSetAB(vSpeedSet,vSpeedSet);// restore motor speed to previous value - on a scale of 1 to 100// restore the LCD display to its precrash valueslcd.setRGB(0,255,0);// make backlight greenlcd.setCursor(0,0);lcd.clear();lcd.print("motor speed % =");lcd.setCursor(0,1);lcd.print(vSpeedSet);delay(10);crash=false;state='c';}// Callback function from the CurieIMU ShockDetect SamplestaticvoidCrashEventCallback(void){if(CurieIMU.getInterruptStatus(CURIE_IMU_SHOCK)){// Here is where to define what to do in crash situation// should definitely turn off the motors for safetyMotorDirectionSet(0b0000);//0b0000 stop motorsdelay(10);MotorSpeedSetAB(50,50);// set speed to 50 on a scale of 1 to 100lcd.setRGB(255,0,0);// make backlight redlcd.setCursor(0,0);// reset cursor to upper leftlcd.clear();// clear displaylcd.println("CRASH!!!!!!");// write crash message to lcddelay(10);state='c';prev_state='c';crash=true;}}