Project updated to beta 6.0.1 (released on December 20th, 2021)
This project is a continuation of the Meadow Rover Part 1, that shows you how to control the motors with an HBridge and four LEDs to indicate the direction it moves. In this project we're focusing on showing how to use the onboard Bluetooth Low Energy (BLE) and use a Xamarin.Forms app to control the rover.
Before going further, make sure you're running the latest Meadow.OS version (b6.0) on your board, which enables the ESP's BLE capability.
If you're new working with Meadow, I suggest you go to the Getting Started w/ Meadow by Controlling the Onboard RGB LED project to properly set up your development environment.
Write the code for Bluetooth Connectivity on MeadowMeadowApp Class
For the main MeadowApp class, we'll no longer need the TestCar
method. Copy the following code, especifically the InitializeBluetooth
method:
public class MeadowApp : App<F7Micro, MeadowApp>
{
Definition bleTreeDefinition;
CharacteristicBool up, down, left, right;
RgbLed led;
PwmLed ledUp, ledDown, ledLeft, ledRight;
CarController carController;
public MeadowApp()
{
Initialize();
}
void Initialize()
{
led = new RgbLed(
Device,
Device.Pins.OnboardLedRed,
Device.Pins.OnboardLedGreen,
Device.Pins.OnboardLedBlue);
led.SetColor(RgbLed.Colors.Red);
ledUp = new PwmLed(Device, Device.Pins.D13, TypicalForwardVoltage.Red);
ledDown = new PwmLed(Device, Device.Pins.D10, TypicalForwardVoltage.Red);
ledLeft = new PwmLed(Device, Device.Pins.D11, TypicalForwardVoltage.Red);
ledRight = new PwmLed(Device, Device.Pins.D12, TypicalForwardVoltage.Red);
ledUp.IsOn = ledDown.IsOn = ledLeft.IsOn = ledRight.IsOn = true;
var motorLeft = new HBridgeMotor
(
device: Device,
a1Pin: Device.Pins.D05,
a2Pin: Device.Pins.D06,
enablePin: Device.Pins.D07
);
var motorRight = new HBridgeMotor
(
device: Device,
a1Pin: Device.Pins.D02,
a2Pin: Device.Pins.D03,
enablePin: Device.Pins.D04
);
carController = new CarController(motorLeft, motorRight);
led.SetColor(RgbLed.Colors.Blue);
bleTreeDefinition = GetDefinition();
Device.BluetoothAdapter.StartBluetoothServer(bleTreeDefinition);
up.ValueSet += UpValueSet;
down.ValueSet += DownValueSet;
left.ValueSet += LeftValueSet;
right.ValueSet += RightValueSet;
led.SetColor(RgbLed.Colors.Green);
}
void UpValueSet(ICharacteristic c, object data)
{
ledUp.IsOn = (bool)data;
if ((bool)data)
carController.MoveForward();
else
carController.Stop();
}
void DownValueSet(ICharacteristic c, object data)
{
ledDown.IsOn = (bool)data;
if ((bool)data)
carController.MoveBackward();
else
carController.Stop();
}
void LeftValueSet(ICharacteristic c, object data)
{
ledLeft.IsOn = (bool)data;
if ((bool)data)
carController.TurnLeft();
else
carController.Stop();
}
void RightValueSet(ICharacteristic c, object data)
{
ledRight.IsOn = (bool)data;
if ((bool)data)
carController.TurnRight();
else
carController.Stop();
}
Definition GetDefinition()
{
up = new CharacteristicBool(
"Up",
uuid: "017e99d6-8a61-11eb-8dcd-0242ac1300aa",
permissions: CharacteristicPermission.Write |
CharacteristicPermission.Read,
properties: CharacteristicProperty.Write |
CharacteristicProperty.Read
);
down = new CharacteristicBool(
"Down",
uuid: "017e99d6-8a61-11eb-8dcd-0242ac1300bb",
permissions: CharacteristicPermission.Write |
CharacteristicPermission.Read,
properties: CharacteristicProperty.Write |
CharacteristicProperty.Read
);
left = new CharacteristicBool(
"Left",
uuid: "017e99d6-8a61-11eb-8dcd-0242ac1300cc",
permissions: CharacteristicPermission.Write |
CharacteristicPermission.Read,
properties: CharacteristicProperty.Write |
CharacteristicProperty.Read
);
right = new CharacteristicBool(
"Right",
uuid: "017e99d6-8a61-11eb-8dcd-0242ac1300dd",
permissions: CharacteristicPermission.Write |
CharacteristicPermission.Read,
properties: CharacteristicProperty.Write |
CharacteristicProperty.Read
);
var service = new Service(
name: "ServiceA",
uuid: 253,
up, down, left, right
);
return new Definition("MeadowRover", service);
}
}
In MeadowApp's Constructor, notice it will only call two methods: Initialize
and InitializeBluetooth
.
In the Initialize
method, you can see how all the four LEDs are initialized as PwmLed (which we'll use to turn them on and off depending the direction the car is going), two HBridgeMotor objects (one for each motor), and are passed to a new CarController object.
In the InitializeBluetooth
method, we call Device.InitCoprocessor()
to activate the ESP's bluetooth module, and we immediately create our BLE Definition tree with four boolean characteristics, one for each direction to move the rover. You can read in more detail about Meadow's BLE connectivity guides in our docs.
As we mentioned, in this project we included a Xamarin.Forms app that runs on iOS and Android, which we'll use to control the Meadow Rover.
To communicate to our Meadow device from our phones, we use the Plugin.BLE NuGet Package. They have a great guide on their GitHub repo to show how to do things like scanning for devices, connecting, getting services and characteristics, reading and writing values.
Some things to understand in this app:
MainViewModel
public class MainViewModel : BaseViewModel
{
IAdapter adapter;
ICharacteristic up, down, left, right;
bool isConnected;
public bool IsConnected
{
get => isConnected;
set { isConnected = value; OnPropertyChanged(nameof(IsConnected)); }
}
public MainViewModel()
{
IBluetoothLE ble = CrossBluetoothLE.Current;
adapter = CrossBluetoothLE.Current.Adapter;
adapter.ScanMode = ScanMode.LowLatency;
adapter.DeviceConnected += async (s,e) =>
{
IsConnected = true;
IDevice device = e.Device;
var services = await device.GetServicesAsync();
up = await services[2].GetCharacteristicAsync(
new Guid("017e99d6-8a61-11eb-8dcd-0242ac1300aa"));
down = await services[2].GetCharacteristicAsync(
new Guid("017e99d6-8a61-11eb-8dcd-0242ac1300bb"));
left = await services[2].GetCharacteristicAsync(
new Guid("017e99d6-8a61-11eb-8dcd-0242ac1300cc"));
right = await services[2].GetCharacteristicAsync(
new Guid("017e99d6-8a61-11eb-8dcd-0242ac1300dd"));
};
adapter.DeviceDisconnected += (s, e) =>
{
IsConnected = false;
};
}
public async Task Connect()
{
try
{
Guid guid = new Guid("00000000-0000-0000-0000-d8a01d697eaa");
await adapter.ConnectToKnownDeviceAsync(guid);
}
catch (DeviceConnectionException ex)
{
Debug.WriteLine(ex.Message);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
public async Task MoveForward(bool go)
{
byte[] array = new byte[1];
array[0] = go ? (byte)1 : (byte)0;
await up.WriteAsync(array);
}
public async Task MoveBackward(bool go)
{
byte[] array = new byte[1];
array[0] = go ? (byte)1 : (byte)0;
await down.WriteAsync(array);
}
public async Task TurnLeft(bool go)
{
byte[] array = new byte[1];
array[0] = go ? (byte)1 : (byte)0;
await left.WriteAsync(array);
}
public async Task TurnRight(bool go)
{
byte[] array = new byte[1];
array[0] = go ? (byte)1 : (byte)0;
await right.WriteAsync(array);
}
}
The MainViewModel uses the Connect method to connect to a specific GUID which is our Meadow Rover device, and once connected, it fired the DeviceConnected event handler, which gets all the services and characteristics which would be the four booleans to move the rover around.
We also have four async Task methods to move the rover around, and in each method we're sending a byte array with 1 or 0 to activate/deactivate the rover motors.
Step 4 - Run the projectClick the Run button in Visual Studio. It should look like to the following GIF:
This project is only the tip of the iceberg in terms of the extensive exciting things you can do with Meadow.Foundation.
- It comes with a huge peripheral driver library with drivers for the most common sensors and peripherals.
- The peripheral drivers encapsulate the core logic and expose a simple, clean, modern API.
- This project is backed by a growing community that is constantly working on building cool connected things and are always excited to help new-comers and discuss new projects.
Comments