Quadcopter part 8: I2C

For the quadcopter I bought a GY-80 arduino compatible 10 DOF (degrees of freedom) sensor IMU. The small board comes with an accelerometer, a gyroscope, a magnetometer, and a pressure sensor. For now I only need the accelerometer and the gyroscope, and this post deals with connecting the sensor board to the beaglebone back and reading out the results by using the BlackLib library.

First some specifications on the sensor board:

  • 3 Axis Gyro
    • ST Microelectronics L3G4200D
    • Address: 0x69
    • Datasheet
  • 3 Axis Accelerometer
    • Analog Devices ADXL345
    • Address: 0x53
    • Datasheet
  • 3 Axis Magnetometer
  • Barometer + Thermometer

Connecting the sensors to the beaglebone black is very straightforward. The sensor board supports both 5V and 3.3V power, and in case of the beaglebone al the I2C lines are 3.3V logic levels, I decided to power the sensors with the 3.3V.

To connect the SDA and SCL lines to the beaglebone black board, you need to realise that the beaglebone comes with 3 I2C busses. Only I2C bus 0 and 1 are by default enabled, and used by the system, the third one can be turned on by using the device tree overlays. But I2C bus 0 isn’t exported to the expansion headers so I will have to use I2C bus 1, on expansion header P9, pins 19 and 20, since these are by default enabled, no additional configuration was required.

When powering up the board:

root@beaglebone:~# i2cdetect -y -r 1
0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1e --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- 53 UU UU UU UU -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- 69 -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77

With the BlackLib library, reading out the sensor data was fairly easy. Sources can be found on my github.

With this final hardware installed on the quadcopter, the assembly has been completed!
wpid-wp-1425224103231.jpeg

Quadcopter part 7: C++ Programming with BlackLib

[UPDATE]: this was on kernel version 3.8.  I’ve started over on kernel version 4.4, and since the PWM code in BlackLib (v2 and v3) aren’t working with 4.4, I’ve decided to do the implementation myself.  All the C++11 options, dialects, etc can be removed from the eclipse project settings.
BlackLib v3 does have a nice Makefile now, so building and using it doesn’t require you to set up a seperate project in eclipse anymore, you just need to specify the include path and the library path.

First you’ll need the cross compiler. For example, on Ubuntu:
sudo apt-get install gcc-arm-linux-gnueabihf

This will give you:
nicky@nicky-Precision-M4800:~/Projects/BlackLib$ arm-linux-gnueabihf-g++ --version
arm-linux-gnueabihf-g++ (Ubuntu/Linaro 4.9.1-16ubuntu6) 4.9.1
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

nicky@nicky-Precision-M4800:~/Projects/BlackLib$ arm-linux-gnueabihf-gcc –version
arm-linux-gnueabihf-gcc (Ubuntu/Linaro 4.9.1-16ubuntu6) 4.9.1
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

However it seems that this version is a bit more recent then the one BlackLib has been developed with, and it won’t compile due to a few missing includes. You can find this fix on my github repo: https://github.com/panic1/BlackLib/tree/unistd

Now I will be using eclipse to build a shared library from this BlackLib code, in my setup I’m using Eclipse Luna with CDT.

  1. Create a new C++ Project, Click next.
  2. Project name: BlackLib
  3. Uncheck default location, and select the location where you checked out the BlackLib code
  4. Select Shared Library, Empty Project
  5. Select cross toolchain (you might need to uncheck the checkmark at the bottom to show all toolchain options), and click next
  6. The toolchain prefix: arm-linux-gnueabihf-. Click finish

Now the fun starts, BlackLib is written with quite advanced C++11 functionality, and the compiler will have to be enable to allow these experimental features:

  1. Right click on the project, select Properties
  2. Go to C/C++ Build, Settings
  3. In the Cross Settings the prefix should be set to arm-linux-gnueabihf- (just in case you’ve forgotten in a previous step
  4. In the Cross C++ Compiler, Dialect, Language standard should be set to ISO C++11
  5. In the Cross C++ Compiler, Preprocessor, add a new symbol __GXX_EXPERIMENTAL_CXX0X__
  6. In the Cross C++ Compiler, Miscellaneous, check the Position Independent Code (-fPIC)
  7. Apply

As we don’t need them in the resulting library, in the project you can right click, go to Resource Configuration, and Exclude from Build the following folders:

  • SPI_SETUP
  • V1_0
  • V2_0/SPI_SETUP
  • V2_0/exampleAndTiming
  • exampleAndTiming.cpp

Now go ahead and build the project, this should result in a libBlackLib.so file we’ll start to use in the next steps.

Using the library in a new project is pretty straight forward:

Create a new C++ project in Eclipse, choose Empty Project and the Cross GCC toolchain, and click next. The toolchain prefix should be arm-linux-gnueabihf-, just like before.

Right click the project, and go to Properties, Project References and select the BlackLib project to be referenced.
Go to C/C++ Build, Settings, and add the path to the BlackLib/v2.0 folder Cross G++ Compiler Includes, and Cross G++ Linker Libraries should include the BlackLib library, and the path to the BlackLib/Debug where the libBlackLib.so file can be found.

To run your application on the Beaglebone board, you will first have to copy the libBlackLib.so file. There are several valid locations where Linux searches for shared libraries, I usually put it in /usr/lib/. Then copy your application binary, location doesn’t really matter, for example /root or /home/debian, make the binary executable (chmod), and run it.

Quadcopter part 6: PWM

[Update] This was on kernel version 3.8.  It had to be completely redone when I performed an upstep to version 4.4.  I will put my findings in a separate post.

I’ve been working on this for a very long time. It was the first time I came into contact with Device Trees in the Linux kernel, and that was a steep learning curve all in its own right, but I also came across a lot technical difficulties and bugs in the beaglebone pwm driver code. Those definitely didn’t help. Here’s the story of enabling 4 PWM pins to do what I want…

Verifying that the PWM output can drive the Turnigy ESCs

The first experiment I wanted to do was to check whether the beaglebone PWM outputs can drive the Turnigy speed controllers. My worry was that the 3.3V PWM signal wouldn’t be enough to drive the 5V controllers.
So I used this guide to control a single (note: single, this will be important later…) PWM output. It was easy to set the period and duty cycle, I verified the timings on the oscilloscope, looking good. I hooked it up to a speed controller, and sure enough: nothing happened. The engine kept beeping happily (it’s an alarm indicating invalid input signal).

Before looking up and ordering a logic level shifter, or designing a transistor inverter circuit to boost the 3.3V to a 5V signal, I decided to give it a try with an Arduino board I had laying around. The Arduino has 5V PWM outputs. Hooking up the beaglebone, the Arduino and the ESCs, I thought it was a good idea to also connnect the grounds for all the devices together, just in case. It worked, the propeller spinning happily on all four engines. I corrected the direction of those propellors that were spinning backwards. In a last attempt I decided to give the beaglebone board just one more try, now that the grounds are all connected together. And that worked too!! Lesson learned: connect ground! Second lesson learned: don’t leave the propeller on the engine when doing tests, I had one of those props fly up to my face when I accidentally set the signal to full power. A quick calculation about the speed that prop reached at full rotational speed, encouraged me to be more careful (I would be stupid to ignore a sharp plastic blade flinging itself around at 500km/h).

Device tree files

To activate the PWM outputs, so that they can be controller from a C++ application, I needed to get to know the device tree overlays. Since the 3.8 kernel Torvalds disallowed the use of platform support code that was quickly flooding the kernel kernel sources. Device trees were to be used instead.
A device tree is a flat file detailing the entire hardware, were all the peripherals are located, which drivers to load, etc, etc…

In order to have some flexibility they are using device tree overlays that can be added on top of a base configuration.

In case of the beaglebone black board, these is a cape manager that can load these device tree overlays at runtime, and the debian image I installed on the beaglebone comes equipped with a full set of example precompiled overlays in /lib/firmware.

In order to load the PWM pins for the quadcopter we need to:
root@beaglebone:/lib/firmware# echo am33xx_pwm > /sys/devices/bone_capemgr.9/slots
root@beaglebone:/lib/firmware# echo bone_pwm_P8_13 > /sys/devices/bone_capemgr.9/slots
root@beaglebone:/lib/firmware# echo bone_pwm_P8_19 > /sys/devices/bone_capemgr.9/slots
root@beaglebone:/lib/firmware# echo bone_pwm_P9_14 > /sys/devices/bone_capemgr.9/slots
root@beaglebone:/lib/firmware# echo bone_pwm_P9_16 > /sys/devices/bone_capemgr.9/slots

and verify with:
cat /sys/devices/bone_capemgr.9/slots
Should return:
0: 54:PF---
1: 55:PF---
2: 56:PF---
3: 57:PF---
4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
8: ff:P-O-L Override Board Name,00A0,Override Manuf,am33xx_pwm
9: ff:P-O-L Override Board Name,00A0,Override Manuf,bone_pwm_P8_13
10: ff:P-O-L Override Board Name,00A0,Override Manuf,bone_pwm_P8_19
11: ff:P-O-L Override Board Name,00A0,Override Manuf,bone_pwm_P9_14
12: ff:P-O-L Override Board Name,00A0,Override Manuf,bone_pwm_P9_16

Or do automatic at bootup by modifying /boot/uboot/uEnv.txt
Add this to the bootargs (in my example I edited a line in the “Example” section):
cape_enable=capemgr.enable_partno=am33xx_pwm,bone_pwm_P8_13,bone_pwm_P8_19,bone_pwm_P9_14,bone_pwm_P9_16

Running into problems

In part 7 I’m explaining how to control the PWM outputs from a C++ application. But during this development I quickly ran into issues. I could control the duty cycle just fine, but the period was always set to 500µs, and couldn’t be changed because INVALID PARAM… It worked just fine with the python script just before.

I checked the code behind the python library, and compared it to the code in the BlackLic C++ library, but the implementation was the same. The pins were controlled by writing to set of files in /sys/devices/ocp3/pwm_test_P8_13.14/. Trying to write 20000000 into the period file will keep failing.

Hunting around the web I quickly found other people encountering the same issue. The PWM on the beaglebone is implemented on three distinct chips. Each of those chips have 2 ehrpwm (enhanced resolution) outputs. The period can only be managed on a per chip basis, and has to be the same for both outputs. However, the beaglebone pwm_test driver exposes both outputs separately, and asserts that any new configuration you want to apply to an output has to be same as the other (chicken or egg?). Maybe it is possible to disable both outputs, change the periods, and enable them again, I haven’t looked into that in greater detail.

The prevailing solution on the web would be to patch the pwm_test driver. The period would be set to 0 in the device tree overlay, and the test_pwm driver would interpret this value and not enable the output. That would allow you to change the period after boot. In my case I wasn’t really interested in being able to change the period, I just wanted it set to 20ms. And if the device tree overlays would allow me to do that, I wouldn’t even need the patch.
Source: http://saadahmad.ca/using-pwm-on-the-beaglebone-black/

I found the device tree source files here: https://github.com/beagleboard/devicetree-source
But I discovered later that I could have also decompiled the precompiled dtbo files in the /lib/firmware folder.

I went ahead and changed the period in these files from 500000 to 20000000 (20ms):
root@beaglebone:~/dts# vim bone_pwm_P9_14-00A0.dts
root@beaglebone:~/dts# vim bone_pwm_P9_16-00A0.dts
root@beaglebone:~/dts# vim bone_pwm_P8_13-00A0.dts
root@beaglebone:~/dts# vim bone_pwm_P8_19-00A0.dts

Compile like this:
dtc -O dtb -o bone_pwm_P9_14-00A0.dtbo -b 0 -@ bone_pwm_P9_14-00A0.dts
dtc -O dtb -o bone_pwm_P9_16-00A0.dtbo -b 0 -@ bone_pwm_P9_16-00A0.dts
dtc -O dtb -o bone_pwm_P8_13-00A0.dtbo -b 0 -@ bone_pwm_P8_13-00A0.dts
dtc -O dtb -o bone_pwm_P8_19-00A0.dtbo -b 0 -@ bone_pwm_P8_19-00A0.dts

Copy to the dtbo files, but make sure you backup /lib/firmware first!
cp *.dtbo /lib/firmware

Rebooting the beaglebone, and still not working…. The period was still 500µs.

It took me a whole while before I figured out what was actually happening. During the search I decided to download and recompiled the kernel, just like the original article suggested. However the howto on rebuilding the kernel is not correct for the kernel image in the standard debian image, instead:
git clone https://github.com/RobertCNelson/bb-kernel.git
cd bb-kernel
git checkout 3.8.13-bone50
sudo apt-get install device-tree-compiler lzma lzop u-boot-tools libncurses5-dev:amd64 libncurses5:i38
./build_kernel.sh

In the folder KERNEL/firmware/capes I could find the same dts files and looking through the kernel code, it seemed like these device tree files are compiled and included in a binary blob somewhere in the kernel binary, instead of being read from /lib/firmware like everyone claimed it was.

Anyway…
I shared the bb-kernel folder over samba, so I could mount it on the beaglebone:
mkdir bb-kernel
mount -t cifs //192.168.1.102/bb-kernel ./bb-kernel -o user=nobody

I also had to change a few commands in bb-kernel/tools/local_install.sh:
where you see:
sudo tar xf "${DIR}/deploy/${KERNEL_UTS}-dtbs.tar.gz" -C "${location}/dtbs/"
replace it with:
sudo tar xf "${DIR}/deploy/${KERNEL_UTS}-dtbs.tar.gz" --no-same-owner -C "${location}/dtbs/"

Now run ./tools/local_install.sh on the beaglebone and the new kernel with the adapted device tree files will be installed on the beaglebone.

That did replace the modules folder, and made me lose my mt7601Usta driver for the wireless antenna. Instead of doing the complete local_install, I just copied the zImage I found the bb-kernel/deploy folder over the /boot/uboot/zImage in a clean debian installation. Reboot, and it worked!

Second thought… maybe you are supposed to copy the dts files into a file of your own, adapt as needed and try to push that into /lib/firmware. So I did a few more experiments:

  • Increase the version number (from 00A0 to 00A1), and hope that the system is intelligent enough to load the one with the highest version. It wasn’t
  • Renaming the file, and using the new name. I renamed bone_pwm_P8_13-00A0.dts to panic1_pwm_P8_13-00A0.dts, compiled it, put it in the /lib/firmware folder. And it does work when you echo the new name into the slots file, but the bootloader doesn’t seem to be able to access the files in /lib/firmware, putting the new name in the uEnv.txt file didn’t work.

Quadcopter part 3.3: Wireless (finally)

Those LogiLink wireless dongles were going nowhere fast, I gave one to a colleague of mine, maybe he has a bit more luck with it. So I decided to count my losses and go with one of those USB wireless interfaces listed on the beaglebone site, and I ordered the UWN200 from Logic Supply. I reverted my beaglebone board to the standard debian image, because I wanted to start afresh and the module worked straight out the package, just like that. The interface is now called ra0 instead of wlan0, but that was not totally unexpected.

The big antenna is also an added bonus, as I expect it to have a longer range for the quadcopter. But reading up on the internet, I do expect that running this as an access point is not possible with the current ralink driver. I will have to find a different solution for that, maybe I’ll set up a wireless AP on my laptop instead.

I measured a 100s average of 24.5MBits/s with iperf, which is just wonderful!

Quadcopter Part 3.2: Fixing the poor wireless performance

I stumbled upon this site from Adafruit.  It talks about a number of problems with Wireless on the Beaglebone. One of that is poor driver support for RealTek based devices, which I’ve also ran into, and it informed me that the HDMI circuit causes a lot of signal interference, and that degrades the performance of small wireless USB dongles in particular because of their close proximity to that circuit.

So their solutions are worth a try as well.
To fix the HDMI interference they propose to:

  • Disable the HMDI circuit in the bootloader configuration
  • Move the USB dongle away from the board, either with a USB extension cord, or with a small USB hub.

In my quadcopter case, I’d rather not add more hardware, and I don’t need the HDMI interface, so I’ll just try to turn it off, following the steps on the Adafruit guide. To be honest, after doing the steps and rebooted the board I don’t see such a major improvement with the HDMI off.

To fix the driver issue, they don’t upgrade the kernel to the testing image, but instead upgrade to the latest stable and then install a service which will reset the wireless dongle during boot, in order to stabilize it. But in my case, when using the testing (3.16) kernel, it does more harm than good: the wireless connection doesn’t appear to come back up after reboot.

The wireless saga continues…

UPDATE: to give you some idea on the level of poor performance. I ran iperf from the beaglebone to my laptop over the wireless network here at home, and it report over a 1 minute interval an average speed of about 450Kbits/sec. If I move the dongle further away from the board, by using a USB hub, I get 730Kbit/sec, which is only marginally better.

UPDATE 2: even the wired network is very limited in bandwidth, more tests are needed to find out what is going in here. In the mean time I ordered a new type of USB dongle, one from the list on the beaglebone wiki. Hopefully that works.

Quadcopter part 3.1: Updating the kernel

Apparently, updating the Linux kernel with a pre-build image from Robert C Nelson on the beaglebone black is extremely easy. The scripts to do so are already installed available in the standard debian build I used.

Following these instructions on a clean debian install, gave a running debian with a 3.16 kernel (I used the testing image).

The LogiLink wireless dongle works out of the box with that kernel, no fiddling with the driver was necessary.

Quadcopter part 3: wireless on the beaglebone

UPDATE: It looks like updating the kernel is an easier solution than replacing the RealTek driver, see part 3.1 for more information.
UPDATE 2: The reason that the wireless performance is so poor on my setup, would be (according to the Adafruit site) because of signal interference with the HDMI circuits. The small USB wireless dongles in particular would be very susceptible to this because of their close proximity. See part 3.2 on how I tried to fix it.

In my previous mail I gave you the parts for my quadcopter project. That list also contained this LogiLink NANO WL0085a wireless dongle. These dongles arrived in the mail today, and right away I set out to try one on the beaglebone black boards, the one that I have already flashed with the debian image I found on the beagleboard website.

It was supposed to just work straight out of the box… It didn’t. This post will describe the things I had to do to make it work.

First thing, the dongle needs to be plugged in before you power on the device. For some reason, it won’t be detected when plugged in after bootup.
And secondly, make sure the beaglebone black is powered via the 5V DC connector. I was able to salvage an adapted from an old discarded routed that had the same connector. Although it was only rated for 1.2A, it does seem to power it properly.

Now, the dongle was receiving power, the networking parameters set, and still it wouldn’t connect to the wireless network here at home. So on with the hunt for a solution I first looked for the device information:

root@beaglebone:~# lsusb
Bus 001 Device 002: ID 0bda:8176 Realtek Semiconductor Corp. RTL8188CUS 802.11n WLAN Adapter
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

With this in hand, Google points me to website stating that the realtek driver provided in the beaglebone image doesn’t work.

So I download the driver sources from the RealTek Drivers page, and copy the zip file to the beaglebone. I didn’t bother with setting up the whole toolchain and kernel headers on my local machine so I could cross compile a simple loadable kernel module for the arm, I decided it would be easier to set it up on the beaglebone itself, and have it compile there.
Before you are able to compile a kernel module, you also need the linux headers. Normally this would just be an “apt-get install linux-headers-3.8.13-bone50“, but this package doesn’t exist. So I had to go look elsewhere for it. And here it is: linux-headers-3.8.13-bone50_1.0precise_armhf.deb. Just install if like you would install any other debian package: “dpkg -i xxxxxx.deb“.

Back to the Realtek driver directory, there is a subdirectory called “driver”, cd into it, and unpack the tar ball. cd into the extracted directory and type in “make ARCH=arm“.

Error.

root@beaglebone:~/Projects/quadcopter/debian_image/realtek_driver/RTL8188C_8192C_USB_linux_v4.0.2_9000.20130911/driver/rtl8188C_8192C_usb_linux_v4.0.2_9000.20130911# make -C /lib/modules/3.8.13-bone28/build M=/root/rfm12b-linux modules
make[1]: Entering directory `/usr/src/linux-headers-3.8.13-bone28'
CC [M] /root/rfm12b-linux/rfm12b.o
In file included from include/linux/timex.h:65:0,
from include/linux/jiffies.h:8,
from include/linux/ktime.h:25,
from include/linux/timer.h:5,
from include/linux/workqueue.h:8,
from include/linux/srcu.h:34,
from include/linux/notifier.h:15,
from include/linux/memory_hotplug.h:6,
from include/linux/mmzone.h:761,
from include/linux/gfp.h:4,
from include/linux/kmod.h:22,
from include/linux/module.h:13,
from /root/rfm12b-linux/rfm12b.c:20:
/usr/src/linux-headers-3.8.13-bone28/arch/arm/include/asm/timex.h:18:24: fatal error: mach/timex.h: No such file or directory

A discussion I found online (here) suggested I just create an empty timex.h file in the linux headers, because it serves no purpose anyway:

root@beaglebone:~# cd /usr/src/linux-headers-3.8.13-bone50/arch/arm/include/
root@beaglebone:/usr/src/linux-headers-3.8.13-bone50/arch/arm/include# mkdir mach
root@beaglebone:/usr/src/linux-headers-3.8.13-bone50/arch/arm/include# touch mach/timex.h

The code compiles, run “make install” to install the module and blacklist the old ones like so:
cd /etc/modprobe.d
echo "install rtl8192cu /bin/false" >wifi_blacklist.conf
echo "install rtl8192c_common /bin/false" >>wifi_blacklist.conf
echo "install rtlwifi /bin/false" >>wifi_blacklist.conf

I rebooted the board, and there I had it: a wireless connection! But results are still a bit depressing. It is very slow, and it occasionally drops out. I read elsewhere online that later versions of the kernel (>=3.12) should work alot better, but I’m not sure whether there is a beaglebone standard image available for that. Maybe I have to cook my own instead, and set up a buildroot (or something) instead.

Quadcopter part 1: the beaglebone black

The first parts for the quadcopter have arrived: the beaglebone black boards that I would like to use as controllers. I bought two, one for use on the quad and another for experimentation and general tinkering. The beaglebone black comes with four PWM output pins I hope can be controlled at the same time, I still have some documentation to read and small experiments to make to find out which interfaces to use best.  Next up would be to figure out which other parts to order first and to get a go at the design of the software to control the quad.