Category: Crazyflie

A couple of weeks ago we found the NeoPixel ring from Adafruit at a local shop, we had to attach this neat board to our copter and see what cool effect we could make with it. The ring has 16 RGB LEDs that can be driven independently with only one data wire. The LEDs the Neopixel contains are called WS2812.

This post is describing the development of a WS2812 driver for the Crazyflie. A later post will show the usage we did of it (fairly limited in comparison of the seemingly endless possibility, as usual we have more ideas than time to execute them :-).

The WS2812 LED

First of all we needed to see how to control the LEDs. The protocol used by the WS2812 is quite simple but special. All LED on the ring have a data input and a data output pin in a chained manner. Colors of the LED is send over the Data line to the first LED that will save it internally. When sending a second color the first LED will send the saved color to the second LED. And so on, by sending 16 color data we can set the color of all LEDs of the ring independently. Colors are sent as 24 bits (8 bits per component).

Up to there nothing is really peculiar, the system is pretty neat and allows to control a lot of LEDs with just one IO. The problems comes with the Bit encoding:

ws2812_format

Bits are encoded with pulse width: a short pulse means 0 and a long pulse means 1. The bitrate is of about 800KHz and the tolerances on bit timing is pretty tight. As there is no hardware peripheral dedicated for this (on common microcontrollers), this bitstream needs to be implemented either by bit-banging or by being a bit creative with the peripheral we actually have at our disposal.

Driver implementation

Adafruit did implement a driver for the WS2812 for Arduino, this is a software implementation that uses the CPU to implement the signal. However at that bitrate a software/bit-bang implementation have to be timed carefully and the easiest for that is actually to implement it in assembler. Arduino runs on an AVR processor and these processor are very predictable: each instruction runs in a specified number of clock cycle which makes it possible to implement a timed loop like the one of Adafruit. However this becomes impractical on bigger CPU that implements caches and other optimization that makes it really hard to predict how much cycle each instruction will take.

The Crazyflie runs a STM32F103 based on an ARM Cortex-M3. This is just complex enough to make the ASM-timed loop impractical: The CPU core runs at 72MHz but the flash is slower so a simple cache memory is inserted in the middle and depending of the state of this cache it may take from 1 to 3 cycles to execute an instruction. There is an even bigger problem: a CPU timed loop requires to stop all interrupt and to have the CPU running exclusively on updating the WS2812. We cannot allow that on the Crazyflie that require a 250Hz control loop to stay (controllably) airborne.

We asked Google to see if someone already came with a solution and actually someone did. The Elia’s Electronics Blog posted a neat, well documented, solution using the STM32 Pulse Width Modulation (PWM) capabilities of the STM32 to generate signals that the WS2812 understands. All we needed was a timer/pwm output then. It happens that we have one timer1 output on the Crazyflie extension port:

crazyflie_neopixelring

This is only 3 wires: VCOM for the battery voltage, GND and the timer output.

I started porting the Elia’s code to the Crazyfle. The only free timer we could easily use was different and much more complex than the one Elia uses. So part of the frustrating implementation was to figure out WHY the signal was looking weird on the scope! Finally after an hour or so the code was working:

IMG_20140411_202251

Enhancements

Now that was not quite enough. The driver is using the PWM to generate the 1’s and 0’s pulse width and the DMA is used to feed the bit width independently of the CPU (so that it can do something else, like controlling the copter attitude…). It requires all the 16 LEDs data to be written in the memory buffer before starting the DMA. Each LED has 24 bits color data and each bit will be encoded as 16bit pulse length for the timer. It means that the full ring will take 768Bytes in RAM. It sounds small but its a bit too much for us and, most importantly, it does not scale: if we want a second ring the ram requirement will double.

To fix this I implemented two things: An easy one is that the DMA can do some type conversion and one of these is that it can read 8 bit in memory and write 16 bit in the peripheral. This allows to store only 8 bits pulse width in memory and so divides by 2 the memory requirement, but it still doesn’t scale.

The fix to scaling is double buffering. The STM32F103 does not implement DMA double buffering as such but what it has is close enough: circular DMA with half-transfer interrupt. The idea is to load the 2 first LED in a buffer and to start the DMA for 42 bytes in circular mode. When the DMA has transferred the first LED it triggers the half-transfers interrupt which allows the CPU to replace the first LED data by the 3rd. When the 2nd LED is transferred the transfer-complete interrupt is triggered by the DMA and the CPU fills in the 4th LED in place of the 2nd one. The DMA roll-over at the beginning of the buffer and sends what is now the 3rd LED data. This continues until all the LEDs has been  sent. This solution scales: it requires a 42 bytes buffer for as many LED as we want!

Conclusion

All that process was not really simple. However the result is simple, now the only thing required to control a Neopixel ring in the Crazyflie is:

 

#define BLACK {0x00, 0x00, 0x00}</span>
static uint8_t color[][3] = {{40, 40, 40}, {32, 32, 32}, {16,16,16}, {8,8,8},
                             {4,4,4}, {2,2,2}, {1,1,1}, BLACK,
                             BLACK, BLACK, BLACK, BLACK,
                             BLACK, BLACK, BLACK, BLACK,
                            };

/* ... */
ws2812Send(color, 16);

And it will run nicely without interrupting other tasks like control and communication. Replace 16 by 32 to control 2 Neopixel rings. The current implementation has been pushed in the crazyflie-firmware neopixel_dev branch (still require some clean-ups!). Actual implementation is in ws2812.c and neopixelring.c.

neopixel_flying

The only problem left is not software: At the end of the battery life, the voltage is not enough to fully lit the blue LED. That make all white look orange-ish. The only way to fix this problem would be to step-up the battery voltage higher then the forward voltage of the blue LED. Though it works well enough without that.

Stay tuned for a following post about actual usage of the ring.

Most of last week was spent at the Devoxx France conference in Paris. Even though we didn’t have our talk until the end of the week, we hung around most of the time just developing and working. And of course we also got the chance to meet lots of interesting people and demo the Crazyflie. We worked on some code for the NeoPixel ring from Adafruit, implementing some different effects. We also did a quick hack to be able to change effects when flying using the Leap Motion. Since we haven’t had any time to prepare videos/photos of it and to clean up the code, we will post more about that next week.

We also got a chance to do some other fun things, like flying the FPV Crazyflie over the crowd at one of the keynotes and also record some video while flying though the conference.

 

[pe2-gallery album=”http://picasaweb.google.com/data/feed/base/user/115721472821530986219/albumid/6002584693333310545?alt=rss&hl=en_US&kind=photo” ]

A while ago we posted a tutorial on how to modify the firmware to add logging/parameters and to plot/modify them from the client. We have done a continuation on this tutorial to show how to modify the client to integrate logging and parameters directly into the UI (like we have done on the flight tab). For the tutorial we use our virtual machine to do the development and running the code. Since we continue on the concepts and design made in the first video, it might be a good idea to see that one first.

 

A while ago I started working on a brushless motor control driver for the Crazyflie. I implemented most of it but did not really have time to test it. Recently we have gotten some request and questions about it so we took some time to do some further testing.

Implementing a brushless motor control driver can be done in many ways. If you have brushlesss motor controllers that can be controlled over I2C that could have been one way but usually the brushless motor controller (BLMC) take a PWM input. This is most commonly a square wave with a period of 20ms and a pulse width of 1-2 ms high, were 1 ms is 0%, and 2 ms is 100%. A period of 20 ms means a frequency of 50Hz. This is most often a high enough update rate for R/C electronics like servos etc. but when it comes to BLMC that is not the case. Therefore many new BLMC can read a much higher update rate of up to 400 Hz were the pulse still is 1-2 ms high. That way you can match the BLMC input to the update rate of the stabilization control loop and increase stability. In the code we added a define BLMC_PERIOD where this can be set.

To test this we wanted a frame which was quick to setup and found this. It is based of a PCB just like the Crazyflie and has the four motor controllers with it, perfect! The built in BLMC are based on an the Atmel MCU Atmega8 which is very commonly used in the R/C BLMC which means it is possible to re-flash them with the SimonK firmware. This is know to be a great firmware and enables fast PWM update rate etc. So we built and flashed the firmware configured for the tgy6a which is compatible and it worked right away, yay!

Now we only had to connect the Crazyflie to the BLMC:s on the frame. The BLMC electrical interface for the PWM signal is often a 5V interface but the Crazyflie runs on 2.8V. 2.8V would in most cases be treated as an high input and can probably be used directly but there is no simple way to connect this signal on the Crazyflie. Instead one way is to use the existing motor connectors and the pull-down capability that is already there. Then it is also possible to pull this signal to 5V with a resistor to get a 5V interface so this is what we did. To power the Crazyflie we took the connector of an old battery and soldered it the 5V output of the frame.CF to BL Frame connections

Now it was just a matter of testing it! However as size increases so does the potential damage it can make. We therefore took some precaution and tied it down. First we tested the stability on each axis using the stock values and it worked really well so we decided to not tune it further. The only issue was that suddenly one of the BLMC mosfets burnt. We replaced it and it worked again but don’t know why it burnt. Later when we flew it something was still strange so we have to investigate this.

We will upload the code as soon as it has been cleaned up. Please enjoy a short video of the journey :)

A couple of weeks ago we attached a uBlox MAX-7 GPS module to the Crazyflie (blog post). Back then it was mostly a proof of concept, all we did was to re-route the raw GPS data (in text NMEA format) directly to the PC using the Crazyflie text console port. This allowed us to quickly prove that a GPS can work on the Crazyflie but was not that useful and efficient: the copter did not decode the gps position and a lot of radio bandwidth was used. Last Friday we decided to fix it and to make it clean(er).

The ultimate goal was to measure the Crazyflie speed, if it wasn’t for the rain we could have done the measurement! Anyway, this work allowed us to exercise the debug functionality of the Crazyflie platform and so to see the strength of it but also what needs to be enhanced. In this post we will try to document (at high level) the steps taken to implement the GPS in the Crazyflie. The source code is pushed in the crazyflie firmware and python-client git repos. The Python client code is in the master branch and the firmware code in the gpu_ublox_dev branch (dev branch means that the code is far from final/clean, but it works!).

Electronically the GPS is connected using only 4 pins: VCC, GND, serial RX and serial TX. The serial port is connected to pins 3 and 5 of the expansion header. The power is connected to VCC.

crazyflie-ublox

The electronic was already tested and working so we had 2 tasks left:

  • Decoding the GPS information in the firmware and creating log variables to make the data available for the PC software
  • Updating the GPS tab of the PC software to fetch GPS data from the log subsystem instead of parsing it from the text console

It happens that these two tasks could be done mostly independently and Marcus and I started to work in parallel. The only thing we had to agree upon was which log variable and what scaling to use for the variables. We used the format that the GPS chip is already using which made things easier.

Firmware

For the firmware part, the first step was to acquire the GPS data. GPS chips usually can talk two languages: the standard text-based NMEA and some kind of proprietary binary format, UBX for uBlox. I chose the binary format as it is a lot easier use in C: no text parsing has to be done, all data dirrectly fits in a C structure. But first the GPS has to be setup to output data in binary modes and to output the data we were interested in. To quickly setup the GPS I used a tool that uBlox provides and that permits to generate proper UBX messages:

u-center_msg

Two UBX messages where required: One to disable NMEA output and one to enable the NAV-PVP message which contains basically all data you would want from a GPS (position, speed, date and the accuracy). Once this is sent to the GPS chip it starts to send a NAV-PVP UBX packet once a second. Then, the GPS acquisition loop in the Crazyflie (currently implemented in the UART task, so it has to be moved into a proper driver) just has to wait for an UBX packet, read it and if it is a NAV-PVT packet then extract values from it. The GPX code has been tested on PC using another uBlox receiver connected to a USB serial cable. Then after copying the newly added uartReceiveUbx() function  into the Crazyflie firmware, the GPS acquisition loop looks like this:

  while(1)
  {
    uartReceiveUbx(&msg, 100);

    if (msg.class_id == NAV_PVT) {
      gps_fixType = msg.nav_pvt->fixType;
      gps_lat = msg.nav_pvt->lat;
      gps_lon = msg.nav_pvt->lon;
      gps_hMSL = msg.nav_pvt->hMSL;
      gps_hAcc = msg.nav_pvt->hAcc;
      gps_gSpeed = msg.nav_pvt->gSpeed;
      gps_heading = msg.nav_pvt->heading;
    }

    ledseqRun(LED_GREEN, seq_linkup);
  }

The last thing, to make the data available from the PC, is to add a GPS log block and to add variables to it:

LOG_GROUP_START(gps)
LOG_ADD(LOG_UINT8, fixType, &gps_fixType)
LOG_ADD(LOG_INT32, lat, &gps_lat)
LOG_ADD(LOG_INT32, lon, &gps_lon)
LOG_ADD(LOG_INT32, hMSL, &gps_hMSL)
LOG_ADD(LOG_UINT32, hAcc, &gps_hAcc)
LOG_ADD(LOG_INT32, gSpeed, &gps_gSpeed)
LOG_ADD(LOG_INT32, heading, &gps_heading)
LOG_GROUP_STOP(gps)

Debugging the firmware code can be done using the client log plotter tab, not so nice to look at positioning but good enough to see if it is working (when the accuracy, gps.hAcc,  goes down the GPS has a fix!):

gps_graph_fix

Client

Once the log block was decided, Marcus could start the client development by updating the debug driver. The debug link is a module of the Python client that behaves like a CRTP (the Crazyflie protocol) link but is in fact just some software running offline. It allows to easily develop and debug the client without requiring the usage of a Crazyflie. The debug link was modified to include all variable that the Crazyflie would eventually contain and to give them some value that can be logged.

When this was done, the GPS tab has to be updated to display actual values, a max speed and reset button is also added (the idea was to measure the Crazyflie speed). After fighting more than expected with the QT layouts the result is good enough:

vm-gps-debug

Note that the client uses the Python binding of KDE Marble which has to be compiled manually. Only Marcus has had the courage to do that on his computer, but luckily he also compiled it on the latest Bitcraze VM so that we can all easily enjoy the new GPS tab :).

Merge

Now that the client and the firmware are made separately we ‘just’ have to connect the new client to the new firmware. And guess what? it worked the first time :-) (Yes I know you have no reason to believe me but this time it really worked the first time).

Unfortunately for us last Friday was one of these Swedish rainy day, all we could do was to take turns to stand in the middle of the road outside of our office, in the rain, holding the Crazyflie in a plastic bag and waiting for the GPS to get a fix (people passing by were looking quite strangely at us …). It happens that the rain where not helping at all! And the fact that we don’t have assisted-GPS (yet) means that the GPS would get a fix in 40sec best case, it took about 5-10minutes for us. But eventually we got the fix:

gps-test-fix

Conclusion

One thing we have to work on is the modularity of the firmware. Things like having a clear and easy to use HAL for peripherals on the extension port. It is on our ToDo list and it would have been useful here to do a cleaner job with the firmware implementation. A good thing is that while this implementation is uBlox specific for the firmware part, it is completely hardware-independent on the client side. It means that it is possible to implement any kind of positioning, with other GPS chip or other technology, and as long as this positioning declares the right log variable the client will work with it unmodified.

As for the GPS, uploading assistance data to the Crazyflie would permit to drastically reduce the fix time to about 10-15sec. Also this GPS is capable of 10Hz update rate which would be nice to test. The GPS on a Crazyflie is still mostly a proof-of-concept and is of course not useful for indoor flight. Though with light winds the Crazyflie is pretty capable outdoor, so with GPS capability it could be interesting to experiment a bit with trajectory planing. Of course this is even more true for country with a warm and dry weather :-).

A couple of weeks ago we moved into the Malmö based business incubator Minc. It’s a great chance to get some extra help developing Bitcraze and also to get a chance to meet people from other start-ups. Below is a picture of a bunch of Crazyflies in our lab (i.e the table designated lab).

Bitcraze Lab

On a more technical note, here’s a video of a blimp that was hacked together with some left over party-balloons and a broken Crazyflie that we had lying around. It’s the day after the party, so there wasn’t that much lift left in the balloons (that’s why we have a bunch of them). After a few hacks to the firmware it actually works pretty well! The motors power has been redistributed and only the Yaw regulation is active, which explains that the yaw is still pretty stable, we are thinking about pushing the ‘blimp-mode’ at some point (first we will need more balloons and Helium though :) ).

We have released version 0.5 of the Bitcraze Virtual Machine and version 0.3 of the Raspberry Pi image.

Here are some of the changes for Bitcraze VM 0.5:

  • Upgraded all Ubuntu Packages
  • Installed custom build of KDE Marble with Python bindings
  • Installed PyQtGraph
  • Installed Oracle Java JRE (needed for PyCharm)
  • Installed PyCharm Community Edition 3.1
  • Installed KiCad from repo (using build script, but excluding documentation) to add support for all of our projects in their new format
  • Installed EmbSys RegViewer plugin for Eclipse
  • Replace old BitBucket repos with new GitHub repos and updated the “Update all repos” script
  • Decreased image size (Even with all the new stuff this new version is smaller than the previous)

The full Bitcraze VM 0.5 changelog is available here and the files are available as direct download or as torrent.

Here are some of the changes for the Raspberry Pi image 0.3:

  • Moved configuration files in the fat partition: It is now possible to configure radio link and inputdevice mapping easily from windows/linux/mac. See crazyflie folder.
  • Upgraded all packages
  • Replace old BitBucket repos with new GitHub repos and updated to the latest versions
  • Updated the README.txt with information on how to flash the Crazyflie/Crazyradio from the Raspberry Pi
  • Image size increased from 2GB to 4GB

The full Raspberry Pi image 0.3 changelog is available here and the files is available as direct download or as torrent.

 

Finally after some issues and lots of interruptions we made the move to GitHub. We will keep the BitBucket repos (but as read-only) since there’s lots of inbound links for code and issues.

If you are interested in doing the same move here’s what we did.

Create the reposiory on GitHub, then pull in Mercurial project from BitBucket into a local git repos using git-remote-hg:

git clone hg::https://bitbucket.org/bitcraze/crazyflie-firmware

The new github repos can then be added to the local git. Tags and branches can then be pushed to github. As Mercurial and Git have very different way to handle branches you will have to chose which branch to push and push them manually (we did not find any way to push all at once and we did not want to push all the branches anyway…):

cd crazyflie-firmware
git remote add github git@github.com:bitcraze/crazyflie-firmware.git
git push github master
#Pushing tags
git push --tags github
# Tracking and pushing all interesting branches
git branch --track 2014.01 origin/branches/2014.01
git push github 2014.01
git branch --track gps_ublox_dev origin/branches/gps_ublox_dev
git push github gps_ublox_dev

 

Copying the issues from BitBucket to GitHub can be done using this script. It takes the source user/repo and destination user/repo as arguments. Keep in mind that GitHub doesn’t have the same metadata as BitBucket does. So there’s a JSON file where you set up the mapping between BitBucket kind/status/priority/component and GitHub labels. In this you can set up zero to multiple labels. If a mapping for components is missing then a label with the same name is used. Milestones on BitBucket is mapped 1:1 to GitHub. The needed labels and milestones will be created as they are used while copying the issues.

If you are copying issues to an organization repository you will have to use a GitHub API token for identification, since you cannot log in using the organization username. Here’s an example for what we did for the Crazyflie python clients:

#Migrating tickets from Bitbucket to Github
python migrate.py -g bitcraze -d crazyflie-clients-python -u bitcraze -s crazyflie-pc-client -k your_api_token_here

Two tips: Create a new user that does the migration (like bitcraze-issue-importer) and make sure that you haven’t created any issues in the target repositories before the migration. If you don’t have any issues before then the number will match and all the references will work out of the box (from commit messages and other issues).

To show the functionality in the logging and parameter frameworks we did a video. It shows how to use the Bitcraze virtual machine to add variables and parameters to the firmware and to flash it using the radio bootloader. It also shows how to visualize the log-data using the plotter and to modify parameters from the client.

While cutting the video we noticed that the temperature goes down when we touch the pressure sensor. Either we are more cold-blooded than we should be or there’s a bug in there somewhere…

Edit: Turns out it not a bug. After some discussions this morning (and testing) we arrived at the conclusion that the measurement was correct. The PCB get’s a bit warm when running/charging and a fingertip isn’t necessarily 37C.

After a week of fixing things for the Crazyflie firmware/software RC (release candidate) we have now tagged and built the final release 2014.01 of the crazyflie PC client and firmware. Last week we listed the changes, but for the final version we also made improvements for the logging and communication. This mostly effects the connection and downloading of TOCs and parameters, which improves the connection reliability.

Screenshoot_2014.01.0

With this release there are some updates to the Python API for the Crazyflie. In order to more easily keep the examples in sync with the API we have added some basic examples in the example folder in the repository. We will also update the wiki with these changes.

To download the new version the links are: