RC software test 1 passed

Courtesy of the creator of SMBus2 on GitHub, the joysticks are working perfectly.  Here’s the code.  Time now to move on to the network connection with Penelope.

import time
from smbus2 import SMBusWrapper, i2c_msg 

    with SMBusWrapper(1) as bus:

        bus.write_byte_data(0x40, 0x76, 2)
        bus.write_byte_data(0x41, 0x76, 2)
        while True:

            msg = i2c_msg.read(0x40, 2)
            data = list(msg)

            assert (len(data) == 2), "Joystick 0 data len: %d" % len(data)

            if (data[0] > 127):
                X_0 = data[0] - 256
                X_0 = data[0]

            if (data[1] > 127):
                Y_0 = data[1] - 256
                Y_0 = data[1]
            msg = i2c_msg.read(0x41, 2)
            data = list(msg)

            assert (len(data) == 2), "Joystick 1 data len: %d" % len(data)

            if (data[0] > 127):
                X_1 = data[0] - 256
                X_1 = data[0]

            if (data[1] > 127):
                Y_1 = data[1] - 256
                Y_1 = data[1]

            print "X_0 = %d | Y_0 = %d | X_1 = %d | Y_1 = %d" % (X_0, Y_0, X_1, Y_1)

except KeyboardInterrupt:


RC hardware complete…

courtesy of lots of extra layers of the Pimoroni PiBow Ninja.  They also did a couple of custom cuts for Penelope’s PiBow so I could lower her by 2 layers (6mm) making space to include the battery warmer.  Penelope is however still waiting for the Garmin LiDAR-Lite v3HP.

RC hardware

RC hardware

I wish I could say the same about the software.  The problem is how to access these I2C Hall Effect Joysticks.  Although “i2cdetect -y 1” can see them, none of three different I2C python libraries (smbus, smbus2 and pigpio) can read the data correctly from them.  The problem is that the joysticks’ I2C does not need a register, you simply read two bytes from the I2C address, yet none of these libraries successfully doing this.  A google query found my own post high in the rankings which is disappointing when hoping to find someone else’s solution, so I’ve contract Grayhill directly.

Until this is solved, there’s little point adding the code to connect to Penelope (and for her to be listening), nor having Ivy load the code on boot.  Very frustrating.

WAP RPi 3B+ and Stretch

So Hermione is a 3B running Jessie from February 2017 due to network and I2C problems added in the March release.

Penelope is now a 3B+ running Stretch.  As a result, I want to update to the latest WAP software and I2C.  The biggest problem for me is setting up the isolated Wireless Access Point.  I’ve finally solved it with a lot of help from my friends on the RPi Forum.  Here’s how:

To set up an isolated AP:

  • Follow these instructions up to but not including “ADD ROUTING AND MASQUERADE” section.
  • Comment out any “NETWORK={” entries in /etc/wpa_supplicant/wpa_supplicant.conf
  • reboot

To disable the isolated AP to contact the internet for apt-get updates etc:

  • In /etc/default/hostapd set DAEMON_CONF=””
  • Reinstate the “NETWORK={” entries in /etc/wpa_supplicant/wpa_supplicant.conf for your home network ssid etc
  • Comment out the AP interface IP details in /etc/dhcpcd.conf
  • reboot

To solve the I2C, I’m waiting for the Garmin LiDAR-Lite v3HP which has been consistently delayed by up to 8 weeks since (at least) the start of February i.e. it’s still delayed by up to 8 weeks, two months later!  The Garmin LiDAR-Lite v3 exposed the I2C problem: it was strictly running I2C rules whereas the RPi I2C included an incompatible bend of the rules commonly supported many other devices e.g the MPU-9250 IMU.  I’m hoping the delay is due to the fact they are updating their I2C implementation to support the bent rules.


P.S. Penelope has a new salad bowl:

Penelope's Salad Bowl

Penelope’s Salad Bowl

Doesn’t bode well

I’ve been tinkering with the existing PCB layout (shown below) to see if power supply changes fix the Garmin LiDAR Lite (GLL) I²C interference.

Hermione's closeup

Hermione’s closeup

I’ve basically removed the black cuboid 1.5A 5V switching power regulator using the space instead to add a 680uF electrolytic capacitor between 5V and ground near the GLL as specified in the docs.  I then powered her up directly via the PCB, rather than indirectly via the RPi micro USB 5V output GPIO pin.  I used a lab power supply which can handle up to 5A.

Hermione showed identical I²C errors with the GLL plugged in, even if not used.  The PSU showed only 0.45mA at boot, 0.5mA with Hermione’s code running passively, and about 0.6A while she was ‘flying’.  This strongly suggests the new PCB won’t solve the problem either.  I am running out of fingers to cross 🙁

The option of using the GLL PWM output is still open, but I’m not keen to swap over to using the pigpio library this requires as it uses a daemon process in the background, unlike RPi.GPIO and RPIO which both directly access the /dev/gpio devices.  I wonder how hard it would be to write my own PWM C library with python wrapper to epoll the GLL PWM pin?  It would require a new PCB revision as would pigpio so there’s a PITA there too.  For some reason, probably because this all just works on Zoe, I still want to keep banging my head to solve the I²C problems.

Back on the wagon

I’ve flown my Mavic probably for 20 minutes over the course of 5 short flights simply to get familiar with the controls while dodging the rain showers of the last couple of days.  I’m back inside again trying to track down why Hermione has started throwing her I²C wobbly again.

Motion processing is working well, keeping processing close to the minimum 100Hz regardless of other sensor inputs – here 156 samples were processed in 1.724s.

Processing rate

Processing rate

Garmin’s height is running stably at the intended 20Hz and it’s well withing the accuracy possible for distances less than 1m

Garmin LiDAR v3

Garmin LiDAR v3

Here’s the problem though: the IMU is fine for 862 samples averaged into the 155 motion processing blocks, showing just gravity as Hermione sits on the ground, but suddenly the IMU values spike for no reason for the 156 sample average.  Note that this happens only when the Garmin is plugged in.  There are in fact two spikes: the first is shown, the second causes an I/O exception and the diagnostics are dumped:

IMU stats

IMU stats

I’ve tried power supplies up to 3.4A, both battery and mains powered; I’ve resoldered various critical PCB joins; I’ve added the 680uF capacitor as the Garmin spec suggests despite Zoe being fine without it, and I’ve used a newly flashed SD card, all to no avail.

I have two things left to try:

  • currently the Garmin is read every motion processing loop, despite being updated at 20Hz; the spec says there’s an interrupt, but as yet, I’ve not got it to work.  Must try harder!
  • Failing that, I’ll have to replace the MPU-9250 with another, and see if the current one is faulty.

Beyond these two, I’m out for ideas.

Hermione’s first flights

Hermione’s been out and about using the full X8; her first flight shot her into the air at hyper-speed due to a FIFO-overflow that the code didn’t catch.  With the code fixed and a replacement motor installed, I tried again.  After a couple of reasonable flights proving the X8 PIDs needed no tuning, they then started to fail for a couple of reasons:

  • FIFO overflows
  • I2C I/O errors

Both types were caught and killed the flights, but why were they happening?

  • The FIFO errors I think are due simply because I’m now pushing the limits of my A+’ performance; adding an exception to ensure the FIFO was processed as a priority when the FIFO fills over 50% has resolved that.  However…
  • The I2C I/O errors seem (after lots of testing) to be related to the Garmin LiDAR-Lite.  If it’s plugged in, regardless of whether I’m sampling it or not, I get the I2C I/O errors.  If it’s unplugged, all works perfectly.

I have various things to try out:

  1. It could be due to the extra power-draw when it’s plugged in.  Easy to test as I’ve just bought a huge 4S2P 6600mA LiPo:

    GensAce 4S2P 6600mAh LiPo

    GensAce 4S2P 6600mAh LiPo

  2. It could be confirmation of a rumour I’d heard up that the LiDAR-Lite V2 wasn’t good at sharing the I2C bus with anyone else – I hope the V3 doesn’t suffer from the same.
  3. It could be due to noise on the bus caused by the 20cm long cables.  I picked up some stuff from my PX4FLOW investigations about wiring requirements for long cables.
  4. Mentioned in the PX4FLOW docs, and also in the Garmin LiDAR Lite V3 instructions is the need for a decoupling capacitor between Vcc and ground.

If / when I can get the Garmin working together with the IMU, then it’s all systems go for sustained zero drift flights.

Say ‘Hi!’ to Hermione*

Hermione will be the mother of all that’s gone; she’s a bit heartless at the moment – her HoG will be an RPi A3 when it appears on the market.

Hermione flat-out (with banana for scale)

Hermione flat-out (with banana for scale)

Hermione flat-out (with banana for scale)

Hermione tip-toes (with banana for scale)

The main reason behind building yet another quadcopter is the frame:

  • loads more space for extra sensors
  • folding arms so it’s easily transportable
  • beautiful build – lots of CF and CNC machining
  • lots of extension platforms adding lots of options of where to put all the pieces – installed here are a power distribution board (in the plate sandwich), a plate for her HoG, and a GPS plate sticking out to the left.

The one thing that’s missing are legs – I’ve had the ones supplied before, and they crack and break with the down-falls my testing produces.  There are stronger ones I’ll be getting.  I’ve bought from quadframe.com previously for Chloe so I know the high quality of their frames; the previous frame I’d bought was premature, and sadly I ended up selling it.

But this time, I have a purpose for all the extra space: Hermione will be deployed with GPS, LiDAR x 2, PX4FLOW, a RPi Camera and anything else I can get onto her.  Also the frame supports an X8 layout as well as standard quad format; X8 is where each arm has 2 motors and props: the top motor prop is set up as normal; the lower one is upside down, and the prop is installed on the motor upside down.  The topside and underside props spin in opposite directions.  This format provides more power for heavy lifts and protection against motor / prop damage or failure.  Each prop has its own ESC and separate PWM feed.

Doing the above requires the A3 for its extra cores to do a lot more sensor processing and a new PCB to support the new GPIO pin requirements.

GPS, LiDAR and LEDDAR require UART connections; X8 requires another 4 GPIO pins for the 4 ESCs PWM feed.  One of the sensors – probably GPS – can use the USB port, assuming the A3 has built-in WiFi like the B3.  PXFLOW uses I2C.

My thinking is that LEDDAR and PXFLOW provide term fused inputs to the distance PIDs; GPS provides targets for the flight plan; compass is the yaw input with the the flight plan providing direction of travel so that a camera pointing forwards can always track her progress; Scance Sweep provides object detection overriding the GPS flight path short term to avoid objects.

Together, this means I could set a start and end position for a flight using GPS (either just start and finish or with intermediate check points), and then just set her loose autonomously to track against those points, whether this is simply flying from A to B or getting from the entrance to the centre of a maze, logging “where I’ve been” to ensure she always prioritises new paths through the maze.

Here’s the current PCB eagle layout for Phoebe and Chloe:



  • the right hand side 3 vertical PCB tracks are for the LiPo to 5V regulator – I’m planning on Hermione having a BEC on her PDB (power distribution board), so that opens up space for the rear X8 PWM pins.
  • the left hand side 4 vertical PCB track are I2C extensions in place for the URF, but the space is needed for the the front X8 PWM pins so the I2C extension is moved to the bottom edge and will be used for the PIX4FLOW.

Here’s the first draft of the revised PCB:

Hermione PCB beta

Hermione PCB beta

PIN usage:

  • pins 4, 6, 8, 10 and 12 are used for LEDDAR as now but extended outwards
  • the GPIO pins for PWM are completely reworked for X8 format as is the MPU9250 interrupt pin; I’ll probably end up adding X8 before I add Scanse Sweep simply to fill the gap between now and its delivery
  • I2C is extended on the lowerside for the PX4FLOW
  • That leaves a selection of pins and space on the topside for Scanse Sweep – I need to get wiring specs for this; and alternative is via USB using a UART to USB converter – I have one of these already for use configurating LEDDAR by my Windows PC.
  • GPS will also be via USB, assuming the WiFi is now build into the A3 board or I might use a mini USB hub like on of these that I used already for other projects.

Regarding WiFi, to extend the range, I’m going to replace the (hopefully) on board antenna with a HiRose U.FL connector, as per here.  That will then allow me to connect to a U.FL to RP-SMA cable, and place a higher gain antenna on the board.

I’ve still  left a few breadboard pins at the base as they may turn out to be useful for LEDs, buzzers etc.

As you can see, there’s a lot that needs to be done, but it breaks down into lots of separate blocks to keep me busy until the A3 and Scanse Sweep arrive.  The first step is simple: move Chloe’s HoG over to Hermione’s frame, and get her stable.

*Penelope (as in “Pitstop”) was a close runner up, but with my existing HoGWARTs WAP installation notes, Hermione (as in “Granger”) won the ballot outright.

Snow White

Just sharing a couple of photos.

The first is my new prototype Pi with a discrete level shifter used to drive the URF I2C at 5V, while using 3.3V over the RPi I2C interface.  The type of MOSFET is not critical as long as its Vgs is much less than 3.3V – many use the BSS138 or 2N7000 – I just used a couple from my stash which fit breadboards better.  The next step is yet another PCB for Phoebe*:

Discrete I2C level shifter

Discrete I2C level shifter

The second is Zoe who I’m in the process of upgrading to the latest Raspian Jessie Lite – this is a start-from-scratch ‘update’ so I’ll blog the step next.

Zoe litening

Zoe litening

Zoe’s upgrade has so far gone pretty smoothly except for one minor glitch with updating to the new networking interface model and not having a GUI.  The one thing that’s made it a lot easier is that I’ve not had to dismantle her at all.  Also, in the past I’ve used a 10-port powered USB hub for WiFi, keyboard and mouse, but here I have a 2-port USB hub attached to Zoe’s OTG USB connector.  In all, that’s made the upgrade process much less painful than I was expecting.

*Nope, this URF won’t work with Phoebe – even with the level shifter, when I updated the I2C baudrate to 400kbps, the simple test program blocked, just like Phoebe’s own URF does on 3.3V. I also ran Zoe at 100kbps by mistake during her litening transformation, and that proved 100kbps is not fast enough to empty the IMU FIFO as fast as it is being filled.


Progress report

Although the blog’s been quiet, I am still tinkering with both Zoe and Phoebe.

Zoe’s drift is better with 3 changes:

  • I’ve changed my 0g offset calibration so that it’s now done standing on her feet, and an average of 4 readings are taken with her rotated 90° each time and an average used
  • I’ve changed the double sided foam sticking her HoG to the frame – it’s all a bit of a balancing act between having her held down firmly, and yet also providing buffering against vertical prop vibrations
  • I’ve change the code which controls how many samples are per motion processing – there was a minor bug in the timing I knew of, but until now had not thought it worth the effort fixing it.

Here’s the result.

I don’t think it’s going to be possible to get things much better – my guess is the 0g offsets are the dominant contributor to the reduced drift, and they vary. Here’s the output from three runs – the first with her resting on her props, and the other two on her feet; the entries with the Z axis set to 0 are the averages of the preceding 4 calibration test entries. 8000 is roughly 1g to give you an idea of the size of these offsets.

0g calibration results

0g calibration results

The progress with Phoebe is less positive; with the SRF02 rangefinder installed, the code blocks during initializion; i2cdetect can see the sensor so I presume it’s because the sensor is only rated to 100kbps I2C baudrate.  There is a similar URF from a different manufacturer which does support 400kbps, but only if the I2C master (the Raspberry Pi) supports clock stretching; sadly the RPi I2C driver does not, although that may be fixed soon apparently.  However, until that happens, height control using the URF is blocked.

Without the sensor, she’s flying, but occasionally seems to decide to drift; it’s not a steady drift, it suddenly kicks in as though she gets a dodgy reading from the sensors.  I then have to kill the flight before she gains too much height.  It’s not clear to me yet what, if anything I can do about this.

The benefits of having a Brazilian…

comment on the blog: courtesy of Gustavo yesterday, I’m now able to empty the FIFO a batch at a time with a single I2C read (12 bytes) rather than 12 reads of 1 byte.  That’s made my code a lot faster, buying even more time to check other sensors etc.

It’s all to do with i2c.readList (i2c.smbus.read_i2c_block_data): I’d used readList when I was accessing the data registers direcly; reading 14 bytes from register 59 actually gave me a single byte each from registers 59 to 72 – the full set of sensor data I needed.  As a result, I’d ruled out using readList at register 116 (FIFO) getting me 12 bytes per read from just that single register; but that’s what Gustavo does and sure enough it worked for me too.

Sadly a quick flight this morning still showed the same negative G problems – have a look at the az values – these should read roughly 16384 at hover; these aren’t all the samples, just the ones showing negative G during the 8s flight:

[WARNING] (MainThread) __init__ 1397, Zoe is flying.
[WARNING] (MainThread) __init__ 312, SRD:, 1
[WARNING] (MainThread) __init__ 411, IMU core temp: 19.888756
[WARNING] (MainThread) fly 1522, fly = False, flight plan = , calibrate_0g = 0, hover_target = 150, shoot_video = False, vvp_gain = 400.000000, vvi_gain = 200.000000, vvd_gain= 0.000000, hvp_gain = 1.500000, hvi_gain = 0.100000, hvd_gain = 0.000000, prp_gain = 110.000000, pri_gain = 11.000000, prd_gain = 0.000000, rrp_gain = 90.000000, rri_gain = 9.000000, rrd_gain = 0.000000, yrp_gain = 80.000000, yri_gain = 8.000000, yrd_gain = 0.000000, test_case = 1, rtf_period = 1.500000, tau = 5.000000, diagnostics = False
[WARNING] (MainThread) load0gCalibration 551, 0g Offsets:, 0.000000, 0.000000, 0.000000
[WARNING] (MainThread) fly 1522, fly = True, flight plan = fp.csv, calibrate_0g = 0, hover_target = 380, shoot_video = False, vvp_gain = 400.000000, vvi_gain = 200.000000, vvd_gain= 0.000000, hvp_gain = 1.500000, hvi_gain = 0.100000, hvd_gain = 0.000000, prp_gain = 110.000000, pri_gain = 11.000000, prd_gain = 0.000000, rrp_gain = 90.000000, rri_gain = 9.000000, rrd_gain = 0.000000, yrp_gain = 80.000000, yri_gain = 8.000000, yrd_gain = 0.000000, test_case = 0, rtf_period = 1.500000, tau = 5.000000, diagnostics = False
[WARNING] (MainThread) load0gCalibration 551, 0g Offsets:, 0.000000, 0.000000, 0.000000
[WARNING] (MainThread) fly 1661, pitch -1.718589, roll -2.401715
[WARNING] (MainThread) fly 1662, egx 0.000000, egy 0.000000, egz 0.976289
[WARNING] (MainThread) fly 1739, 0 data errors; 0 i2c errors; 0 2g hits
[CRITICAL] (MainThread) readFIFO 469, ax: -20304; ay: 8588; az: -556; gx: 1501; gy: 1042; gz: 1181!
[CRITICAL] (MainThread) readFIFO 469, ax: 6156; ay: -784; az: -552; gx: -379; gy: 454; gz: -75!
[CRITICAL] (MainThread) readFIFO 469, ax: 1580; ay: -2772; az: -564; gx: -2126; gy: 304; gz: -339!
[CRITICAL] (MainThread) readFIFO 469, ax: -8784; ay: -1812; az: -608; gx: -2906; gy: 239; gz: -864!
[CRITICAL] (MainThread) readFIFO 469, ax: 5016; ay: 1420; az: -636; gx: -1649; gy: 2227; gz: 1277!
[CRITICAL] (MainThread) readFIFO 469, ax: 9652; ay: -1816; az: -732; gx: -1665; gy: 1730; gz: 751!
[CRITICAL] (MainThread) readFIFO 469, ax: -84; ay: 2328; az: -2392; gx: -2358; gy: 1965; gz: 926!
[CRITICAL] (MainThread) readFIFO 469, ax: -5884; ay: -1184; az: -108; gx: -75; gy: 1438; gz: 351!
[CRITICAL] (MainThread) readFIFO 469, ax: -2180; ay: 7628; az: -2316; gx: 493; gy: 2743; gz: 384!
[CRITICAL] (MainThread) readFIFO 469, ax: -152; ay: 5336; az: -2208; gx: 407; gy: -860; gz: -703!
[CRITICAL] (MainThread) readFIFO 469, ax: 21212; ay: -2948; az: -392; gx: -175; gy: -1606; gz: -538!
[CRITICAL] (MainThread) readFIFO 469, ax: -7704; ay: -5088; az: -1184; gx: -899; gy: 2470; gz: 10!
[CRITICAL] (MainThread) readFIFO 469, ax: 4904; ay: -2688; az: -676; gx: -1409; gy: 2067; gz: 649!
[CRITICAL] (MainThread) readFIFO 469, ax: 11732; ay: 3232; az: -976; gx: -1023; gy: 1170; gz: 1019!
[CRITICAL] (MainThread) readFIFO 469, ax: 4920; ay: 2740; az: -3212; gx: -552; gy: 958; gz: 667!
[CRITICAL] (MainThread) readFIFO 469, ax: -10364; ay: 5020; az: -2744; gx: -553; gy: 839; gz: 310!
[CRITICAL] (MainThread) readFIFO 469, ax: 4104; ay: -1516; az: -2756; gx: -693; gy: 46; gz: 654!
[CRITICAL] (MainThread) readFIFO 469, ax: -10048; ay: 5772; az: -20; gx: -686; gy: 283; gz: 1207!
[CRITICAL] (MainThread) readFIFO 469, ax: -6844; ay: 5884; az: -1868; gx: -424; gy: 974; gz: 703!
[CRITICAL] (MainThread) readFIFO 469, ax: 6428; ay: 1472; az: -368; gx: -394; gy: 496; gz: 582!
[CRITICAL] (MainThread) readFIFO 469, ax: -3064; ay: 9312; az: -1852; gx: -575; gy: 2588; gz: 801!
[CRITICAL] (MainThread) readFIFO 469, ax: -840; ay: 6392; az: -1944; gx: -366; gy: 1737; gz: 149!
[CRITICAL] (MainThread) readFIFO 469, ax: -2468; ay: 10792; az: -1580; gx: -737; gy: -727; gz: -65!
[CRITICAL] (MainThread) readFIFO 469, ax: -4448; ay: 2652; az: -3624; gx: -974; gy: 639; gz: -111!
[CRITICAL] (MainThread) readFIFO 469, ax: 4604; ay: 7728; az: -4000; gx: -560; gy: -61; gz: -529!
[CRITICAL] (MainThread) readFIFO 469, ax: -23048; ay: 9136; az: -4992; gx: 1366; gy: -524; gz: -5!
[CRITICAL] (MainThread) readFIFO 469, ax: -3860; ay: -1384; az: -488; gx: 1241; gy: -1463; gz: 293!
[CRITICAL] (MainThread) readFIFO 469, ax: -12488; ay: 6820; az: -516; gx: 2382; gy: -3340; gz: -664!
[CRITICAL] (MainThread) readFIFO 469, ax: -2024; ay: -7380; az: -40; gx: -1119; gy: -831; gz: 76!
[CRITICAL] (MainThread) readFIFO 469, ax: -3676; ay: 1472; az: -1756; gx: -687; gy: 6099; gz: 2686!
[WARNING] (MainThread) fly 2068, IMU core temp: 18.121548
[WARNING] (MainThread) fly 2069, motion_loops 934
[WARNING] (MainThread) fly 2070, sampling_loops 9522
[WARNING] (MainThread) fly 2072, 30 data errors; 0 i2c errors; 0 2g hits

That’s 30 errors detected out of 9522 samples, so not terrible, but not perfect.  I was flying her at her new high speed sampling rate of 1kHz and with the accelerometer low pass filter set to 0 (460Hz).  Next step is to turn off the protective code that skips these negative G values and have another go with alpf 2 (92Hz) to see what happens. Certainly the other errors of -32768 in the gyro have now gone, so it’s worth a try.