A few days ago the ev3dev project launched a great feature: nightly image builds. Right after that I got a received a notice that they included in the image for Raspberry Pi 2/3 support for onboard the Bluetooth and needed to test it.
So I did test it. And found out that onboard Bluetooth indeed works… as also onboard Wi-Fi… as also the Brick Pi, no need to disable BT. Yeah, no more USB dongles!
The procedure is very simple – the really important step is freeing the hardware serial port for the BrickPi (both the onboard Bluetooth and the BrickPi need a UART so a soft UART (“miniuart”) is used for BT instead of the default one.
get the latest nightly image build for the Pi2/Pi3 (mine was 26 July 2016) and restore it to a microSD card
insert the card in the Pi3
connect an USB keyboard and a HDMI display to the Pi3
power up the Pi
login (robot + maker) – if you cannot see the login prompt change to the proper console with Alt+F1 or Alt+F2 or Alt+F[n]
run ‘sudo connmanctl’ to configure BT and Wi-Fi (see this tutorial on how to configure Wi-Fi from command line; for BT just run ‘sudo connmanctl enable bluetooth’)
edit the ‘/boot/flash/config.txt’ and uncomment these 4 lines:
dtoverlay=brickpi
init_uart_clock=32000000
dtoverlay=pi3-miniuart-bt
core_freq=250
sudo reboot
remove the display and the keyboard and from now on just connect through Wi-Fi
To test that both Bluetooth and the BrickPi work properly I used a python script to read the NXT ultrasonic sensor (in the first input port) and change the color of my WeDo 2.0 Smart Hub from green to red:
#!/usr/bin/python
# run with sudo
# assumes NXT Ultrasonic at INPUT #1
from ev3dev.auto import *
from gattlib import GATTRequester
from time import sleep
BTdevice = "hci0" # BlueTooth 4.0 BLE capable device
WeDo2HubAddress = "A0:E6:F8:1E:58:57"
InputCommand_hnd = 0x3a
OutputCommand_hnd = 0x3d
RGBAbsoluteMode_cmd = str(bytearray([01,02,06,17,01,01,00,00,00,02,01]))
RGBAbsoluteOutput_cmd = str(bytearray([06,04,03])) # or "\x06\x04\x03"
DELAY = 0.3
# DO NOT FORGET TO CONFIG FOR US sensor:
# sudo echo nxt-i2c > /sys/class/lego-port/port0/mode
# sudo echo "lego-nxt-us 0x01" > /sys/class/lego-port/port0/set_device
#
us = UltrasonicSensor('ttyAMA0:S1:i2c1')
assert us.connected
req = GATTRequester(WeDo2HubAddress,True,BTdevice)
sleep(DELAY)
# configure RBG LED to Absolute Mode (accepts 3 bytes for RGB instead of default Index Mode)
req.write_by_handle(InputCommand_hnd,RGBAbsoluteMode_cmd)
while(True):
if (us.value() < 10):
print("!")
req.write_by_handle(OutputCommand_hnd, RGBAbsoluteOutput_cmd+chr(255)+chr(0)+chr(0))
sleep(DELAY)
else:
print("-")
req.write_by_handle(OutputCommand_hnd, RGBAbsoluteOutput_cmd+chr(0)+chr(255)+chr(0))
sleep(DELAY)
My script need the gattlib library to talk with Bluetooth Low Energy devices. You can install this library with ‘pip’ but first need to install some dependencies:
Thanks to Google, I already knew how to set the Tilt Sensor to «mode 1» and then read it. Unfortunately I was not able to understand the data format used by the sensor until I read the recently released SDK.
So the Tilt Sensor has 3 operating modes:
Angle (the default mode) = 0
Tilt = 1
Crash = 2
In this post I’ll explain how to read just the Tilt Mode as is similar to the original WeDo Tilt Sensor operating mode (and also because I still have to organize all my SDK notes).
The WeDo 2.0 ports can also be configured to read analog values in 3 formats: RAW (0), PERCENTAGE (1) and SI (2). Again, I will use just the SI format.
To change the operating mode to Tilt Mode we write this command to the Input Command characteristic (0x003a):
0102012201010000000201
The meaning is similar to the command used in last post for setting the RGB LED to Absolute Mode, the only difference is the Device Type (22h is the Tilt Sensor).
In Tilt Mode and SI Format there are 6 possible values we can read from the Tilt Sensor:
These values are read from the SensorValue characteristic (0x0032) in a 6-byte format where the first 2 bytes are always “0201” and the other 4 bytes are a IEEE754 float format representation of the value – and now I finally know how to understand those values!
So with just a crude python script we can use the WeDo 2.0 Hub with a Tilt Sensor to control a Mindstorms EV3 car:
#!/usr/bin/env python
# run with sudo
from ev3dev.auto import *
from gattlib import GATTRequester
from time import sleep
BTdevice = "hci0" # BlueTooth 4.0 BLE capable device
WeDo2HubAddress = "A0:E6:F8:1E:58:57"
SensorValue_hnd = 0x32
InputCommand_hnd = 0x3a
# assume TiltSensor at Port #1, change to Mode 1 (Tilt) and Unit = SI
# TiltCmdMode1= "0102012201010000000201"
TiltCmdMode1 = str(bytearray([01,02,01,22,01,01,00,00,00,02,01]))
# Sensor value should read:
# "0201" + a 4 byte value (LITTLE_ENDIAN)
# representing a float in IEEE754 format
# http://www.h-schmidt.net/FloatConverter/IEEE754.html
TILT_SENSOR_DIRECTION_NEUTRAL = "0000" # 0
TILT_SENSOR_DIRECTION_BACKWARD = "4040" # 3
TILT_SENSOR_DIRECTION_RIGHT = "a040" # 5
TILT_SENSOR_DIRECTION_LEFT = "e040" # 7
TILT_SENSOR_DIRECTION_FORWARD = "1041" # 9
TILT_SENSOR_DIRECTION_UNKNOWN = "2041" # 10
DELAY = 0.3
STEP = 0.175
DT = 75
tilt_data=[0,0,0,0,0,0]
m1 = LargeMotor(OUTPUT_A)
m2 = LargeMotor(OUTPUT_B)
m1.run_direct()
m2.run_direct()
m1.duty_cycle_sp=0
m2.duty_cycle_sp=0
req = GATTRequester(WeDo2HubAddress,True,BTdevice)
sleep(DELAY)
# try reset - none works
# req.write_by_handle(InputCommand_hnd,"\x22\x44\x11\xAA")
# req.write_by_handle(InputCommand_hnd,"\x44\x11\xAA")
# sleep(DELAY)
# Configure Tilt Sensor
req.write_by_handle(InputCommand_hnd,TiltCmdMode1)
sleep(DELAY)
while True:
# read Sensor
data=req.read_by_handle(SensorValue_hnd)[0]
if data:
for i,b in enumerate(data):
tilt_data[i]=ord(b)
if (tilt_data[4] == 0x00) and (tilt_data[5] == 0x00):
print("NEUTRAL")
elif (tilt_data[4] == 0x40) and (tilt_data[5] == 0x40):
print("BACKWARD")
m1.duty_cycle_sp=-DT
m2.duty_cycle_sp=-DT
sleep(STEP)
m1.duty_cycle_sp=0
m2.duty_cycle_sp=0
elif (tilt_data[4] == 0xa0) and (tilt_data[5] == 0x40):
print("RIGHT")
m1.duty_cycle_sp=-DT
m2.duty_cycle_sp=DT
sleep(STEP)
m1.duty_cycle_sp=0
m2.duty_cycle_sp=0
elif (tilt_data[4] == 0xe0) and (tilt_data[5] == 0x40):
print("LEFT")
m1.duty_cycle_sp=DT
m2.duty_cycle_sp=-DT
sleep(STEP)
m1.duty_cycle_sp=0
m2.duty_cycle_sp=0
elif (tilt_data[4] == 0x10) and (tilt_data[5] == 0x41):
print("FORWARD")
m1.duty_cycle_sp=DT
m2.duty_cycle_sp=DT
sleep(STEP)
m1.duty_cycle_sp=0
m2.duty_cycle_sp=0
elif (tilt_data[4] == 0x20) and (tilt_data[5] == 0x41):
print("UNKNOWN")
else:
print("No data")
sleep(0.1)
Just a warning note: I ran a first version of this script in my Ubuntu laptop (without the EV3 parts) and it always fails at first run (“No data”, no matter what I do with the Tilt Sensor) but if I abort the script and run it again while the Hub is still in discovering mode then it always works. But when the same script runs in the EV3 it almost always works at first try.
And of course I do have a good explanation: gremlins! What else?
After some head aches with the WeDo 2.0 SDK I found out that the WeDo 2.0 Hub has 2 modes for the RGB LED device:
Indexed
Absolute
“Indexed” is the one I used before – only 10 colors are available. This the mode used in the WeDo 2.0 App, and when in this mode the command used to write to the RGB LED accepts only one byte as argument, which works as an “index” to 10 predefined colors. Why only 10 if the same byte can address up to 255 colors? Internal memory limitations?
The same command accepts also 3 one-byte arguments (Red, Green and Blue) but only when the RGB LED mode is “Absolute”. This is clear in the SDK… what is not so clear is how to change from default (power on) Indexed mode to Absolute?
After reading many Java files I found out how: we use the “Input Command” characteristic (handle 0x3a) and send it this command:
0102061701010000000201
I’ll explain the format of the “Input Command” in another post, but for now this is the meaning:
first two bytes (0102) is the header used to set definitions
the third is the port of the device (06 is the RGB LED)
the fourth is the type of the device (17h a RGB LED)
the fifth is the mode (01 is Absolute, 00 is Indexed)
the sixth to nineth bytes is the delta for notifications to be noticed (so 01 00 00 00 = 1d is the minimum)
the tenth byte is the unit format to be used (02 is “SI”, internation standard)
the eleventh and last byte is to disable or enable notifications (01)
So we can now use any of the 16777216 color tones available:
The pyhton script used for the video above (the video just shows a small part)
#!/usr/bin/python
# run with sudo
from gattlib import GATTRequester
from time import sleep
BTdevice = "hci0" # BlueTooth 4.0 BLE capable device
WeDo2HubAddress = "A0:E6:F8:1E:58:57"
InputCommand_hnd = 0x3a
OutputCommand_hnd = 0x3d
RGBAbsoluteMode_cmd = str(bytearray([01,02,06,17,01,01,00,00,00,02,01]))
RGBAbsoluteOutput_cmd = str(bytearray([06,04,03]))
DELAY = 0.3
req = GATTRequester(WeDo2HubAddress,True,BTdevice)
sleep(DELAY)
# configure RBG LED to Absolute Mode
req.write_by_handle(InputCommand_hnd,RGBAbsoluteMode_cmd)
# loop all colors
while True:
for blue in range (0,256,16):
for green in range (0,256,16):
for red in range (0,256,16):
req.write_by_handle(OutputCommand_hnd, RGBAbsoluteOutput_cmd+chr(red)+chr(green)+chr(blue))
Great news – LEGO Eduction released the WeDo 2.0 SDK today!
After digging into it, I found the information needed to control the Piezo: as expected, it’s controlled by the same handle that is used for controlling the motor and the RGB LED (0x003d). The “port” is “05” and the “command” to activate the Piezo is “02”, followed by a payload of “04” bytes containing:
the Frequency in Hz (2 bytes, reversed)
the duration in ms (2 bytes, reversed)
So to play a “C” (or “Do”, 261 Hz) during 1/8 of a second (125 ms) we use this command: