Cornell University: ECE 4960
Lab 2, Bluetooth
Objective
The Artemis board and a computer are capable of low-latency, moderate-throughput wireless communication via Bluetooth LE. In this lab, you will expand a basic application for wireless communication over a Generic Attribute (GATT) framework on top of robust BLE stacks.
Distribution Code
Please download the distribution code.
Files included
- ECE_4960_Robot/*: contains the Arduino sketch to upload.
- main.py: A Linux, macOS, and Windows compatible python Bluetooth example. Searches for the robot board (if not saved in Settings.py), connects, and performs tasks in myRobotTasks. Handles messages from the board with a simple handler that you will expand.
- constants.py: A collection of useful constants. ..* Commands must match your commands on the robot. ..* Bluetooth UUIDs are preset and correct.
- settings.py: Settings dictionary.
- Other c files: functions that use the Ambiq suite SDK to control the radio and establish a GATT serial port protocol. The true protocol (Ambiq Micro Data Transfer Protocol) employs some additional handshaking, but our example just uses plain bytes.
Tasks
USB Passthrough Bluetooth
The USB BLE adapter in your kit of parts that should work with little extra configuration. If you can set up USB passthrough, that adaptor should work with the Ubuntu VM provided in Lab 1. The staff will be able to provide better support for that setup. As a fallback, BLE exists on many computers made after 2012 and running recent operating systems. The bluetooth example should be compatible with backends on Linux (bluez), Windows 10 (Windows BLE), and macOS (CoreBluetooth).
You’ll also need an installation of bleak
on python3. The easiest way is to
install python3, pip, and run python3 -m pip install bleak
or py -m pip install bleak
.
The Ubuntu VM should already have python3 and bleak
installed. However, running the install
command should not break anything. As a result, once you get USB passthrough working,
you should be able to download the distribution code and commence work immediately.
Connect to the Artemis Board
Upload the provided Arduino sketch to the Artemis board. Upon reset, the blue LED should blink rapidly for a short time and then extinguish itself. The Serial Monitor should also show:
Revision = Rev 1.0 ECE 4960 Robot Compiled: Aug 28 2020 14:38:19
############## setting up WatchDogTimer WDT ########################
Interrupt Count = 93 ticks
Reset Count = 93 ticks
Reset Status Register = 0x1
Run main.py
to try to discover your robot. The bleak
library might throw some
errors (usually on macOS) when trying to discover the robot. Re-running the program
gives bleak
another chance to search for the robot. It may take many re-runs for
bleak
to connect, but once it connects, the connection should be fairly reliable.
(Optional step: add your robot’s MAC address or UUID to Settings["cached"]
in
settings.py
. This especially helps on macOS.)
If you come up with a better solution than re-running the program, please share it.
Robot command framework
The distribution code includes the framework for a basic, efficient command language. command.h
includes a 99-byte structure (cmd_t
) with the first byte as the command type, the
second byte as some sort of length (you will decide how to use it!), and the rest as data.
A 1-byte enum names a few commands (cmd_type_e
) which must match the commands found in
constants.py
. We recommend that you expand this framework in a way that works well for
your implementation of future labs. A few things to note:
- Your programs might not have to handle all kinds of received commands. One-way commands may make debugging easier (i.e. SER_TX will travel only from your robot to the computer, while SER_RX only travels from the computer to your robot).
- There are also some helper functions, such as
printOverBluetooth()
andtheRobot.sendMessage()
. They are slow compared to sending numbers themselves. You can usesprintf()
on Arduino to build strings forprintOverBluetooth()
.
Ping Your Robot
Right now, your robot doesn’t do anything when connected. Comment pass
in myRobotTasks()
and uncomment await theRobot.ping()
. Observe the output and comment on the round-trip latency.
What is it on average? How does the latency vary? How many bytes does the ping transfer?
How does the round-trip data transfer rate compare to a wired, serial connection? Please include
a useful plot (likely a histogram) related to the latency in your lab write-up.
Request a float
You can send commands using theRobot.sendCommand(cmd, length, data)
. If length and data are
“don’t care” values, you may just send the command. For example, in main.py/myRobotTasks()
,
uncomment await theRobot.sendCommand(Commands.REQ_FLOAT)
to request a float.
In the Arduino sketch, your task is to put code in case REQ_FLOAT:
.
Put in a float that starts at (res_cmd->data
). Review C data types and casting as necessary.
(Update 19 Sep 2020: You may need to use memcpy(dstAddr, srcAddr, numBytes);
depending
on where exactly you end up putting your float. Misaligned memory accesses can cause hardfaults.)
Replace TODO_VAL
in the nearby amdtpsSendData((uint8_t *)res_cmd, TODO_VAL);
with the
number of bytes that you will send. If your code works, simpleHandler will unpack the float
that you sent and print it. Does the float displayed to the screen match the float that you
tried to send? What does this mean with regards to float comparision?
Test the Data Rate
You know (approximately) what the round-trip latency is. We also want you to get a feeling
for the wireless data transfer rate is, and how reliable the connection is. You’ll get a
decent sense of this by streaming bytes from the Artemis to your computer.
In the main Arduino sketch, find the line if (bytestream_active)
. At the very least, send
an example of a 32-bit integer and a 64-bit integer. A count and the result of micros()
would be useful. On your setup, what is the (approximate) average time between packets sent?
How often are packets dropped? Does the number of bytes sent make a difference? In your
report, please include at least two useful figures (likely histograms) related to your
data rate results.
(Update 19 Sep 2020: See the note on memcpy(dstAddr, srcAddr, numBytes);
It is
idiomatic to pack structs with memcpy
)
Optional for this lab: Reliable Data Transfer
Can you transfer a large (2KB or more) set of data to and from the Artemis board reliably over Bluetooth? Cite any outside sources and produce some reliability metric of your solution. Include summary graphics as necessary. How quickly can you transfer the data?
Best Practices
- Keep individual calculations in loop() short. So long as everything fits in memory, the WSF OS should stay out of your way, for example, if you want to use the PDM. You could explore how to add tasks to WSF OS, but it isn’t necessary for this lab.
- Devise efficient data structures.
- For asyncio “concurrent” operation using
asyncio.gather()
, you should includeawait asyncio.sleep()
calls in your tasks so that they can interleave. Otherwise, they won’t actually run concurrently.
Write-up
To demonstrate that you’ve successfully completed the lab, please upload a brief lab report (<800 words), with code snippets (not included in the word count), photos, and/or videos documenting that everything works and what you did to make it happen. Use the questions asked throughout this document to guide your report.