Category: Crazyflie

For a long time, communication inside the Crazyflie was fairly simple. The main STM32 microcontroller handled flight control, sensors, motors and most of the system logic. A second microcontroller handled the radio and power management. The two processors communicated using Syslink, a small packet protocol over UART. Syslink was designed for one fixed connection between two known processors. Both sides were developed together, the number of packet types was limited, and there was little need for discovery or elaborate flow control.

This worked quite well for the system it was designed for.

Crazyflie 2.0 system architecture

Over time, however, the Crazyflie platform has changed. More and more expansion decks contain their own processors, firmware and internal state. Some decks are no longer simply collections of sensors connected over I2C or SPI. They are small embedded systems of their own. This has made communication between processors a recurring part of the platform rather than a special case.

Most of what we are talking about in this blog post is very much work in progress: this is a work that we are doing as part of yet another deck that will have an microcontroller.

From Syslink to CPX

Syslink was created for one link between two processors. Later, when developing the AI deck, we encountered a different problem. The system contained several processors and several communication links. Packets could travel between the Crazyflie STM32, the ESP32, the GAP8 and a computer connected either through Wi-Fi or through the normal Crazyradio path. This led to CPX, the Crazyflie Packet eXchange protocol. One of its main roles is routing packets between processors and transports.

CPX solved an important practical problem, and routing is still useful. At the same time, our experience with it has shown that not every processor link needs the whole routed communication model. For a connection between two known processors, requiring routing and application functions to be part of the same protocol can make the stack heavier than necessary.

The current work is therefore not intended to replace CPX. Instead, we are trying to separate the different responsibilities more clearly. CPX could eventually be one of the services transported over a lower-level peer-to-peer link when routing is required.

Yet another processor on a deck

We are currently working on a new deck with a processor that needs to communicate with the Crazyflie. The fastest solution would have been to create another deck-specific UART protocol. The endpoints are known, the initial set of commands is fairly small, and such a protocol would probably have been finished much earlier. That would have been a completely reasonable engineering choice but it would also have added one more custom processor protocol to the platform.

We expect processor-based decks to become more common, so we decided to use the this new deck as an opportunity to work on something more reusable. The new deck does not strictly require all the infrastructure we are building. Rather, it provides a real product in which we can develop and test a communication model that may also be useful elsewhere.

Building the stack in layers

One lesson from Syslink and CPX is that we should avoid putting every communication concern into one protocol. The current new design is split into layers:

physical transport

transport binding

peer-to-peer Link

services such as Control, CPX or Console

The physical transport may be UART, SPI, I2C or another suitable connection.

A transport binding handles the details required to move frames over that transport, such as framing, integrity checks and resynchronization.

Above this is the Link between two embedded peers. It is responsible for establishing a communication session, negotiating compatible parameters, discovering available services and managing bounded buffers.

The Link does not need to understand Wi-Fi configuration, console text or routed CPX packets. These belong to services above it. This separation is important. It allows a simple product to expose only the services it needs, while a more complex system can add routing, structured control, console traffic or other services without changing the basic Link.

The initial implementation uses UART for our new deck, but the communication model itself is not intended to be UART-specific.

A first useful example: Console

Since starting this work, we have implemented a first Console service on top of the new Link.

This also led to a more generic console interface in the Crazyflie firmware. Instead of assuming that all console output comes from the main STM32, the Crazyflie can now expose several console sources. With the current prototype, a client can receive the Crazyflie console, the new deck console, or both. The available consoles can be queried, and by default the normal Crazyflie console continues to behave as before. This is a fairly small feature, but it is a good example of what the architecture is intended to make possible.

Today, the nRF51 radio microcontroller is largely opaque from a debugging point of view. This was quite intentional in the original Crazyflie architecture: the STM32 was the processor we expected users to develop on, while the nRF51 mainly provided supporting functions such as radio and power management. That boundary has become less clear over time. The radio firmware has grown more capable, and both we and some users increasingly work on it directly. If the nRF51 eventually uses the same Link model, it could expose the same Console service. The existing client-side console infrastructure could then show its output without requiring another custom protocol and another special debugging path.

This is the kind of reuse we are looking for.

Control and reusable capabilities

Another service we are exploring is Control.

Control provides a structured way to get values, set values and call commands. It is somewhat similar to an RPC interface, but it also exposes organized sets of readable and writable state. Within Control, related functionality can be described using versioned contracts.

Wi-Fi provisioning is a useful example: adding Wi-Fi support to the AI deck was not only a matter of implementing a command in the deck firmware. The Crazyflie needed to forward the operation, the client library needed an API, and a user-facing tool was required to configure the connection. A future Wi-Fi-enabled deck would otherwise need another product-specific implementation of much of the same path.

With a common Wi-Fi contract, a deck could advertise through its Control service that it supports Wi-Fi provisioning. Generic software could discover this capability and use an existing interface for it, without needing to know which deck or processor implements it. More importantly this means that we can tie Wifi credential to a Crazyflie and not to the deck: for our current work we have decided to store the wifi credential in the Crazyflie. This means that, once setup, the Crazyflie will connect the same wifi network independent of which wifi-capable deck is attached to it. It makes the system much easier to work with. We may update the AI deck to follow the same model in the future.

The same idea can apply to much simpler features. Several decks contain visible RGB LEDs. Instead of every deck exposing a different way of setting a color, they could implement one common LED-control contract and use the same client API.

This is the higher-level counterpart to reusing the Link. The Link avoids rebuilding processor-to-processor communication, while services and contracts make it possible to reuse functionality above it.

Where this could lead

The protocol developed for our new deck is not yet a complete Syslink2 specification for the whole Crazyflie ecosystem. The current work mainly demonstrates the point-to-point Link architecture: sessions, service discovery, versioning and flow control based on actual available buffers. Console already provides a concrete service on top of it, while Control and its contracts are still more exploratory. There are also larger questions we are deliberately leaving outside the first Link. Routing remains a separate concern, and CPX may continue to provide it where it is needed.

More generally, discoverable services could make parts of the Crazyflie platform easier to extend. Today, replacing or experimenting with a subsystem often requires changes across several parts of the stack simply because both ends need to know exactly what hardware is present. If processors can instead advertise standard services and capabilities, some of those dependencies could become looser. For example, a future radio processor could expose a radio service instead of being identified only by its position in the hardware architecture. In principle, another processor or even a deck could expose a compatible service while the rest of the system continues to use the same interface.

This is still very much a direction rather than a finished architecture, but it is one of the possibilities that makes this work interesting to us. The new deck gave us a practical place to start. So far, the layered model seems to fit both our immediate use case and some of the communication problems we expect to encounter more often in future Crazyflie platforms.

Inheriting an experiment is often harder than building one. A PhD student graduates. A postdoc moves on. A sensor gets discontinued, and six months later, reproducing the earlier work means reconstructing a software environment, rebuilding a hardware setup, and re-deriving assumptions that were never written down anywhere. The paper is still there, but the experiment usually isn’t.

Reproducibility is normally discussed in terms of published results: can someone else get the same numbers from the same method, but in practice, most labs run into a different problem first, and it’s a more mundane one. Continuity. A new student needs to take over a project without starting from scratch, or a collaborating lab needs to replicate a setup without rebuilding the surrounding infrastructure. Then the experiment itself needs room to evolve without dragging everything underneath it into a rewrite.

Preserving context, not just code

Code and datasets are easier to share now than they’ve ever been, and that’s real progress. But a repository is not the same thing as an experiment. What a new student actually inherits is a pile of things that rarely make it into a GitHub repo: hardware configurations, positioning system calibration, flight scripts tuned by trial and error, a specific combination of dependency versions that happens to work, and a layer of tacit lab knowledge. That last part is usually the stuff someone would tell you in five minutes at the whiteboard. It never gets written down.

The problem compounds as the work moves forward. A sensor gets replaced. ROS moves to a new version. New questions branch off from the original project, and each one drags a little more infrastructure along with it.

A stable platform doesn’t solve reproducibility. Poorly documented assumptions are still poorly documented assumptions. But it can remove one source of uncertainty: the platform underneath the experiment hasn’t become something completely different. That matters when the next researcher needs to understand what was built before they can build on it.

Compatibility over time has research value

Infrastructure that survives across multiple projects tends to become more useful with time. A platform a lab can keep using across student cohorts and research directions accumulates knowledge around it: working configurations, scripts, extensions, troubleshooting experience and, eventually, people who know how the pieces fit together.

That’s one of the less obvious benefits of the Crazyflie® ecosystem. The platform has changed considerably over the years, as it should. Hardware has evolved, APIs have developed and tools have come and gone. But much of the underlying architecture and the way the pieces relate to each other remains recognizable.

That technical lineage matters. Someone returning to Crazyflie research from five or even ten years ago won’t find exactly the same system, but neither will they find an unrelated one. The communication model, firmware architecture, development tools and approach to hardware extensions have evolved incrementally rather than being repeatedly replaced.

It means older work can remain useful as a starting point rather than simply becoming a historical reference.

Reproducibility between people, not just between runs

Reproducing an experimental result and continuing someone else’s research aren’t quite the same problem. But they share much of the same infrastructure.

Robotics has irreducible uncertainty in it, and no platform removes that. What a well-designed, long-lived platform can do is lower the cost of understanding, reconstructing and extending previous work.

It’s a less visible kind of value than a benchmark result. But in labs where a project outlives the people who started it, that continuity is often what makes cumulative research possible at all

Did you know?

  • Today’s firmware still supports Crazyflie 2.0 (apart from certin recent features)
  • You can take over someone else’s sysid work. If you would build your own platform you would need to redo that. (When the Crazyflie 2.1 Brushless came out immediately people rushed to get out their sys id papers. There’s multiple out there now already.)
  • CRTP (the radio protocol) has been stable for over a decade.
  • Param/log TOC lets the Crazyflie tell a client at runtime what parameters/logs are available. This (among other things) allows years-old scripts to maintain compatibility with newer firmwares and even across different platforms in the ecosystem.
  • The firmware reports its own firmware revision so user logs can include this.
  • The deck port (the pins) are unchanged. The deck working on your Crazyflie 2.0 works on your Crazyflie 2.1 Brushless.
  • Out-of-tree extension points; allows writing apps controllers and estimators for the community to separate their work from our firmware version.

A few years ago, we started looking at Rust as a possible foundation for parts of the Crazyflie software ecosystem. At first, this was mostly exploratory work: experimenting with Rust on embedded systems, looking at WebAssembly, and investigating whether one communication implementation could be shared between several platforms and programming languages.

In Rust, Wasm and the Crazyflie, we explored what a Rust implementation of the Crazyflie communication stack could look like. Later, in Rust at Bitcraze, we described a somewhat more concrete direction: implementing the low-level Crazyflie communication and subsystem drivers in Rust and making them available to Python, C++, JavaScript and other environments.

At the time, this was still mostly a plan.

Since then, the Rust Crazyflie library has moved from a side project to an official Bitcraze project. It is used in production, has supported a number of tools and demonstrations, and is now becoming the foundation for the next version of our Python library.

With the first release of cflib2, this seems like a good time to look at where we are and how it relates to the direction we described a few years ago.

Why a shared Rust library?

The Crazyflie Python library, cflib, has been around for a long time and is used by a large part of the community. It is the reference library and so it supports most Crazyflie functionality and is the foundation of tools such as the Crazyflie client.

Over the years, we have also felt some of the limitations of its architecture. In particular, it has proved difficult to reach the level of communication performance we want for larger swarms. Improving this within the existing pure-Python implementation would require fairly deep changes which would require quite deep user-facing change (like going async) without a certainty of results.

A separate problem is supporting users working in languages other than Python. One community solution has been to maintain a semi-independent C++ implementation of the Crazyflie communication stack, the one used by Crazyswarm2. While this provides access from C++, it also means maintaining another implementation. New functionality usually lands in the Python library first and is only ported to C++ later, often when its absence becomes a pain point. Over time, this makes the ecosystem harder to keep consistent and maintain.

The Rust library addresses these two problems from different directions. It gives us a new foundation on which we can build a more efficient communication architecture, including better support for swarms. At the same time, it can act as a shared reference implementation for several languages. Native Rust applications can use it directly, while bindings can expose the same functionality to Python, C++ and other environments.

These two goals are mostly independent, but they reinforce each other. Instead of improving performance in one library and then reproducing the same work elsewhere, we can implement the communication and subsystem logic once and make it available to users regardless of the language they choose.

This does not mean that all Crazyflie users are expected to start writing Rust. Quite the opposite: one of the main goals is to make the advantages of the Rust implementation available while still allowing users to work in the language and environment that suit their application.

From an experiment to a useful library

The Rust Crazyflie library has gradually moved from being an experiment to something we can use in real applications.

It has been used in production test systems, development tools, demos and swarm experiments. In these applications, we have started to see some of the improvements we originally hoped for.

The library uses an asynchronous architecture, which makes it easier to manage multiple Crazyflies and several ongoing communication flows at the same time. It also gives us more freedom to improve how information is transferred between a Crazyflie and a computer.

For example, parameters no longer need to be downloaded during the initial connection; instead, they can be loaded lazily when they are first requested. This is the kind of functionality for which Rust’s type system and language features make it possible to build a clean architecture with a relatively straightforward implementation.

Individually, this kind of changes may sound fairly small, but together they can make connections faster and reduce the amount of radio communication required. This becomes especially visible when working with larger groups of Crazyflies.

We have already used the Rust library in demonstrations involving many Crazyflies connected through one Crazyradio 2.0. It is still an evolving implementation, but these experiments have shown that the new architecture can make a practical difference.

Bringing the Rust library to Python

Python remains one of the most important ways of working with the Crazyflie. For this reason, one of the main goals of the Rust library has always been to make it available through a Python API.

This is what we are now doing with cflib2.

cflib2 is a new, async-first Python library built on top of the Rust Crazyflie library. Its first release is now available on PyPI and the source code is available on GitHub.

Our intention is for cflib2 to become the official Python library for the Crazyflie and, in time, replace the current pure-Python cflib. We are not there yet, though. This is the first public release, made at a point where the library has started to see more use both internally at Bitcraze and by a number of early users.

cflib2 is not designed as a drop-in replacement for cflib. Its API has been designed again around an asynchronous model, and we are using the opportunity to reconsider some earlier design decisions.

Most of the communication and Crazyflie subsystem handling in cflib2 is implemented by the Rust library. The Python layer exposes this functionality in a form that should feel natural to Python users. This gives Python applications access to the same connection handling, caching and asynchronous communication used by native Rust applications.

We and a number of early users have already seen significant improvements when moving applications to cflib2. One visible example was our recent demonstration in which 49 Crazyflies were controlled through one Crazyradio 2.0. This was made possible by a combination of the new radio functionality and the architecture used by the Rust library and cflib2.

For now, cflib2 should still be considered a preview. A large part of the functionality is already available, and it is being used in real applications, but the API is not yet stable and breaking changes should be expected. The existing cflib therefore remains the official and established Python library today.

This first release is an important step towards making cflib2 the official library. Releasing it now allows more users to try it in real applications and gives us feedback while it is still possible to make larger changes to the API and architecture.

More than Python

Python is the first binding we are working seriously on, but it is not the only environment we have in mind.

The original idea was to use the Rust library as a common foundation for several languages and platforms. C++, WebAssembly and mobile applications are still possible directions.

In particular, we hope that the same Rust communication backend can eventually be used by future mobile Crazyflie clients. Sharing the implementation between platforms could make these clients easier to develop and maintain.

There is still work to do before all of this becomes available, and the exact shape of the different bindings is not decided. For now, cflib2 is the most concrete example of how the architecture can be used outside Rust itself.

The next step in the journey

Looking back at what we wrote in 2021 and 2023, the overall direction has remained surprisingly consistent.

We wanted to implement the lower levels of the Crazyflie communication stack once, use Rust where its properties were useful, and expose the result to the languages and platforms used by the community.

The Rust Crazyflie library and cflib2 do not complete that journey, but they are a significant next step.

For the first time, Python users can install a library based on the shared Rust implementation and start using it in their own applications. At the same time, we can continue improving the communication stack without having to reproduce every improvement separately in each language.

We are still learning what works well, and both the Rust library and cflib2 will continue to change. Feedback, bug reports and experiments using the new library are very welcome.

You can find the projects here:

It is nice to see that an idea we started exploring a few years ago is now becoming a useful part of the Crazyflie ecosystem.

At Bitcraze we really have a front-row seat in robotics research. Every week, various papers appear: Conference papers, PhD theses, journal articles, and preprints that resonate across the community. Sometimes they arrive from universities we’ve worked with for years, and sometimes from labs we’ve never heard of before.

We are constantly humbled by the possibilities and the lengths that the community takes the platform. Although applications seem endless, we’ve grouped recent papers from the community into broad categories to give a useful snapshot of how different labs and research groups are answering questions from different angles.

Swarms are all about robustness (and size)

A decade ago, entire keynote talks revolved around ever-larger choreographed formations. Don’t get me wrong, it’s always impressive to see large swarms, but recent papers seem to increasingly raise questions around reliability.

How do individual robots coordinate without overwhelming the communication network? What happens when information arrives late? What if one vehicle fails? How do you distribute decisions without relying on a central controller?

DMPC-Swarm: Distributed Model Predictive Control for Nano-UAV Swarms is a good example. Rather than treating coordination as a centralized optimisation problem, the authors investigate distributed model predictive control, with the optimization shared across the swarm rather than handled by a single central unit (see https://link.springer.com/article/10.1007/s10514-025-10211-w).

It’s also interesting to see how much current work still builds on the foundations laid by the original Crazyswarm framework (https://ieeexplore.ieee.org/document/7989376). Nearly a decade later, it continues to serve as a reference point for new ideas, while more recent work pushes towards larger, more resilient, and more scalable swarm systems (https://ieeexplore.ieee.org/document/10611499).

Flying robots are literally touching the world

The most common drone use case is still the observer. To inspect bridges, map forests, measure crops, and capture data, but aerial manipulation turns that idea upside down.

Instead of asking what a flying robot can see, researchers ask what it can do. Some groups investigate cooperative payload transport (https://ieeexplore.ieee.org/document/8461014). Others focus on cable manipulation and contact-rich interaction (https://ieeexplore.ieee.org/document/10382688). More recent work explores increasingly sophisticated manipulation strategies while maintaining stable flight (https://ieeexplore.ieee.org/document/10802794).

Aerial manipulation forces multiple disciplines together. Control theory, estimation, mechanical design, and physical interaction all become tightly coupled. As these systems mature, flying robots may increasingly move beyond sensing and inspection roles into applications that require direct interaction with the physical world.

The challenge of making AI practical

Look anywhere and everything’s “AI”, but thankfully the research papers tell a nuanced story. Researchers are asking practical questions, if learning can improve flight performance without sacrificing stability, if sophisticated controllers can run on tiny embedded processors, and if simulation can reduce the amount of expensive real-world data needed before deployment?

DATT: Deep Adaptive Trajectory Tracking explores how learned components can complement classical control methods under uncertain conditions (https://arxiv.org/abs/2310.09053).

Learning to Fly in Seconds takes another route, demonstrating how efficient training in simulation can dramatically shorten the path to successful real-world flight (https://ieeexplore.ieee.org/document/10517383).

Palossi et Al. investigates yet another line of research on how machine learning can be integrated directly into flight control, allowing nano-quadcopters to improve their performance while remaining reliable and computationally efficient (https://ieeexplore.ieee.org/document/8715489)..

The frontier isn’t simply making robots smarter. It’s making sophisticated autonomy accessible on hardware small enough to fit in the palm of your hand.

Can robots learn to work with people, not just around them?

Technical performance alone doesn’t determine whether a robotic system is successful. People need to understand what the robot is doing, communicate with it naturally, and develop enough confidence to work alongside it.

That has led researchers to investigate everything from gesture-based interfaces, or “SwarmTouch” (https://ieeexplore.ieee.org/document/8758191) to shared autonomy in Hand-worn Haptic Interface for Drone Teleoperation (https://ieeexplore.ieee.org/document/9196664) and broader questions around interaction and collaboration between humans and aerial robots bu La Delfa et Al. (https://ieeexplore.ieee.org/document/10973956).

In some cases, the hardest challenge is not controlling the robot itself, but designing the relationship between humans and machines. After all, even the most capable autonomous system ultimately exists to help someone accomplish something.

Sometimes the cleverest solution is the simplest one

Before a robot can make intelligent decisions, it must know where it is. For small aerial robots, this question is particularly challenging, since limited payload capacity restricts sensor choices, while limited onboard compute constrains what algorithms can realistically run. This has made nano-quadcopters an attractive platform for investigating efficient perception and navigation techniques.

In Visual Route-following for Tiny Autonomous Robots, published in Science Robotics, researchers demonstrated an insect-inspired navigation strategy using an omnidirectional camera mounted on a Crazyflie Brushless (https://www.science.org/doi/10.1126/scirobotics.adk0310).

Rather than constructing detailed maps of the environment, the robot simply learns visual routes and follows them. It’s an elegant reminder that engineering progress doesn’t always come from adding complexity, but from asking what can be removed.

That same philosophy appears elsewhere in recent perception and navigation research, where teams continue to develop increasingly efficient onboard perception and localization methods suited to the severe constraints of nano-quadcopters. NanoSLAM: Enabling Fully Onboard SLAM for Tiny Robots (https://ieeexplore.ieee.org/document/10343110, and Robust and Efficient Depth-Based Obstacle Avoidance for Autonomous Miniaturized UAVs are perfect examples of this (https://ieeexplore.ieee.org/document/10272390).

A community exploring difficult problems

What makes the broad body of Crazyflie research interesting is the sheer variety of approaches researchers bring to the same fundamental challenges. Across universities, laboratories, and disciplines, researchers continue to investigate cooperation, physical interaction, learning, perception, and human collaboration from different angles.

Taken together, these projects provide a glimpse of where robotics research is heading. There’s clearly more to find out, and we’re glad to keep being part of how people go looking.

Visit our Applications pages for more examples of how researchers, educators, and innovators are using the Crazyflie.

Flying formations with a swarm is always fun to develop and watch, but it is also a great way to stress-test the software behind it. As part of the development of cflib2, we put it through one of its toughest tests yet: flying 49 Crazyflies in coordinated formations.

The entire swarm was controlled using a single Crazyradio 2.0, highlighting the combined improvements in cflib2 and Crazyradio firmware over the past few months.

The setup

The hardware configuration was relatively straightforward. We used 49 Crazyflie 2.1 Brushless drones, each one equipped with a bottom-mounted Color LED deck for vivid lighting effects. For positioning, we used the Lighthouse positioning system, covering the entire flight area, which was roughly 5x5x2m. Its accuracy allowed the Crazyflies to fly grid formations with just 0.4m spacing between them.

One of the big practical challenges when managing a large swarm is swapping the depleted batteries for charged ones. Thanks to the Crazyflie 2.1 Brushless PCB design, each drone can now charge while sitting on its charging dock, making it much easier to prepare the swarm for the next flight.

Flying the formations

The swarm performed a sequence of synchronized formations under the control of a central PC. Rather than streaming the full trajectories to every drone, the formations are built using the Crazyflie’s High-Level Commander. Each Crazyflie receives simple motion commands such as go to or spiral, and executes the corresponding trajectory onboard. At the same time, it receives commands for changing the color of the Color LED deck.

Using cflib2, these commands can be sent to all 49 Crazyflies through a singe Crazyradio 2.0, which was not possible with cflib.

Looking ahead

This demonstration is an exciting milestone for cflib2 and showcases what the new library makes possible. While controlling 49 Crazyflies is an impressive demonstration, cflib2 is designed to benefit projects of every size. Whether you are flying a single Crazyflie or coordinating a large swarm, the goal is to provide a faster, more scalable, and more robust communication library.

Most of the functionalities from cflib have already been migrated to cflib2, and development is continuing. For many applications, cflib2 is already ready to use, so if you would like to try it out, you can find the repository here.

The Crazyflie 2.1 Brushless uses the DSHOT protocol to command its ESCs — a clean, digital, CRC-protected signal that replaced the old analog PWM signals. But until now, that communication was strictly one-way: the firmware sends a throttle command and hopes for the best. There’s no direct feedback from the motors about what they’re actually doing, even if the brushless motors are naturally able to do that by sensing the back-EMF on their windings.

As part of our recent work at IDSIA on nonlinear system identification for the Crazyflie 2.1 Brushless, we developed a new bidirectional DSHOT driver to change this. Each ESC now reports its electrical RPM back over the same signal line used for commands, giving us per-motor RPM telemetry at every control cycle. The new driver was just merged in the firmware and it’s now available for everyone to use (PR #1556).

Plot showing RPM measurements during hover and landing

What is Bidirectional DSHOT?

Standard DSHOT encodes a throttle command as a sequence of bit periods on a digital line: each bit is either a short or a long pulse, with the full frame containing an 11-bit throttle value, a telemetry request flag, and a 4-bit CRC. It’s fast, noise-resistant, and needs no calibration. In other words, a major step up from the analog PWM for the old Crazyflie 2.1 Brushed.

Bidirectional DSHOT extends this with a simple trick. After the flight controller finishes transmitting a command frame, it releases the line (sets the GPIO to input). The ESC responds with a telemetry packet containing its current electrical RPM (eRPM), encoded using a GCR (Group Code Recording) scheme. The whole exchange, i.e., command out, telemetry back, takes roughly 150 µs. To enable bidirectional mode, the STM32 inverts the polarity of the DSHOT line (idle-high instead of idle-low), which the ESC recognises as a request to reply with telemetry.

The eRPM value from the ESC reports electrical rotations. To get the mechanical RPM that actually matters, you divide by half the number of motor poles: RPM = eRPM / (poles / 2). For the motors on the Crazyflie Brushless, this is 12 poles per motor, but the driver can be customized with a fixed constant set at build time.

This protocol is well-established in the FPV and Betaflight ecosystem, where it’s used for RPM filtering in flight controllers. The ESC firmware on the Crazyflie Brushless (BlueJay) already supports it. The missing piece and technical challenge was the firmware on the STM32 side.

Logic analyser captures of the standard and bidirectional DSHOT exchanges. In bidirectional DSHOT the command frame is followed by the GCR-encoded telemetry response.

Implementation

Bidirectional DSHOT turns a transmit-only interface into a half-duplex one: the same physical line must alternate between output (command) and input (telemetry). On the Crazyflie’s STM32, implementing this must account for a few hardware constraints.

DMA Input Capture for receiving telemetry

Rather than polling or bit-banging the telemetry response, the firmware uses DMA Input Capture: it configures the timer to record a timestamp at every edge transition on the DSHOT line, with DMA transferring each timestamp to a buffer without CPU intervention. After the command frame is sent, the code opens a 100 µs receive window (accounting for 30 µs of wait time, around 50 µs for the telemetry packet, plus some headroom). Once the window closes, the buffer contains a sequence of edge timestamps that can be decoded into the GCR telemetry frame at leisure.

This is the same approach used in Betaflight’s bidirectional DSHOT implementation, adapted to the Crazyflie’s timer and DMA configuration.

Hardware conflicts

All four motor DSHOT outputs share the TIM2 timer on the STM32. This was not an issue with traditional DSHOT: the timer is left free-running, and each motor can be controlled independently. With bidirectional DSHOT, the transmit and receive phases require different timer periods (the command and telemetry frames have different bit rates), so the transmit and receive phases can’t overlap between motors.

The solution is a two-phase scheme within each control cycle:

  1. Phase 1: Transmit command frames to motors M1, M3, and M4. After transmission, switch the lines to input and capture their telemetry replies.
  2. Phase 2: Once the receive window closes (detected by a TIM2 interrupt), transmit the command to M2 and capture its reply.

This sequencing means M2’s command is delayed by roughly 100 µs relative to the other motors — the cost of sharing a single timer. In practice, at the Crazyflie’s control rate, this additional latency is negligible.

What you get

With bidirectional DSHOT enabled, the firmware exposes four new log variables:

  • motor.m1_rpm
  • motor.m2_rpm
  • motor.m3_rpm
  • motor.m4_rpm

These can be streamed and recorded through cfclient like any other log variable, at the full control loop rate.

Validation

Desired throttle vs. measured RPMs over a 30s flight.

To verify that the telemetry readings are correct, we compared the reported RPM values against the thrust-stand characterisation performed for the battery compensation work. At a given commanded thrust and battery voltage, the expected RPM can be computed from the motor voltage–thrust polynomial and the motor’s known Kv constant. The onboard RPM readings match this prediction well across the operating range.

During flight, the RPM traces clearly show the motor dynamics that the open-loop command model doesn’t capture: spin-up and spin-down transients, asymmetries between motors, and the RPM dips that correspond to aggressive attitude changes. This is exactly the kind of information that was previously invisible to the firmware.

Applications

Per-motor RPM telemetry opens the door to several applications.

System identification with measured actuator signals

RPM telemetry changes what’s possible for system identification. In previous nano-drone datasets, the “motor input” is the commanded PWM or throttle value — but the actual motor response differs from the command due to ESC dynamics, battery sag, and nonlinear torque curves. Any model trained on commanded inputs cannot disentangle these actuation nonlinearities from the airframe dynamics it’s actually trying to capture.

This is exactly the problem we addressed in Busetto et al. (2025): we released a benchmark for nonlinear system identification based on the Crazyflie 2.1 Brushless. The dataset contains 75k real-world samples across four aggressive flight trajectories, with synchronised 4-dimensional motor RPM inputs and 13-dimensional output measurements (IMU + motion capture). Crucially, the motor inputs are measured RPMs from bidirectional DSHOT — not commanded values. This cleanly separates the actuation subsystem from the airframe dynamics, giving identification algorithms a much better signal to work with.

The benchmark includes multi-horizon prediction metrics for evaluating both one-step and multi-step error propagation, along with baseline models ranging from physics-based to neural network approaches. All data, scripts, and reference implementations are open-source at github.com/idsia-robotics/nanodrone-sysid-benchmark. The firmware feature described in this post directly enabled this data collection.

Closed-loop RPM control

Beyond identification, using RPM telemetry as closed-loop feedback enables new opportunities in control. The battery compensation scheme introduced in PR #1526 solves the voltage-sag problem by adjusting the PWM command based on measured battery voltage and a motor voltage–thrust polynomial. It works well, but it’s fundamentally still open-loop with respect to the motor itself: the firmware corrects for the expected effect of voltage on thrust, without ever checking whether the motor actually reached the intended speed.

With RPM feedback, a different approach becomes possible: close the loop at the motor level. Instead of commanding a PWM duty cycle and compensating for voltage, command a target RPM and let a per-motor controller (or the ESC’s own closed-loop mode) handle the rest. This makes the thrust response inherently invariant to battery voltage, temperature drift, and propeller wear — anything that shifts the relationship between PWM and actual speed.

Broader impact

Having RPM as a standard logged variable on an open-source, commercially available nano-drone lowers the barrier for the entire research community. Anyone with a Crazyflie Brushless can now collect flight dynamics datasets with true actuator measurements, validate sim-to-real transfer (e.g., with Crazyflow), or prototype RPM-aware controllers — without any hardware modifications.

Try It

Bidirectional DSHOT is available today and enabled by default in the main branch of the Crazyflie firmware. To use it:

  • You need a Crazyflie 2.1 Brushless with BlueJay ESCs (the stock configuration).
  • Log the motor.m{1,2,3,4}_rpm variables through cfclient.

It is also used by the supervisor to check that all motors has spun up during arming or if a motor is blocked during flight. We’re looking forward to community feedback. Let us know how it works for you.

ICRA 2026 has wrapped up, and we’re back from a fantastic week in Vienna! Booth 91 was busy from start to finish, and we wanted to put together a short highlight video to share some of what happened — for everyone who stopped by, and for everyone who couldn’t make it this year.

The Swarm Demo

At the center of our booth was our live autonomous swarm demo — multiple Crazyflies flying autonomously in a controlled indoor environment, with everything tracked, repeatable, and stable across runs. We also could play around with our Lighthouse wand – which was also a great solution for troubleshooting the few misbehaving drones we had during those 3 days.

SwarmGPT, Live and Interactive

One of the highlights of the week was demonstrating SwarmGPT together with the Learning Systems and Robotics Lab (LSY) at the Technical University of Munich. SwarmGPT explores a simple but powerful idea: instead of hand-coding trajectories, you describe the intent — pick a piece of music, prompt a style or expression — and the system handles the planning and safety while the swarm performs it.

This time around, we brought a more interactive version of the demo than our end-of-year collaboration a few months back, and visitors got to try it out for themselves at the booth. Watching people prompt the swarm and then watch their idea come to life in the air was a great reminder of how far natural-language interfaces have come, and how much room there still is to explore in this space.

Research We Saw on the Crazyflie

Beyond our own demos, one of our favorite parts of ICRA is talking with our users and seeing what the community has built. This year was no exception — we spotted Crazyflies appearing in research spanning multi-agent coordination, modular micro-UAVs designed for autonomous mid-air docking, and decentralized swarm control approaches where each drone makes its own decisions based on local information rather than a central planner. Some examples include:

It’s always a bit surreal to see the same small quadcopter we ship from our office end up at the center of such different research questions — from choreography and language-driven control, to docking and modular hardware, to fully decentralized swarms. If you presented work involving the Crazyflie this year, thank you for stopping by and sharing it with us — and if you left a poster behind, it’s already found a home on our office wall.

Thanks for Stopping By

ICRA continues to be one of our favorite events of the year, not just for the demos, but for the conversations. Someone describes a challenge they’re running into in their lab, and a few months later, that conversation has often turned into a feature, a library improvement, or a new piece of hardware. If you stopped by booth 91, told us about your research, or just said hello, thank you. We’re already looking forward to the next one!

If you’d like to dig deeper into any of what’s shown in the video, or want to get started with the Crazyflie yourself, head over to bitcraze.io or reach out at contact@bitcraze.io.

When people think of Urban Air Mobility (UAM), they might think of large, futuristic eVTOL aircraft carrying people between skyscrapers, not a 30-g nano-quadrotor fitting in the palm of their hand. However, testing full-scale aircraft in chaotic urban wind fields is dangerous, expensive, and practically impossible. During my PhD thesis, I found that the brushless Crazyflie makes an ideal subscale proxy to rigorously explore flight in these complex microclimates.

Urban microclimates are notoriously unpredictable and dangerous for aircraft. High-intensity wind gusts and spatial gradients are formed by multi-scale interactions between the atmospheric boundary layer and the buildings, bridges, and other urban infrastructure that define our skylines. There are entire fields dedicated to studying urban wind patterns using supercomputers and precise wind tunnel testing, but how can drones leverage all this data and modeling in real time?

Put another way, if we want package delivery drones and electric air taxis safely operating in urban airspaces, we need innovative ways for aircraft to predict hazardous wind conditions and adapt to them on the fly. Our initial research showed that drones could predict surrounding time-averaged urban winds with reasonable accuracy using LiDAR, and our follow up work suggests that incorporating these local wind predictions into navigation could reduce potential crashes and even improve energy efficiency.

This article breaks down the hardware details for experiments during the final six months of my thesis, where the brushless Crazyflie played a pivotal role in validating concepts from my thesis in the real world.

A Subscale UAM Testbed

A lot of research related to drone wind estimation is limited by the lack of ground truth data available to compare against. With my research, I really wanted to use a highly instrumented laboratory environment, where we could tightly control the experimental conditions and get accurate, repeatable trials without being at the mercy of the weather.

To accomplish that, we used the WindShaper facility at NASA Ames Research Center in Mountain View, CA, a state-of-the-art open-air wind tunnel (the “WindShaper”) positioned inside a basketball court-sized motion capture space. We mimicked “urban winds” using wooden boxes meant to represent buildings.

The problem was that the WindShaper was only so big. We needed a similarly scaled drone that could represent our eVTOL. That’s where the Crazyflie came in!

The (Modded) Crazyflie

For multiple reasons, all relating to dynamic similitude, the Crazyflie ended up being the perfect size for our experiments. The timing was perfect too because the brushless Crazyflie had just been released, offering a similar footprint as the original Crazyflie, but with the additional thrust and battery life necessary to carry our payloads and handle gusty wind conditions.

We modified the Crazyflie with two additional sensors. The first was an INA260 power sensor that we used for… well, measuring the power draw from the battery! The second sensor was a bit more interesting. In partnership with the Microrobotics Lab at Carnegie Mellon University, we put a whisker-based air flow sensor on the Crazyflie to measure the wind. You can read more about that neat sensor here.

The all up weight, including the battery, power sensor, whisker, and motion capture markers was 57g, almost double the standard weight. With a 350mAh battery we could get about 3 to 5 minutes of flight, depending on the wind speed, which was just enough time for us to run our experiments.

Actuator, Aerodynamic, and Power System Identification

I did two experiments to characterize the brushless Crazyflie. The first experiment involved strapping the Crazyflie onto a thrust stand with an optical RPM sensor to model the relationships between thrust, speed, commanded PWM signal, and battery supply voltage. The last three signals were relevant because at the time the firmware didn’t support direct motor speed measurements!

The second experiment involved subjecting the Crazyflie to wind speeds varying from 0 m/s up to 8 m/s. For these experiments, I used the onboard accelerometer to measure the net drag force, which required being able to subtract the static thrust from the propeller, hence the previous experiment; the INA260 sensor to measure power draw from the battery; and a combination of motion capture and a pitot-static probe on a tripod to measure the drone’s ground speed and wind speed, respectively.

The plot above shows a surprisingly linear relationship between the acceleration and the sum of airspeed and motor speeds for the x body axis. This is known as rotor drag, and it’s generally considered the dominant form of drag for quadrotors at lower airspeeds. The slope of this line is the rotor drag coefficient. What this model lets us do is infer the airspeed along the x axis from the accelerometer and motor speeds alone, no dedicated wind sensor necessary!

Lastly, I used the same experiment to model the power curve for the Crazyflie. What we were able to show was that there is a measurable dip in the average power consumed by the brushless Crazyflie at steady level forward flight. This effect is well-documented in full-scale helicopters, where the translational lift effect makes the rotors more efficient in forward flight, but it is rarely captured or documented on a scale as micro as the Crazyflie. To the best of my knowledge, this is one of the first empirical confirmations of any sort of power dip on a nano-UAV, which may (or may not!) have relevance as we think more about taking these tiny drones out into the wild.

My measurements indicate an average power consumption of 10.56 Watts in hover, placing the hover efficiency at around 0.188 W/g. Keep in mind this is for the modded Crazyflie weighing in at 57g.

Putting It All Together

The ultimate goal with all these experiments was to test out my methods for wind prediction & estimation and wind/obstacle-aware motion planning in real time. We tested these algorithms through multiple trials across five “building” configurations, totaling over an hour of actual flight time. Below is a video showcasing the wind field prediction happening in real time.

In this trial, the Crazyflie is trying to track the XY location of the end of the wand while also avoiding the obstacles using (simulated) LiDAR scans. Meanwhile the WindShaper is throwing wind at about 4 m/s at the drone. This particular trial was focused more on the wind prediction side of things rather than motion planning, so the motions aren’t particularly exciting. Nevertheless, the network was capturing salient flow features like the high speed wind tunnel effect between the building clusters.

Closing Thoughts

Building this testbed proved that nano-quadrotors like the Crazyflie are far more than just educational toys or swarm demonstrators. When properly characterized, their tight physical scaling parameters make them uniquely qualified as safe, high-fidelity proxies for validating the next generation of Urban Air Mobility algorithms.

None of this would have been viable without the open-source architecture of the Bitcraze ecosystem. Being able to easily modify the firmware to interface with our custom INA260 power monitor, log high-frequency accelerometer measurements, and dynamically communicate with external motion capture and WindShaper using ROS allowed us to treat the Crazyflie as a true subscale UAM development kit.


If you want to dive deeper into anything you found interesting in this article, or better yet get access to the data I collected, you can find more details in my full dissertation (available on arXiv) or reach out to me on LinkedIn!

Some Fun-Friday projects begin with a clear goal and a straight path to the finish line. The best ones, however, take you somewhere completely unexpected.

This project originally set out to build a device for determining spatial coordinates within a Lighthouse-covered flight area. Instead, it evolved into the Lighthouse Wand, a hand-held “magic wand” letting you grab and move drones in 3D space just by pointing at them.

How it works

The Wand is a Crazyflie platform with a Lighthouse positioning deck. That’s enough for it to know its own position and orientation in the room. When the button is pressed, it starts broadcasting those 6 numbers over Peer to Peer radio.

Any Crazyflie/receiver in the room on the same radio channel, listens to those packets and runs a simple “grasping” algorithm: while the wand line (positive x-axis) passes close enough to the drone, it builds up a confidence score. Once the score crosses a threshold, the drone is considered grasped. From that point on, it just keeps a specific distance from the wand, while being on the wand line.

When the button is released, the grasped drone either hovers in place, or lands, depending on the release height.

The Color LED deck on the receiver drone, gives you visual feedback: yellow while the Crazyflie is building up its confidence score, green when it’s grasped, and red when it’s landing.

A big advantage of this system is that all interactions run entirely onboard the Crazyflies, allowing them to operate without relying on the cfclient or cflib during flight.

The hardware design

The wand is a Crazyflie Bolt 1.1 with a Lighthouse positioning deck and a Buzzer deck for audio feedback. To allow for user input, I created a simple “Button deck” based on the Prototyping deck utilizing the GPIO pins of the Crazyflie. It also includes an LED for visual feedback when the button is pressed.

The casing is fully 3D printed in PLA and was designed to give the device a more wand-like feel in the hand. Its shape also makes it easier to hold, aim, and use intuitively during interaction.

The firmware design

Both the Wand and the receiver are firmware apps created on top of the crazyflie-firmware. In the design that I followed, there is a clean separation between the two parties. The wand is a pure broadcaster: it only reads its own pose and transmits it. All grasping logic and flight control run independently on each receiver. Since each receiver is fully autonomous, the system scales to any number of drones with no extra load on the wand.

Where to find the Lighthouse Wand?

A version of the Lighthouse wand is now integrated in our decentralized swarm demo, where it can be used to interact with multiple drones, while the collision avoidance algorithms are still on. This system was first showcased at the European Robotics Forum 2026 in Stavanger, and we’ll also be bringing it to ICRA 2026. If you’re there, stop by booth 91and try flying a bunch of Crazyflies yourself using the wand.

You can find the complete Lighthouse Wand project in this repository. It contains the firmware, the hardware files, and detailed documentation to build and experiment with the wand yourself.

If you’ve ever gone looking for a more advanced, or use-case-specific Crazyflie example (something beyond the basic single-feature ones), you’ve probably ended up digging through the cflib and crazyflie-firmware example folders. That’s about to change.

We’ve created a new repository: crazyflie-demos. It’s a dedicated place where both us at Bitcraze and the broader community can host self-contained, well-described Crazyflie demos.

Why a new repository?

The examples in the core Bitcraze repositories were meant to be kept focused: demonstrating one feature, one API, or one subsystem at a time. But real demos tend to grow beyond that pretty quickly. Once you start combining positioning systems, swarming, custom firmware apps, external sensors, or other integrations, things stop fitting naturally into the firmware or library repos.

crazyflie-demos gives those larger, more practical examples a proper home, and finally provides a good answer to the question: “where should I put this cool thing I built?

Why not just a folder of examples?

We want to avoid the fate of some older example collections that gradually turned into an unmaintained pile of half-working demos and missing context.

The goal with crazyflie-demos is that every demo should be properly documented and actually runnable. That means clear descriptions, listed dependencies, and enough context to understand what’s going on without digging through source code for an hour.

Another important part is reproducibility: each demo is self-contained and uses pinned dependencies, so an example you clone two years from now should still work.

What’s in there already?

The repository is organized by demo type:

  • scripts/cflib: Host-side Python scripts using crazyflie-lib-python, covering the full Crazyflie API.
  • scripts/rust: Rust demos using crazyflie-lib-rs, showcasing its high-performance and native async support.
  • scripts/cflib2: Early demos for our new Python library, crazyflie-lib-python-v2, built on top of the Rust library. cflib2 doesn’t have a release yet, but we’re already writing demos for it to test the API and the performance.
  • firmware: Out-of-tree firmware apps that are flashed directly to the Crazyflie. Each demo carries its own crazyflie-firmware submodule so you’re always building against the right version.
  • hybrid: Demos that combine onboard firmware with a host-side script working together.

A place to share your work

A big motivation behind crazyflie-demos is making it easier to share work with the community.

If you’ve built something useful, or just a fun experiment using our products, this is the place for it. Not everything needs to live in its own repository or branch. A well-described demo here makes it easier for others to find, understand, and build on your work, and most importantly, to get inspired by it.

We’ll also be using this repository as the go-to reference whenever people ask for more use-case-specific examples, so good demos here will naturally help more people discover what’s possible with the Crazyflie ecosystem.