Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • danc/MicroCART
  • snawerdt/MicroCART_17-18
  • bbartels/MicroCART_17-18
  • jonahu/MicroCART
4 results
Show changes
Showing
with 2059 additions and 0 deletions
---
title: Out of tree build
page_id: oot
---
It is possible to have an out-of-tree build of parts of the crazyflie firmware. This enables developers to work on elements without worrrying about merging it with the full code base.
# App layer.
Technically the app layer is an example of an out of tree build. Follow the [app layer intructions](/docs/userguides/app_layer.md) for this.
# OOT extimators
In a seperate folder make a Makefile which contain the following content:
```
CFLAGS += -DOOT_ESTIMATOR
VPATH += src/
PROJ_OBJ += estimator_out_of_tree.o
CRAZYFLIE_BASE=[LOCATION OF THE CRAZYFLIE FIRMWARE]
include $(CRAZYFLIE_BASE)/Makefile
```
in [your_estimator_out_of_tree].c in the src folder you will just need to make sure that the following functions are filled:
* ```init = estimatorOutOfTreeInit```
* ```test = estimatorOutOfTreeTest```
* ```update = estimatorOutOfTree```
* ```name = "OOT```
# OOT Controllers
Not yet implemented. Please request this feature in the [crazyflie-firmware issue list](https://github.com/bitcraze/crazyflie-firmware/issues)
---
title: On-chip debugging
page_id: openocd_gdb_debugging
---
One of the key components of getting really serious about developing on
the crazyflie, is to dive into the C-based firmware. If you really want
to do some major changes to the intrinsic of the code, it is essential
to have proper tools to be able to debug the code (place breakpoints,
read real-time values ect\...). On this page, we will put a few examples
how to do this with IDE\'s and different environments.
> **_NOTE:_**
> This page requires our debug adapter and ST Link V2 Debugger! See
> this page: [Debug adapter](https://wiki.bitcraze.io/projects:crazyflie2:debugadapter:index)
## Debugging using VS Code
Thanks to the [Cortex-Debug](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug) extension, it is now easily possible to debug ARM executables straight inside of VS Code!
### Mac OS and Ubuntu
#### Prerequisites
First ensure that you have the ARM GCC toolchain and OpenOCD installed and in your path. To check, run:
which openocd
which arm-none-eabi-gcc
The path to your OpenOCD binary and ARM GCC binary should output. If not, try installing them again.
##### Ubuntu
These steps have been tested on Ubuntu 20.04. The link to gdb-multiarch is required because Ubuntu does not ship arm-none-eabi-gdb anymore, but the new gdb-multiarch that supports all architecture.
sudo apt-get install openocd
sudo apt-get install gcc-arm-none-eabi gdb-multiarch
sudo ln -s /usr/bin/gdb-multiarch /usr/local/bin/arm-none-eabi-gdb
If you do not have vscode yet, the easiest way to install it on Ubuntu is via snap using 'Ubuntu Software' of by typing:
sudo snap install --classic code
##### Mac OS
brew install open-ocd
brew tap PX4/homebrew-px
brew install arm-none-eabi-gcc
### The Cortex Debug Extension
Install the [extension](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug) either by clicking "Install" on the web page, or by searching "Cortex Debug" in the Extensions tab of VS Code.
Click on "Run", then "Add Configuration", then "Cortex Debug".
![VS Code add configuration](/docs/images/vscode_add_configuration.webp)
This should automatically create the needed "launch.json" file.
### Configuring Cortex Debug
Inside of the file, replace everything with the following:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceRoot}",
"executable": "./cf2.elf",
"request": "launch",
"type": "cortex-debug",
"device": "STM32F405",
"svdFile": "STM32F405.svd",
"servertype": "openocd",
"configFiles": ["interface/stlink-v2.cfg", "target/stm32f4x.cfg"],
"runToMain": true,
"preLaunchCommands": [
"set mem inaccessible-by-default off",
"enable breakpoint",
"monitor reset"
]
}
]
}
### Explaining the Cortex Debug configuration
- "svdFile" refers to the necessary file for peripheral registers to show up nicely in the debug pane, all named and structured; we'll add it in the next step
- "configFiles" refers to the files you need so that OpenOCD knows what device and interface you're using; it should already come with them
- "runToMain" tells the GDB debug server to jump to main by default
- "preLaunchCommands" specifies the commands for the GDB server to send before giving away control to you; the commands here mimic the options that the above tutorial for Eclipse specifies
### Installing the SVD file
Now for the SVD file: just download it from [here](https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/STMicro/STM32F405.svd) and into your root directory. Make sure it has the exact name of "STM32F405.svd"!
### Debugging!
After all this, go to the Debug tab of VS Code (on the left sidebar, the icon with the little bug next to the play button), and hit that play button next to "Run"!
If you followed everything, it should start running nicely and look a little something like this:
![VS Code Cortex Debug](/docs/images/vscode_cortex_debug.webp)
Notice the nice peripherals pane at the bottom, along with the variables pane at the top. Awesome, now you can code _and_ debug all within VS Code!
---
## Debugging using eclipse
### Ubuntu
> **_Versions:_**
>
> - Ubuntu 18.04.2 LTS (64 bit)
> - Eclipse 2019-03 (Eclipse IDE for C/C++ Developers)
#### Installing prerequisites
First install GDB and openOCD:
sudo apt-get update
sudo apt-get install gdb
sudo apt-get install openocd
Then install java:
sudo apt install default-jre
Then install eclipse itself: Go to their download page: [eclipse
20](https://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/2019-03/R/eclipse-cpp-2019-03-R-linux-gtk-x86_64.tar.gz)
and then go into you download folder and unzip the file.
tar -zxvf "your-downloaded-file".tar.gz
and start up eclipse:
"YOUR-UNZIPPED-FOLDER"/.eclipse
#### Installing required Eclipse Plugins
Install the C++ development tools and GNU MCU plugin by following the
instructions [here](https://gnu-mcu-eclipse.github.io/plugins/install/).
- C++ Development - Follow the instructions under the header \'CDT\'.
- GNU MCU plugin - Follow the instructions under the header \'Plug-ins
install/update -\> The Eclipse Marketplace way\'
#### Import Crazyflie Firmware
First import the
[crazyflie-firmware](https://github.com/bitcraze/crazyflie-firmware)
into eclipse:
- File > import...
- C/C++ > Existing Code as Makefile Project -> Next
- Give it a name
- Existing Code Location > Browse... > //Look for the firmware folder//
- //Toolchain for Indexer Settings// can be ignored.
- Finish
#### Setting up Eclipse Debugging environment
- Go to: Run \> Debug Configurations\...
- Double click \'GDB OpenOCD Debugging\'
Now input the following settings in the debug configurations:
##### Main
![stm openocd main](/docs/images/stm_openocd_main.png)
Insert the filepath to the cf2.elf file to _C/C++ Application_.
##### Debugger
![stm openocd debug](/docs/images/stm_openocd_debugger.png)
check the following settings: OpenOCD setup -\> Config options: \<code\>
-f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c init -c targets
\</code\> GDB Client Setup:
- Executable name: Filepath to gdb toolchain
- Commands: \<code\> set mem inaccessible-by-default off \</code\>
##### Startup
![stm openocd startup](/docs/images/stm_openocd_startup.png)
##### Hit Debug!
If you don\'t see any errors, eclipse should go to an dedicated
debugging environment automatically and it automatically halts the
crazyflie\'s firmware before it goes into the main function of
src/init/main.c. Press F8 or Run \> Resume to let it continue and place
a breakpoints anywhere in the code (double clicking just in front of the
line in the gray area or press Shift+Ctrl+B). Now you should be able to
read out the values of the defined variables at that very position.
---
Make sure that your cf2.elf is the
same as the one you uploaded to the crazyflie!
---
### Mac OS
Install gdb and openocd
brew install gdb
brew install open-ocd
Install java JDK [java
download](https://www.oracle.com/technetwork/java/javase/downloads/index.html)
Download eclipse [eclipse
download](https://www.eclipse.org/downloads/download.php?file=/oomph/epp/2019-06/R/eclipse-inst-mac64.dmg)
Choose destination folders - Install
Run eclipse and choose work folder
##### Installing required eclipse Plugins
The rest is the same as for Linux. Make sure that the arm-none-eabi-gcc
is properly installed and its path is configured in the _debug
configurations_.
---
title: Getting the serial number of the Crazyflie 2.X
page_id: serial
---
The Crazyflie 2.X ID is a character string of 24 characters. It is
unique to each Crazyflie 2.X.
Windows
-------
- Open the \"Device manager\" and find the Crazyflie 2.0/2.1.
- Right click on Crazyflie 2.0/2.1, go in \"Properties\" then in the
tab \"Details\".
- In the drop-box select \"Device instance path\".
The serial number is located just after \"5740\\\", in the following
screen it starts by 41:
![windows serial](/docs/images/windows_serial.png){:width=500x}
By right-clicking on the string you can copy it.
Linux
-----
Open a terminal and type (ie. cop-paste) the following command:
sudo lsusb -v -d 0483:5740 | grep iSerial
The Crazyflie serial is then displayed (here it starts by 41):
![linux serial](/docs/images/linux_serial.png)
Mac OS X
--------
From the \"About this mac\" menu, click on \"System reports\...\"
![mac serial](/docs/images/mac_serial_about.png){:width=400px}
Then click on \"USB\", locate the Crazyflie 2 and the serial number can
be copied from there
![mac serial 2](/docs/images/mac_serial.png){:width=500px}
---
title: Development for STM32
page_id: starting_development
---
This page aims at documenting how to start developing with Crazyflie.
This document should work for the Crazyflie 2.X.
### STM32
Clone the crazyflie-firmware project, or update it using the virtual
machine \"Update all projects\" script. For Crazyflie 2.X make sure the current branch is \"**master**.\"
~$ cd projects/crazyflie-firmware/
crazyflie-firmware$ git checkout master
And make sure the `submodules` are up-to-date.
```
crazyflie-firmware$ git submodule init
crazyflie-firmware$ git submodule update
```
Then make the firmware.
For **Crazyflie 2.X**:
```
crazyflie-firmware$ make PLATFORM=cf2
(...)
DFUse cf2.dfu
Build for the CF2 platform!
Build 00:00000000 (20XX.XX.X-XX) CLEAN
Version extracted from git
Crazyloader build!
Flash | 218132/1032192 (21%), 814060 free | text: 213024, data: 5108, ccmdata: 0
RAM | 71564/131072 (55%), 59508 free | bss: 66456, data: 5108
CCM | 43528/65536 (66%), 22008 free | ccmbss: 43528, ccmdata: 0
```
To program using the radio bootloader, first install the cflib and cfclient, and put the CF2.X in bootloader mode:
For **Crazyflie 2.X**:
```
crazyflie-firmware$ make cload
```
From command line the flash make target flashed the firmware using
programming cable
```
crazyflie-firmware$ make flash
```
---
title: Adding a new system task
page_id: systemtask
---
**First check out if you can use the [new app layer](/docs/userguides/app_layer.md), which might be enough for your purpose already**
This howto describes how to create a new system task.
A FreeRTOS task is similar to a thread on a standard operating system.
It has its own function call stack, and can be preempted by the RTOS scheduler.
Typical hazards and techniques of shared-memory multithreaded programming apply.
A new task is most often needed when adding a fundamentally new subsystem
to the Crazyflie firmware.
If the new subsystem needs to do
significant computation in response to inputs such as CRTP radio messages, it
should receive those inputs on a queue and perform the computation within
its own task instead of blocking the radio task.
In this example, we will set up the skeleton for such a new subsystem.
Development environment
-----------------------
You should have the
[crazyflie-firmware](https://github.com/bitcraze/crazyflie-firmware) and
[crazyflie-clients-python](https://github.com/bitcraze/crazyflie-clients-python)
cloned in the same folder. If you are using the [Bitcraze
VM](https://github.com/bitcraze/bitcraze-vm) this is already the case.
The Crazyflie firmware should be on Master branch.
For the rest of the howto you will work in the crazyflie-firmware
project.
Setting the task constants
--------------------------
Each task has a few constants that are stored globally in `src/config/config.h`.
First, set the task priority. The FreeRTOS scheduler always prefers to run
a higher-priority task. High priorities should be reserved for very
time-sensitive tasks like the main stabilizer loop.
In `config.h`, after the existing task priorities, add the line
``` {.c}
#define EXAMPLE_TASK_PRI 1
```
Next, each task has a name.
In `config.h`, after the existing task names, add the line
``` {.c}
#define EXAMPLE_TASK_NAME "EXAMPLE"
```
Finally, we must select a fixed size for the task's function call stack.
`configMINIMAL_STACK_SIZE` gives a fairly small stack.
Tasks that do lots of computation will almost certainly need larger stacks.
In `config.h`, after the existing task stack sizes, add the line
``` {.c}
#define EXAMPLE_TASK_STACKSIZE configMINIMAL_STACK_SIZE
```
Implementing the task
---------------------
High level tasks (those that do not directly talk to hardware) usually go in
the `modules` directory. We will walk through the sections needed in a
new file `src/modules/src/example.c`.
First, include the necessary system header files:
``` {.c}
#include "config.h"
#include "debug.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "static_mem.h"
#include "task.h"
```
### File-static variables
Next, we statically allocate the queue to hold inputs from
other parts of the system such as the radio.
Here we just hold integers in the queue. Often it will be a struct instead.
A queue of length 1 is appropriate if new inputs supersede old ones.
For example, if the input controls the current LED color,
we do not care which other colors were requested in the past.
Note that the `STATIC_MEM_*_ALLOC` family of macros expand to static
variable declarations, and can be used outside of any function scope.
``` {.c}
static xQueueHandle inputQueue;
STATIC_MEM_QUEUE_ALLOC(inputQueue, 1, sizeof(int));
```
Next, we allocate the task call stack and other necessary memory for OS
bookkeeping.
The FreeRTOS task API requires that task functions take a `void *` argument.
``` {.c}
static void exampleTask(void*);
STATIC_MEM_TASK_ALLOC(exampleTask, EXAMPLE_TASK_STACKSIZE);
```
### Functions
Next, we implement the `Init()` function.
This should contain all of the FreeRTOS allocations and initializations
that need to last as long as the firmware is running.
Note the use of the constants we defined in `config.h`.
Later in this howto, we will need to edit another file to call the `Init()`.
``` {.c}
static bool isInit = false;
void exampleTaskInit() {
inputQueue = STATIC_MEM_QUEUE_CREATE(inputQueue);
// TODO
STATIC_MEM_TASK_CREATE(exampleTask, exampleTask, EXAMPLE_TASK_NAME, NULL, EXAMPLE_TASK_PRI);
isInit = true;
}
```
Next, we implement the `Test()` function.
Usually this does not do much, besides verify that `Init()` has been called.
Later in this howto, we will need to edit another file to call the `Test()`.
``` {.c}
bool exampleTaskTest() {
return isInit;
}
```
Now we come to the main task function.
It is usually an infinite loop that waits on inputs from something.
In our case, we wait on inputs from the queue.
Our `xQueueReceive` uses the special timeout value `portMAX_DELAY`, which means
that the `xQueueReceive` call will (potentially) block forever and only return
when the queue is not empty.
Other tasks will use integer or zero timeout values
if they have some work to do regardless of whether or not inputs are received.
``` {.c}
static void exampleTask(void* parameters) {
DEBUG_PRINT("Example task main function is running!");
while (true) {
int input;
if (pdTRUE == xQueueReceive(inputQueue, &input, portMAX_DELAY)) {
// Respond to input here!
}
}
}
```
Finally, we implement the public function that is called by other parts of the
system to pass inputs to our new subsystem.
`xQueueOverwrite` specifies that, if the queue is full, we overwrite the
existing contents instead of blocking on the queue's consumer to remove
an item from the other end of the queue.
In subsystems where we do not want to throw away inputs from the queue,
use the `xQueueSend*` family instead.
Subsystems should not expose their queues directly.
It should always be wrapped in a function, like this.
``` {.c}
void exampleTaskEnqueueInput(int value) {
xQueueOverwrite(inputQueue, &value);
}
```
See [the FreeRTOS docs](https://freertos.org/Embedded-RTOS-Queues.html)
for more details on the many `xQueue` API functions available.
> **Design Note:** For complex subsystems, consider implementing the
> algorithmic core as a library that does not depend on FreeRTOS- and
> ARM-related headers, and calling this library from your task.
> This makes it easier to write unit tests for your algorithmic core
> that can be compiled, run, and debugged on a PC.
Writing the public interface
----------------------------
Subsystems should expose a public interface to other parts of the firmware
that hide implementation details as much as is practical.
We create the new file in `src/modules/interface/example.h`:
``` {.c}
#pragma once
#include <stdbool.h>
void exampleTaskInit();
bool exampleTaskTest();
void exampleTaskEnqueueInput(int value);
```
In a real task, make sure to comment the public API thoroughly.
Initializing the task
---------------------
In `src/modules/src/system.c`, make the following changes.
Include the header for our new task:
``` {.c}
#include "example.h"
```
In `systemTask()`, after the lines where the other `Init()` functions
are called, add the line
``` {.c}
exampleTaskInit();
```
In `systemTask()`, after the lines where the other `pass &= ...Test()` lines,
add the line
``` {.c}
pass &= exampleTaskTest();
```
Adding the task to the build
----------------------------
Add this to the Makefile, after the end of the `Modules` block:
``` {.make}
PROJ_OBJ += example.o
```
Compile, flash and run!
-----------------------
Now the last step is to compile and flash your new firmware. Launch the
following commands in a shell:
``` {.bash}
crazyflie-firmware$ make
crazyflie-firmware$ make cload
```
The output will be similar to the following:
``` {.bash}
crazyflie-firmware$ make
(...)
CC hello.o
(...)
Build for the CF2 platform!
Build 22:f8243162f727 (2020.04 +22) MODIFIED
Version extracted from git
Crazyloader build!
Flash | 218132/1032192 (21%), 814060 free | text: 213024, data: 5108, ccmdata: 0
RAM | 71564/131072 (55%), 59508 free | bss: 66456, data: 5108
CCM | 43528/65536 (66%), 22008 free | ccmbss: 43528, ccmdata: 0
crazyflie-firmware$ make cload
../crazyflie-clients-python/bin/cfloader flash cf2.bin stm32-fw
Restart the Crazyflie you want to bootload in the next
10 seconds ...
done!
Connected to bootloader on Crazyflie 2.0 (version=0x10)
Target info: nrf51 (0xFE)
Flash pages: 232 | Page size: 1024 | Buffer pages: 1 | Start page: 88
144 KBytes of flash available for firmware image.
Target info: stm32 (0xFF)
Flash pages: 1024 | Page size: 1024 | Buffer pages: 10 | Start page: 16
1008 KBytes of flash available for firmware image.
Flashing 1 of 1 to stm32 (fw): 161867 bytes (159 pages) ..........10..........10..........10..........10..........10..........10..........10..........10..........10..........10..........10..........10..........10..........10..........10.........9
Reset in firmware mode ...
$
```
Now you can connect your Crazyflie with the client and see the
"Example task main function is running!" in the debug console.
---
title: Unit testing
page_id: unit_testing
---
## Dependencies
Frameworks for unit testing and mocking are pulled in as git submodules.
The testing framework uses ruby and `rake` as well as `libasan` (AddressSanitizer) to generate and run code.
If you run the tests on your own machine you will have to install them.
To minimize the need for installations and configuration, use the docker builder
image (bitcraze/builder) that contains all tools needed. All scripts in the
tools/build directory are intended to be run in the image. The
[toolbelt](https://wiki.bitcraze.io/projects:dockerbuilderimage:index) makes it
easy to run the tool scripts.
## Running all unit tests
With the environment set up locally
make unit
with the docker builder image and the toolbelt
tb make unit
## Running one unit test
When working with one specific file it is often convenient to run only one unit test
make unit FILES=test/utils/src/test_num.c
or with the toolbelt
tb make unit FILES=test/utils/src/test_num.c
## Running unit tests with specific build settings
Defines are managed by make and are passed on to the unit test code. Use the
normal ways of configuring make when running tests. For instance to run test
for Crazyflie 1
make unit LPS_TDOA_ENABLE=1
---
title: Commander
page_id: crtp_commander
---
The commander port is used to send control set-points for the
roll/pitch/yaw/thrust regulators from the host to the Crazyflie. As soon
as the communication link has been established these packets can be sent
and the values are valid until the next packet is received.
Communication protocol
--------------------
+-------+-------+-------+-------+
| ROLL | PITCH | YAW |THRUST |
+-------+-------+-------+-------+
Length 4 4 4 2 bytes
| Name | Byte | Size | Type | Comment|
| --------| -------| ------| -----------| ----------------------|
| ROLL | 0-3 | 4 | float | The pitch set-point|
| PITCH | 4-7 | 4 | float | The roll set-point|
| YAW | 8-11 | 4 | float | The yaw set-point|
| THRUST | 12-13 | 2 | uint16\_t | The thrust set-point|
---
title: Console
page_id: crtp_console
---
This port is used as a one-way text console for printing text from the
Crazyflie to a host using the consoleprintf function.
Communication protocol
======================
Answer (Crazyflie to host):
+---------//-----------+
| PRINTED CONSOLE TEXT |
+---------//-----------+
Length 0-31
The contents of the buffer on the copter side is sent if any of the
following is fulfilled:
- The output buffer (of 31 bytes) is full
- A \"newline\" character has to be send (\\n and/or \\r)
- A flush command as been issued
---
title: Generic Setpoint CRTP Port
page_id: crtp_generic_setpoint
---
This port allows to send setpoints to the platform. The philosophy is to
be able to define setpoint packet format for each different use-case. As
such this is a generic port that has one channel and one main packet
format:
| Port | Channel | Name|
| ------| ---------| --------------------------------------------------|
| 7 | 0 | [Generic setpoint](#generic-setpoint)|
Generic setpoint
----------------
Generic setpoint packet format:
| Byte | Value | Note|
| ------| ---------| ---------------------------|
| 0 |ID | ID of the setpoint packet|
| 1.. | Payload | Format defined per ID|
Defined IDs:
| ID | Type|
| ----| -----------------------------------------------------------------------|
| 0 | [stop](#stop)|
| 1 | [Velocity World](#velocity-world)|
|2 | [Z Distance](#z-distance)|
| 3 | [CPPM Emulation](#cppm-emulation)|
| 4 | [Altitude Hold](#altitude-hold)|
| 5 | [Hover](#hover)|
| 6 | [Full State](#full-state)|
| 7 | [Position](#position)|
### Stop
This is a setpoint with no payload that stops the motors and disables
the control loops. Should be sent when the Crazyflie is landed.
### Velocity World
Velocity setpoint in the world coordinate together with a YAW rotation
speed. Useful for a teleop mode in a local positioning system.
Payload format:
``` {.c}
struct velocityPacket_s {
float vx; // m in the world frame of reference
float vy; // ...
float vz; // ...
float yawrate; // deg/s
} __attribute__((packed));
```
### Z Distance
Set the Crazyflie absolute height and roll/pitch angles. Used for
Z-ranger.
Payload format:
``` {.c}
struct zDistancePacket_s {
float roll; // deg
float pitch; // ...
float yawrate; // deg/s
float zDistance; // m in the world frame of reference
} __attribute__((packed));
```
### CPPM Emulation
CRTP packet containing an emulation of CPPM channels Channels have a
range of 1000-2000 with a midpoint of 1500 Supports the ordinary RPYT
channels plus up to MAX\_AUX\_RC\_CHANNELS auxiliary channels. Auxiliary
channels are optional and transmitters do not have to transmit all the
data unless a given channel is actually in use (numAuxChannels must be
set accordingly)
Current aux channel assignments:
- AuxChannel0: set high to enable self-leveling, low to disable
Payload format:
``` {.c}
#define MAX_AUX_RC_CHANNELS 10
static float s_CppmEmuRollMaxRateDps = 720.0f; // For rate mode
static float s_CppmEmuPitchMaxRateDps = 720.0f; // For rate mode
static float s_CppmEmuRollMaxAngleDeg = 50.0f; // For level mode
static float s_CppmEmuPitchMaxAngleDeg = 50.0f; // For level mode
static float s_CppmEmuYawMaxRateDps = 400.0f; // Used regardless of flight mode
struct cppmEmuPacket_s {
struct {
uint8_t numAuxChannels : 4; // Set to 0 through MAX_AUX_RC_CHANNELS
uint8_t reserved : 4;
} hdr;
uint16_t channelRoll;
uint16_t channelPitch;
uint16_t channelYaw;
uint16_t channelThrust;
uint16_t channelAux[MAX_AUX_RC_CHANNELS];
} __attribute__((packed));
```
### Altitude Hold
Set the Crazyflie vertical velocity and roll/pitch angle.
Payload format:
``` {.c}
struct altHoldPacket_s {
float roll; // rad
float pitch; // ...
float yawrate; // deg/s
float zVelocity; // m/s in the world frame of reference
} __attribute__((packed));
```
### Hover
Set the Crazyflie absolute height and velocity in the body coordinate
system.
Payload format:
``` {.c}
struct hoverPacket_s {
float vx; // m/s in the body frame of reference
float vy; // ...
float yawrate; // deg/s
float zDistance; // m in the world frame of reference
} __attribute__((packed));
```
### Full State
Set the full state.
Payload format:
``` {.c}
struct fullStatePacket_s {
int16_t x; // position - mm
int16_t y;
int16_t z;
int16_t vx; // velocity - mm / sec
int16_t vy;
int16_t vz;
int16_t ax; // acceleration - mm / sec^2
int16_t ay;
int16_t az;
int32_t quat; // compressed quaternion, see quatcompress.h
int16_t rateRoll; // angular velocity - milliradians / sec
int16_t ratePitch; // (NOTE: limits to about 5 full circles per sec.
int16_t rateYaw; // may not be enough for extremely aggressive flight.)
} __attribute__((packed));
```
#### Position
Set the absolute postition and orientation.
``` {.c}
struct positionPacket_s {
float x; // Position in m
float y;
float z;
float yaw; // Orientation in degree
} __attribute__((packed));
```
---
title: Localization CRTP port
page_id: crtp_localizaton
---
This port groups various packets related to localization. It exposes two
channels:
| Port | Channel | Name|
| ------| ---------| ----------------------|
| 6 | 0 | External Position|
| 6 | 1 | Generic localization|
External Position
-----------------
This packet is used to send the Crazyflie position as acquired by an
external system. The main use it to send the position acquired by a
motion capture system to push it in the Extended Kalman Filter to allow
the Crazyflie to calculate an estimate and control its state.
The packet format is:
``` {.c}
struct CrtpExtPosition
{
float x; // in m
float y; // in m
float z; // in m
} __attribute__((packed));
```
Generic Localization
--------------------
This channel intends to host packets useful for the localization
subsystem. It has been created to serve the Loco Positioning System
packets but can be used for more general things like GPS NMEA or binary
streams. The format of the packet is:
| Byte | Value | Note|
| ------| ---------| ---------------------------------------|
| 0 | ID | ID of the packet|
| 1.. | Payload | Packet payload. Format defined per ID|
| ID | Packet |
| ----| ------------------------------|
| 2 | LPP Short packet tunnel|
| 3 | Enable emergency stop|
| 4 | Reset emergency stop timeout|
### LPP Short packet tunnel
Packet used to send LPP short packet to the loco positioning system. The
payload is sent to the sytem as an [LPP Short
Packet](https://www.bitcraze.io/documentation/repository/lps-node-firmware/master/protocols/lpp/).
### Emergency stop
When received, the stabilizer loop is set in emergency stop mode which
stops all the motors. The loop stays in emergency stop mode until the
situation is reset.
### Reset emergency stop timeout
At startup the emergency stop timeout is disabled.
The first time this packet is receive, the emergency stop timeout is
enabled with a timeout of 1 second.
This packet should then be sent, and received by the Crazyflie, at least
once every 1 second otherwise the stabilizer loop will be set in
emergency stop and all motors will stop.
---
title: Logging
page_id: crtp_log
---
For more information on how to use this and how this is implemented have
a look [here](/docs/userguides/logparam.md).
State machines
==============
Downloading the Table Of Contents
---------------------------------
![crtp log](/docs/images/crtp_log.png)
Communication protocol
======================
The log port is separated in 3 channels:
| **Port** | **Channel** | **Function**|
| ----------| -------------| ------------------
| 5 | 0 | Table of content access: Used for reading out the TOC|
| 5 | 1 | Log control: Used for adding/removing/starting/pausing log blocks|
| 5 | 2 | Log data: Used to send log data from the Crazyflie to the client|
Table of content access
-----------------------
This channel is used to download the Table Of Contents that contains all
the variables that are available for logging and what types they are.
The first byte of each messages correspond a command. All communication
on this port are initated by the client and all answer from the copter
will contain the same command byte.
| TOC command byte |Command | Operation|
| ------------------ |----------- |-----------------------------|
| 0 |GET\_ITEM | Get an item from the TOC|
| 1 |GET\_INFO | Get information about the TOC and the LOG subsystem| implementation
### Get TOC item
The GET\_ITEM TOC command permits to retrieved the log variables name,
group and types from the copter. This command is intended to be
requested from all the ID from 0 to LOG\_LEN (see GET\_INFO).
Request (PC to Copter):
+--------------+----+
| GET_ITEM (0) | ID |
+--------------+----+
Length 1 1
Answer (Copter to PC):
+--------------+----+
| GET_ITEM (0) | ID | If index out of range
+--------------+----+------+------------+--------------+
| GET_ITEM (0) | ID | Type | Group | Name | If returning Item
+--------------+----+------+------------+--------------+
Length 1 1 1 < Null terminated strings >
| Byte |Request fields | Content|
| ------| ---------------- |------------------------|
| 0 | GET\_ITEM | At 0 for GET\_ITEM operation|
| 1 | ID | ID of the item to be retrieved. The variables are numbered from 0 to LOG\_LEN (see GET\_INFO command)|
| Byte | Answer fields | Content|
| ------| ---------------| -------------------------------------------------------|
| 0 | GET\_ITEM | 0 for GET\_ITEM operation|
| 1 | ID | ID of the item returned|
| 2 | Type | Variable type of the element. See variable types list|
| 3.. | Group | Null-terminated string containing variable group|
| .. | Name | Null-terminated string containing the variable name|
Type, group and name are not sent if the required ID is higher than
TOC\_LEN-1.
### Get Info
The get info command is intended to be requested first when connecting
to the copter. This permits to know the number of variable, the
limitations of the log implementation and the fingerprint of the log
variables.
Request (PC to Copter):
+--------------+
| GET_INFO (1) |
+--------------+
Length 1
Answer (Copter to PC):
+--------------+---------+---------+-----------------+-------------+
| GET_INFO (1) | LOG_LEN | LOG_CRC | LOG_MAX_PACKET | LOG_MAX_OPS |
+--------------+---------+---------+-----------------+-------------+
Length 1 1 4 1 1
| Byte |Request fields | Content|
| ------ |----------------| ------------------------------|
| 0 |GET\_INFO | At 1 for GET\_INFO operation|
| Byte | Answer fields | Content|
| ------| ------------------| ----------------------------------------------------------|
| 0 | GET\_INFO | 1 for GET\_INFO operation|
| 1 | LOG\_LEN | Number of log items contained in the log table of content|
| 2 | LOG\_CRC | CRC values of the log TOC memory content. This is a fingerprint of the copter build that can be used to cache the TOC|
| 6 | LOG\_MAX\_PACKET | Maximum number of log packets that can be programmed in the copter|
| 7 | LOG\_MAX\_OPS | Maximum number of operation programmable in the copter. An operation is one log variable retrieval programming|
Log control
-----------
The log control channel permits to setup, activate, deactivate and
remove log packets. Like the TOC access channel the first data byte
represents a command.
| Control command byte | Command | Operation|
| ----------------------| ---------------| ---------------------------------------|
| 0 | CREATE\_BLOCK | Create a new log block|
| 1 | APPEND\_BLOCK | Append variables to an existing block|
| 2 | DELETE\_BLOCK | Delete log block|
| 3 | START\_BLOCK | Enable log block transmission|
| 4 | STOP\_BLOCK | Disable log block transmission|
| 5 | RESET | Delete all log blocks|
### Create block
### Append variable to block
### Delete block
### Start block
### Stop block
Log data
--------
The log data channel is used by the copter to send the log blocks at the
programmed rate. The packet format is
Answer (Copter to PC):
+----------+------------+---------//----------+
| BLOCK_ID | TIME_STAMP | LOG VARIABLE VALUES |
+----------+------------+---------//----------+
Length 1 3 0 to 26
| Byte | Answer fields | Content|
| ------| --------------------- --------------------------------|
| 0 | BLOCK\_ID |ID of the block|
| 1 |ID |Timestamp in ms from the copter startup as a little-endian 3 bytes integer|
| 4.. |Log variable values | Packed log values in little endian format|
---
title: Memory access
page_id: crtp_mem
---
Memory access is not used for the [Crazyflie Nano
Quadcopter](https://wiki.bitcraze.io/projects:crazyflie:index), it\'s currently only implemented
in the Crazyflie 2.0. Using the memory access gives the possibility to:
- Get information about which memories are available
- Read/write/erase memories
Currently the following memories are supported:
- Crazyflie 2.0 onboard EEPROM
- Crazyflie 2.0 expansion board 1-wire memories
There\'s more information available for how the EEPROM is structured and
how the 1-wire memories work and are structured.
Logical flow
============
Getting information and reading/writing the memories is optional for the
clients, but the -Crazyflie Python Client- always downloads information
about the memories on connect.
![crtp mem](/docs/images/crtp_mem.png)
Communication protocol
======================
The memory port uses 3 different channels:
**Port** **Channel** **Function**
---------- ------------- ---------------------------------------------------------------------
4 0 Get information about amount and types of memory as well as erasing
4 1 Read memories
4 2 Write memories
Channel 0: Info/settings
------------------------
This channel is used to get the number of memories present, information
about the memories and the possibility to mass erase memories. The first
byte of every packet is a command byte:
| Command byte |Command |Operation|
| -------------- |-------------------- |--------------------------------|
| 1 | GET\_NBR\_OF\_MEMS | Get the number of memories|
| 2 | GET\_MEM\_INFO | Get information about a memory|
| 3 | SET\_MEM\_ERASE | Mass erase a memory|
### GET\_NBR\_OF\_MEMS
This command is used to get the number of memories present.
The request from host to Crazyflie:
| Byte |Field |Value |Length |Comment|
| ------ |-----------------------| ------- |-------- |------------------|
| 0 |GET\_NUMBER\_OF\_MEMS | 0x01 |1 |The command byte|
Reply from Crazyflie to host:
| Byte | Field | Value | Length | Comment|
| ------| -----------------------| -------| --------| ---------------------------|
| 0 | GET\_NUMBER\_OF\_MEMS | 0x01 | 1 | The command byte|
| 1 | Number of memories | | 1 | The number of memories preset (all types)|
Example where there are 3 memories present on the Crazyflie:
Host-to-Crazyflie: <port/chan> 0x01
Crazyflie-to-Host: <port/chan> 0x01 0x03
### GET\_MEM\_INFO
This command is used to get information about a memory give it\'s id.
The id of memories is sequential and is from 0 to one less than the
returned number of memories from GET\_NUMBER\_OF\_MEMS.
The request from host to Crazyflie:
| Byte | Field | Value | Length | Comment|
| ------| ----------------| -------| --------| --------------------------------------------|
| 0 | GET\_MEM\_INFO | 0x02 | 1 | The command byte|
| 1 | MEM\_ID | | 1 | A memory id that is 0 \<= id \< NBR\_OF\_MEMS|
Reply from Crazyflie to host if the id is valid:
| Byte | Field | Value | Length | Comment|
| ------| ----------------| -------| --------| -------------------|
| 0 | GET\_MEM\_INFO | 0x02 | 1 | The command byte|
| 1 | MEM\_ID | | 1 | The memory id|
| 2 | MEM\_TYPE | | 1 | The memory type (see below)|
| 3 | MEM\_SIZE | | 4 | The size in bytes of the memory|
| 7 | MEM\_ADDR | | 8 | The address of the memory (only valid for 1-wire memories)|
Where the MEM\_TYPE field is:
| MEM\_TYPE | Memory type |Comment |
| -----------| ------------- |---------|
| 0 | I2C | |
| 1 | 1-wire ||
Reply from Crazyflie to host if the id is not valid:
| Byte | Field | Value | Length | Comment
| ------| ----------------| -------| --------| ------------------
| 0 | GET\_MEM\_INFO | 0x02 | 1 | The command byte
| 1 | MEM\_ID | | 1 | The memory id
Example of requesting the information for a 1-wire memory with
MEM\_ID=1, MEM\_SIZE=112bytes, MEM\_ADDR=0x1234567890ABCDEF
Host-to-Crazyflie: <port/chan> 0x02 0x01
Crazyflie-to-Host: <port/chan> 0x02 0x01 0x01 0x70 0x00 0x00 0x00 0xEF 0xCD 0xAB 0x90 0x78 0x56 0x34 0x12
Example of requesting the information for a memory index that is not
valid
Host-to-Crazyflie: <port/chan> 0x01 0x10
Crazyflie-to-Host: <port/chan> 0x01 0x10
### SET\_MEM\_ERASE
This command is used to mass erase a memory with a given id. The id of
memories is sequential and is from 0 to one less than the returned
number of memories from GET\_NUMBER\_OF\_MEMS.
The request from host to Crazyflie:
| Byte | Field | Value | Length | Comment|
| ------| ------------------|-------|--------|--------------|
| 0 | SET\_MEM\_ERASE | 0x03 | 1 | The command byte|
| 1 | MEM\_ID | | 1 | A memory id that is 0 \<= id \<| NBR\_OF\_MEMS
Reply from Crazyflie to host:
| Byte | Field | Value | Length | Comment|
| ------| -----------------| -------| --------| ---------------------------------------|
| 0 | SET\_MEM\_ERASE | 0x03 | 1 | The command byte|
| 1 | MEM\_ID | | 1 | The memory id|
| 2 | STATUS | | 1 | The status of the command (see below)|
Example of requesting a mass erase for a memory with MEM\_ID=2
Host-to-Crazyflie: <port/chan>
Crazyflie-to-Host: <port/chan>
Example of
Host-to-Crazyflie: <port/chan>
Crazyflie-to-Host: <port/chan>
Channel 1: Memory read
----------------------
This channel is only used to read memories and therefore the messages do
not contain any command byte.
The request from host to Crazyflie:
| Byte | Field | Value |Length |Comment|
| ------| -----------| ------- -------- |-------------------------------------------------|
| 0 | MEM\_ID | | 1 |A memory id that is 0 \<= id \< NBR\_OF\_MEMS |
| 1 | MEM\_ADDR | |4 |The address where the first byte should be read |
| 5 | LEN | |1 |The number of bytes to be read |
Reply from Crazyflie to host if memory id is valid and the address and
length to be read is valid:
| Byte | Field | Value |Length | Comment|
| -----| -----------| ------- |-------- |--------------|
| 0 | MEM\_ID | | 1 | A memory id that is 0 \<= id \< NBR\_OF\_MEMS|
| 1 | MEM\_ADDR | | 4 | The address where the first byte should be read|
| 5 | STATUS | | 1 | The status of the request (see below)|
| 6 | DATA | | 1..24? |The data that was read (only if MEM\_ID/MEM\_ADDR/LEN is valid)|
Where the STATUS field is:
STATUS Comment
-------- ---------
0 \...
1 \....
Example of reading LEN=0x0F bytes from MEM\_ID=0x01 MEM\_ADDR=0x0A
Host-to-Crazyflie: <port/chan> 0x01 0x0A 0x00 0x00 0x00 0x0F
Crazyflie-to-Host: <port/chan> 0x01 0x0A 0x00 0x00 0x00 0x00 0x01 0x09 0x62 0x63 0x4C 0x65 0x64 0x52 0x69 0x6E 0x67 0x02 0x01 0x62 0x55
Channel 2: Memory write
-----------------------
This channel is only used to write memories and therefore the messages
do not contain any command byte.
The request from host to Crazyflie:
| Byte | Field | Value | Length | Comment|
| ------| ----------- |------- |--------| -------------------------------------------------|
| 0 | MEM\_ID | |1 | A memory id that is 0 \<= id \< NBR\_OF\_MEMS|
| 1 | MEM\_ADDR | | 4 | The address where the first byte should be read|
| 5 | DATA | | 1..24? | The data to be written|
Reply from Crazyflie to host if memory id is valid and the address and
length to be written is valid:
| Byte | Field | Value | Length | Comment|
| ------| -----------| -------| --------| -------------------------------------------------|
| 0 |MEM\_ID | | 1 | A memory id that is 0 \<= id \< NBR\_OF\_MEMS|
| 1 | MEM\_ADDR | | 4 | The address where the first byte should be read|
| 4 |STATUS | | 1 | The status of the request (see below)|
Where the STATUS field is:
| STATUS | Comment|
| --------| ---------|
| 0 | \...|
| 1 | \....|
Example
---
title: Parameters
page_id: crtp_parameters
---
The parameters system makes all the gettable and settable parameters of
the copter accessible. The copter holds a table of parameters which can
be retrieved. In this table every parameter name is associated with an
ID and a group name. Three IDs are used to acces the TOC and the
parameters:
| Port | Channel | Function |
| ------| ---------| --------------------------------------------------------------|
| 2 | 0 | [TOC access](#toc-access)|
| 2 | 1 | [Parameter read](#parameter-read)|
| 2 | 2 | [Parameter write](#parameter-write)|
| 2 | 3 | [Misc. commands](#misc-commands)|
TOC access
----------
These messages permit to access the parameters table of content. The
first byte of the message is a message ID, three messages ID are
defined:
| Message ID | Meaning in upstream packets | Meaning in downstream packets |
| ------------| -----------------------------| -------------------------------------|
|0 | Reset TOC pointer | Last TOC element|
| 1 | Get next TOC element | TOC element (ID, type, group, name)|
| 3 | Get TOC CRC32 | Number of parameters, TOC CRC32|
The upstream ID are commands and are sent alone. The downstream has the
following formats:
Bytes 1 1 1 Null terminated strings
+---+------------+------+----------+--------------+
| 0 | | | | |
+---+ Param ID | Type | Group | Name |
| 1 | | | | |
+---+------------+------+---+------+--------------+
| 3 | Num. Param | CRC32 |
+---+------------+----------+
Bytes 1 1 4
The parameters are sequentially requested by the PC until the end. When
the last parameter is reached it has the ID 0 \'Last TOC element\'. The
reset command permits to reset the TOC pointers so that the next sent
TOC element will be the first one. The \"Get TOC CRC\" command also
returns the number of parameters.
The CRC32 is a hash of the copter TOC. This is aimed at implementing
caching of the TOC in the PC Utils to avoid fetching the full TOC each
time the copter is connected.
The type is one byte describing the parameter type:
| Type code | C type | Python unpack |
| -----------| -----------| ---------------|
| 0x08 | uint8\_t | \'&lt;B \'|
| 0x09 | uint16\_t | \'&lt;H\' |
| 0x0A | uint32\_t | \'&lt;L\' |
| 0x0B | uint64\_t | \'&lt;Q\' |
| 0x00 | int8\_t | \'&lt;b\' |
| 0x01 | int16\_t | \'&lt;h\' |
| 0x02 | int32\_t | \'&lt;i\' |
| 0x03 | int64\_t | \'&lt;q\' |
| 0x05 | FP16 | \'\' |
| 0x06 | float | \'&lt;f\' |
| 0x07 | double | \'&lt;d\' |
Parameter read
--------------
| Byte | Request fields | Content
| ------| ----------------| ---------------------------------------
| 0 | ID | ID of the parameter to read (see TOC)
| Byte | Answer fields |Content|
| ------| ---------------| -----------------------------------------------------------------|
| 0 | ID | ID of the parameter |
| 1-.. | value | Value of the parameter. Size and format is described in the TOC |
The read request is a simple packet on channel 1. Crazyflie answers with
the value.
Parameter write
---------------
| Byte | Request fields |Content|
| --------| ---------------- |---------------------------------------------------------|
| 0 | ID |ID of the parameter to write|
| 1-\... | value | Value to write. Size and format is described in the TOC|
| Byte |Answer fields |Content|
| -------- |--------------- |-----------------------------------------------------------------|
| 0 |ID |ID of the parameter|
| 1-\... |value |Value of the parameter. Size and format is described in the TOC|
The write request is a simple packet on channel 2. Crazyflie sends back
the parameter value as an acknowledgement.
Misc. commands
--------------
The following misc commands are implemented:
| Code | Command|
| ------| ------------------------------------------------------|
| 0x00 | [Set by name](#set-by-name)|
| 0x01 | [Value updated](#value-updated)
### Set by name
| Byte | Request fields | Content|
| -----------------| ----------------| ---------------------------------------------|
| 0 | SET\_BY\_NAME | 0x00 |
| 1-n | group | Name of the group |
| n-(n+1) | NULL | 0 |
| (n+1)-(n+m+1) | name | Name of the parameter |
| (n+m+1)-(n+m+2) | NULL | 0 |
| (n+m+2)-(n+m+3) | TYPE | Parameter type |
| (n+m+3)-\... | value | Value. Size and format is described by type |
| Byte | Answer fields | Content|
| ---------------| ---------------| ---------------------------------------------------|
| 0 | SET\_BY\_NAME | 0x00|
| 1-n | group | Name of the group|
| n-(n+1) | NULL | 0|
| (n+1)-(n+m+1) | name | Name of the parameter|
| (n+m+2) | NULL | 0|
| (n+m+3) | ERROR | 0 if the parameter has been successfully written. Other code are taken from [errno C codes](http://www.virtsync.com/c-error-codes-include-errno). |
*Group* and *name* are ascii strings of size respectively *n* and *m*.
The type corresponds to the TOC type of the parameter. It is checked for
consistency.
This command is useful to set a parameter without having to fetch the
full TOC. It is enough to know the group, name and type of the parameter
to write it.
### Value updated
There is no request packet for this message, it is only sent by the Crazyflie.
| Byte | Answer fields | Content|
| ---------------| ----------------| ---------------------------------------------------|
| 0 | VALUE\_UPDATED | 0x01 |
| 1-2 | ID | ID of the parameter |
| 3-\... |value |Value of the parameter. Size and format is described in the TOC|
This packet is send by the Crazyflie when a parameters has been modified in the firmware.
This can for example happen when an app is controlling the Crazyflie autonomously.
---
title: CRTP - Communication with the Crazyflie
page_id: crtp_index
---
For communicating with the Crazyflie we have implemented our own
high-level protocol named CRTP (Crazy RealTime Protocol). It\'s a fairly
simple protocol using a number of target ports where data can be sent
and received in either direction, but in most cases the communication is
driven from the host. The protocol can be implemented on a number of
mediums, currently we have USB and Crazyradio support.
CRTP
====
Physical carriers
-----------------
Currently CRTP is supported over Crazyradio and USB (currently only for
Crazyflie 2.0).
| Carrier | Supports|
| -----------------| -------------------|
| Crazyradio (PA) | Crazyflie 1.0/2.0|
| USB | Crazyflie 2.0|
Header
------
Each packet has a 1 byte header and can carry up to 31 bytes data
payload. The header field has the following layout:
7 6 5 4 3 2 1 0
+----+----+----+----+----+----+----+----+
| Port | Link | Chan. |
+----+----+----+----+----+----+----+----+
Where:
- **Port**: Used to identify the functionality or task that is
associated with this message
- **Link**: Reserved for future use
- **Channel**: Used to identify the sub-task/functionality
Port allocation
---------------
Current port allocation:
| **Port** | **Target** | **Used for**|
| ---------| ---------------------------------------------| ----------------------------------------------------------------|
| 0 | [Console](crtp_console.md) | Read console text that is printed to the console on the Crazyflie using consoleprintf|
| 2 | [Parameters](crtp_parameters.md) | Get/set parameters from the Crazyflie. Parameters are defined using a [macro in the Crazyflie source-code](/docs/userguides/logparam.md)|
| 3 | [Commander](crtp_commander.md) | Sending control set-points for the roll/pitch/yaw/thrust regulators|
| 4 | [Memory access](crtp_mem.md) | Accessing non-volatile memories like 1-wire and I2C (only supported for Crazyflie 2.0)|
| 5 | [Data logging](crtp_log.md) | Set up log blocks with variables that will be sent back to the Crazyflie at a specified period. Log variables are defined using a [macro in the Crazyflie source-code](/docs/userguides/logparam.md)
| 6 | [Localization](crtp_localization.md) | Packets related to localization|
| 7 | [Generic Setpoint](crtp_generic_setpoint.md) | Allows to send setpoint and control modes|
| 13 | Platform | Used for misc platform control, like debugging and power off|
| 14 | Client-side debugging | Debugging the UI and exists only in the Crazyflie Python API and not in the Crazyflie itself.|
| 15 | Link layer | Used to control and query the communication link|
Connection procedure
--------------------
CRTP is designed to be state-less, so there\'s no handshaking procedure
that is needed. Any command can be sent at any time, but for some
logging/param/mem commands the TOC (table of contents) needs to be
downloaded in order for the host to be able to send the correct
information. The implementation of the Pyton API will download the
param/log/mem TOC at connect in order to be able to use all the
functionality.
---
title: Deck memory format
page_id: deck_memory_format
---
The OneWire (OW) memory contained in all Crazyflie 2.0 decks is used to
detect the boards connected in order to:
- Decide if it is safe to start the system.
- When the system is started, initialize the required drivers for the
deck installed.
The memory format has CRC to guarantee data integrity. It is separated
in two part:
- The header is 7 bytes long and contains the identity of the board
encoded in VID/PID (Vendor ID and Product ID) as well as a bitfield
describing the pins the deck is driving (to avoid conflict).
- The key/value area is extendable and contains more information like
the deck name, board revision and driver-specific information (ie.
for configuration or calibration for example).
Content
-------
1. Header 1 Byte 0xEB
2. UsedPins 4 Bytes
3. VID 1 Byte
4. PID 1 Byte
5. crc 1 Byte CRC32[0] (LSB byte) from 1 to 4.
6. Version 1Byte, 0 for now
7. DataLength 1 Byte (255 max data length…)
8. key/value data <DataLength> bytes
9. crc 1 Byte CRC32[0] (LSB byte) from 6 to 8
- The header area is from 1 to 5.
- The key/value area is from 6 do 9
Header
------
The header allows to detect the deck installed and to verify that the
deck stack is compatible.
The *UsedPins* value indicates which pins the **deck is driving** on the
expansion port. For example a UART TX pin is driving both high and low
when an i2c port is only driving low. For every GPIO on the expantion
port one bit indicate if the GPIO can be driven high and one bit if it
can be driven low:
| Drive | PC11 | PC10 | PB7 | PB6 | PB8 | PB5 | PB4 | PC12 | PA2 | PA3 | PA5 | PA6 | PA7 | P0.11 | P0.12 | P0.08 |
| ---------- |------| ------| -----| -----| -----| -----| -----| ------| -----| -----| -----| ----- |-----| -------| -------| -------|
| **Low** | 0 |1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15|
| **High** | 16 |17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 31|
For example is a deck has a GPS that has its UART TX on PC11 (push-pull,
drives high and low), the *UsedPins* value will be (1\<\<0) \| (1\<\<16)
= 0x00010001. As another example is a deck has an I2C sensor on PB6,PB7
(Open collector, only drives low), the *UsedPins* value is then:
(1\<\<2) \| (1\<\<3) = 0x0000000C
The *VID* is the vendor ID (or Maker ID :). Currently only 0xBC is
reserved for Bitcraze and 0x00 is reserved for test/development. Contact
Bitcraze if you plan to distribute decks and want a *VID*.
A deck with *VID/PID* = 0/0 must have its name in the key/value area
(the driver will then be chosen by name instead of pid/vid).
Key/value area
--------------
The *version* is 0. The key/value data contains a succession of
elements. Elements id and types are predefined. Unknown elements are
ignored.
Elements have this format:
| **Id** | 1Byte|
| **length** | 1Byte|
| **data** |\<length\> Bytes|
The following elements are defined:
| Element | Element\_id | Data type | Note|
| ------------| -------------| -----------| -------------------------------------------|
| boardName | 1 | string | Short. Starts by "bc" for Bitcraze boards|
| revision | 2 | string | |
| customData | 3 | binary | Data understood by the deck driver|
Example
-------
This is an example of OW memory content for the led ring deck.
0000000: eb 00 00 00 00 bc 01 44 00 0e 01 09 62 63 4c 65 .......D....bcLe
0000010: 64 52 69 6e 67 02 01 62 55 dRing..bU
Content:
{
"header": {
"usedPin": 0,
"vid": 188,
"pid": 1
},
"data": {
"boardName": "bcLedRing",
"revision": "b"
}
}
Element IDs and types (DTD):
{
"boardName": {
"type": "string",
"id": 1
},
"revision": {
"type": "string",
"id": 2
},
"customData": {
"type": "binary",
"id": 3
}
}
---
title: Lighthouse angle conversion
page_id: lh_angle_conversion
---
One way to get started with lighthouse 2 is to create a conversion from lighthouse 2 angles to lighthouse 1 angles,
and use the functionality that has already been implemented for lighthouse 1. Even though it is an easy way to get started, the main drawback is that we need both sweeps for the conversion. Lighthouse 1 angles are also easier to
understand and can be a useful debugging tool.
## Lighthouse 1 to lighthouse 2
Start from the [measurement model for the kalman filter](./kalman_measurement_model.md).
For a point $$(x, y, z)$$, the sweep angles are
$$
\begin{cases}
\alpha^{lh2}_1 = \tan^{-1}(\frac{y}{x}) + \sin^{-1}(\frac{z\tan(-t)}{\sqrt{x^2 + y^2}})\\
\alpha^{lh2}_2 = \tan^{-1}(\frac{y}{x}) + \sin^{-1}(\frac{z\tan(t)}{\sqrt{x^2 + y^2}})
\end{cases}
$$
where $$t = \frac{\pi}{6}$$
$$x$$, $$y$$ and $$z$$ are given by the lighthouse 1 sweep angles
$$
\begin{cases}
x = 1 \\
y = \tan(\alpha^{lh1}_1) \\
z = \tan(\alpha^{lh1}_2)
\end{cases}
$$
this leads to
$$
\begin{cases}
\alpha^{lh2}_1 = \alpha^{lh1}_1 + \sin^{-1}(\frac{\tan(\alpha^{lh1}_2)\tan(-t)}{\sqrt{1 + \tan^2(\alpha^{lh1}_1)}})\\
\alpha^{lh2}_2 = \alpha^{lh1}_1 + \sin^{-1}(\frac{\tan(\alpha^{lh1}_2)\tan(t)}{\sqrt{1 + \tan^2(\alpha^{lh1}_1)}})\\
\end{cases}
$$
or
$$
\begin{cases}
\alpha^{lh2}_1 = \alpha^{lh1}_1 + \sin^{-1}(q\tan(-t))\\
\alpha^{lh2}_2 = \alpha^{lh1}_1 + \sin^{-1}(q\tan(t))\\
\end{cases}
$$
where
$$q = \frac{\tan(\alpha^{lh1}_2)}{\sqrt{1 + \tan^2(\alpha^{lh1}_1)}}$$
## Lighthouse 2 to lighthouse 1
The strategy when creating an equation to convert from lighthouse 2 sweep angles to lighthouse 1, is to find the intersection line between the two light planes. From the intersection line we can easily calculate the lighthouse 1 sweep angles.
The following calculations are all in the base station reference frame.
Assume we know the normals for the two light planes, $$\vec{n_1}$$ and $$\vec{n_2}$$. The cross product of the normals $$\vec{v} = \vec{n_1} \times \vec{n_2}$$
gives us the direction of the intersection line. We also know it is passing through the origin as we are using the base station
reference frame.
### Normals of the light planes
Start by figuring out the normals when $$\alpha=0$$, and then rotate them around the Z-axis using a rotation matrix.
The rotation matrix is
$$R_{z} = \left[\begin{array}{ccc}
cos{\alpha} & -sin{\alpha} & 0 \\
sin{\alpha} & cos{\alpha} & 0 \\
0 & 0 & 1
\end{array}\right]$$
and the resulting normals
$$
\begin{cases}
\vec{n1}=R_z \cdot \begin{bmatrix}0 & -cos{(t)} & sin{(t)}\end{bmatrix} = \begin{bmatrix}cos{(t)}sin{(\alpha^{lh2}_1)} & -cos{(t)}cos{(\alpha^{lh2}_1)} & sin{(t)}\end{bmatrix} \\
\vec{n2}=R_z \cdot \begin{bmatrix}0 & -cos{(t)} & -sin{(t)}\end{bmatrix} = \begin{bmatrix}cos{(t)}sin{(\alpha^{lh2}_2)} & -cos{(t)}cos{(\alpha^{lh2}_2)} & -sin{(t)}\end{bmatrix}
\end{cases}
$$
where $$t=\pi/6$$ is the tilt angle of the light planes.
### The intersection vector
$$\vec{v} = \vec{n_1} \times \vec{n_2} = \begin{bmatrix}-\sin{(t)}\cos{(t)}(\cos{(\alpha^{lh2}_1)} + \cos{(\alpha^{lh2}_2)}) & -\sin{(t)}\cos{(t)}(\sin{(\alpha^{lh2}_1)} + \sin{(\alpha^{lh2}_2)}) & \cos^2{(t)}(\sin{(\alpha^{lh2}_1)}\cos{(\alpha^{lh2}_2)}-\cos{(\alpha^{lh2}_1)}\sin{(\alpha^{lh2}_2)})\end{bmatrix}$$
### Lighthouse 1 angles
Finally we can calculate the lighthouse 1 angles
$$
\begin{cases}
\alpha^{lh1}_1 = \tan^{-1}(\frac{v_2}{v_1})\\
\alpha^{lh1}_2 = \tan^{-1}(\frac{v_3}{v_1})
\end{cases}
$$
$$
\begin{cases}
\alpha^{lh1}_1 = \tan^{-1}(\frac{-\sin{(t)}\cos{(t)}(\sin{(\alpha^{lh2}_1)} + \sin{(\alpha^{lh2}_2)})}{-\sin{(t)}\cos{(t)}(\cos{(\alpha^{lh2}_1)} + \cos{(\alpha^{lh2}_2)})})\\
\alpha^{lh1}_2 =
\tan^{-1}(\frac{\cos^2{(t)}(\sin{(\alpha^{lh2}_1)}\cos{(\alpha^{lh2}_2)}-\cos{(\alpha^{lh2}_1)}\sin{(\alpha^{lh2}_2)})}{-\sin{(t)}\cos{(t)}(\cos{(\alpha^{lh2}_1)} + \cos{(\alpha^{lh2}_2)})})
\end{cases}
$$
$$
\begin{cases}
\alpha^{lh1}_1 = \frac{\alpha^{lh2}_1 + \alpha^{lh2}_2}{2}\\
\alpha^{lh1}_2 = \tan^{-1}(\frac{\sin{(\alpha^{lh2}_2 - \alpha^{lh2}_1)}}{\tan{(t)} (\cos{(\alpha^{lh2}_1)} + \cos{(\alpha^{lh2}_2)})})\end{cases}
$$
---
title: The Lighthouse positioning system
page_id: lh_overview
redirects:
- /docs/functional-areas/lighthouse_overview/
---
The Lighthouse positioning system uses the HTC Vive base stations (aka Lighthouse) togeather with the Lighthouse
deck to achieve high precision positioning.
The basics:
* [System overview](/docs/functional-areas/lighthouse/system_overview.md)
* [Limitations](/docs/functional-areas/lighthouse/limitations.md)
* [Positioning methods](/docs/functional-areas/lighthouse/positioning_methods.md)
Development related pages
* [Terminology and definitions](/docs/functional-areas/lighthouse/terminology_definitions.md)
* [Kalman estimator measurement model](/docs/functional-areas/lighthouse/kalman_measurement_model.md)
* [Converting between LH1 and LH2 angles](/docs/functional-areas/lighthouse/angle_conversion.md)
---
title: Lighthouse kalman measurment model
page_id: lh_measurement_model
---
This page describes the generalized measurement model used for the lighthouse in the kalman state estimator.
In the measurement model we want to get from a sensor position $$\vec{s}$$ to rotation angle $$\alpha$$.
The first step is to calculate the sensor position in the rotor reference frame.
Use a rotation matrix $$R_r$$ to go from the base station reference frame to the rotor reference frame.
For LH2 and the horizontal rotor in LH1 this is the unit matrix, while the vertical drum in LH1 gets
$$R_{vert} = \left[\begin{array}{ccc}
1 & 0 & 0 \\
0 & 0 & 1 \\
0 & -1 & 0
\end{array}\right]$$
The sensor has position $$\vec{s_{cf}}$$ in the CF reference frame and
$$\vec{s} = \vec{p_{cf}} + R_{cf} \cdot \vec{s_{cf}}$$ in the global reference frame. The sensor position
in the base station reference frame is $$s_{bs} = R_{bs}^{-1} \cdot (\vec{s} - \vec{p_{bs}}) =
R_{bs}^{-1} \cdot (\vec{p_{cf}} - \vec{p_{bs}} + R_{cf} \cdot \vec{s_{cf}})$$
Finally, the sensor position in the rotor reference frame is $$\vec{s_r} =
R_r \cdot R_{bs}^{-1} \cdot (\vec{p_{cf}} - \vec{p_{bs}} + R_{cf} \cdot \vec{s_{cf}})$$
## Measurement
The measurement is the rotation angle $$\alpha$$ when the sensor is hit by the light plane.
## Prediction
To calculate the predicted rotation angle $$\alpha_p$$ we have to go from the sensor position
($$s_r = (x, y, z)$$ in the rotor reference frame) to rotation angle, where the rotation angle is from
the X-axis to the line where the light plane intersects the XY-plane. The rotation angle to the sensor
$$\alpha_s$$ is the sum of the predicted rotation angle $$\alpha_p$$ and the rotation angle from the
intersection line to the sensor $$\alpha_t$$, caused by the tilt of the light plane,
$$\alpha_s = \alpha_p + \alpha_t$$
![Prediction geometry](/docs/images/lighthouse/prediction_geometry.png)
The rotation angle to the sensor $$\alpha_s$$ is defined by
$$\tan \alpha_s = \frac{y}{x}$$
$$\alpha_s = \tan^{-1}(\frac{y}{x})$$
To calculate $$\alpha_t$$ we first have to look at the sensor position projected on the XY-plane
$$(x, y, 0)$$. The radius to this point is $$r = \sqrt{x^2 + y^2}$$
We also need the distance $$d$$ from the intersection line to the sensor, perpendicular to the
intersection line, $$d=r\sin \alpha_t$$.
$$d$$ can also be calculated using the tilt and z, $$d=z\tan -t$$. If we combine these
$$r\sin \alpha_t = z\tan -t$$
$$\sin \alpha_t = \frac{z\tan -t}{r} = -\frac{z\tan t}{\sqrt{x^2 + y^2}}$$
$$\alpha_t = \sin^{-1}(-\frac{z\tan t}{\sqrt{x^2 + y^2}}) = -\sin^{-1}(\frac{z\tan t}{\sqrt{x^2 + y^2}})$$
Finally we can calculate the predicted rotation angle
$$\alpha_p = \alpha_s - \alpha_t = \tan^{-1}(\frac{y}{x}) + \sin^{-1}(\frac{z\tan t}{\sqrt{x^2 + y^2}})$$
## H vector
Calculate the position elements of the H vector in the rotor reference frame
$$\vec{g_r} = \begin{pmatrix}
\frac{d\alpha_p}{dx} & \frac{d\alpha_p}{dy} & \frac{d\alpha_p}{dz}
\end{pmatrix}$$
$$\vec{g_r} = \begin{bmatrix}
\frac{-y}{x^2 + y^2} - \frac{xz\tan t}{(x^2+y^2)^{\frac{3}{2}} \sqrt{1-\frac{(z\tan t)^2}{x^2+y^2}} } &
\frac{x}{x^2 + y^2} - \frac{yz\tan t}{(x^2+y^2)^{\frac{3}{2}} \sqrt{1-\frac{(z\tan t)^2}{x^2+y^2}} } &
\frac{\tan t}{\sqrt{x^2+y^2} \sqrt{1-\frac{(z\tan t)^2}{x^2+y^2}} }
\end{bmatrix}$$
Let $$r=\sqrt{x^2+y^2}$$ and $$Q=\frac{\tan t}{\sqrt{x^2+y^2} \sqrt{1-\frac{(z\tan t)^2}{x^2+y^2}}} =
\frac{\tan t}{r \sqrt{1-\frac{(z\tan t)^2}{r^2}}} = \frac{\tan t}{\sqrt{r^2-(z\tan t)^2}} $$
Which leads to
$$\vec{g_r} = \begin{bmatrix}
\frac{-y-xzQ}{x^2 + y^2} &
\frac{x-yzQ}{x^2 + y^2} &
Q
\end{bmatrix}$$
$$\vec{g_r} = \begin{bmatrix}
\frac{-y-xzQ}{r^2} &
\frac{x-yzQ}{r^2} &
Q
\end{bmatrix}$$
Rotate the position elements to the global reference frame to be used in the kalman filter
$$\vec{g}=(R_r \cdot R_{bs}^{-1})^{-1} \cdot \vec{g_r} = R_{bs} \cdot R_r^{-1} \cdot \vec{g_r}$$
Finally we have the H vector
$$H=(g_x, g_y, g_z, 0, 0, 0...)$$
## Optimizations
For rotation matrices the following is true
$$R^{-1} = R^T$$
$$R_1 \cdot (R_2 \cdot \vec{v}) = (R_1 \cdot R_2) \cdot \vec{v}$$
---
title: Lighthouse limitations
page_id: lh_limitations
---
One limitation of the lighthouse deck is that since the deck only has horizontal sensors, the angle at which the base-stations are seen cannot be too shallow. This means that you should fly at least 40cm bellow the base-stations and that the base-stations should be placed above the flight space.
## State of the base station V2 support
The lighthouse V2 support is currently at a Minimum Viable Product state.
This means that is it well supported but still has some limitation that can be lifted in future development.
* 1 and 2 base stations are supported
* The base stations must be configured to use channel 1 and 2
---
title: Lighthouse positioning methods
page_id: lh_positioning_methods
---
There are two basic methods for estimating the position of the Lighthouse deck, they are outlined here.
## Crossing beams
This was the first method to be implemented and is simple and roubust, but requires two base stations to be visible.
The idea is to calculate the vectors from two basestation to a sensor on the Lighthouse deck. This vector is defined by the
intersection line between the two lightplanes of the base station and is sometimes referred to as a "beam", hence the name.
In theory the beams should cross in the point where the sensor is located, in real life there are errors and the
beams will not exactly meet. To handle this the algorithm calculates the point that is closest to both beams instead, and uses
this as the estimated position.
The distance from the estimated position to the beam is called the delta and is available as a log in the Crazyflie. It provides
a measurement of the error in system.
The calculated position is fed into the kalman estimator to be used together with other sensor data.
## Raw sweeps
It is preferable to use senstor data that has been processed as lite as possible in a kalman filter, and that is
what we try to do in this method. The base station geometry and angle of one sweep is passed directly into
the kalman estimator to be used to improve the estimate. The measurement model is based on the fact that the
sensor must be located in the plane that is defined by the base station geometry and sweep angle.
One base station is enough to estimate the position using this method, but more base stations adds precission and redundancy.
## Ground truth
To use the lighthouse positioning as a ground truth measurement for your research, you should put ```CFLAGS += -DLIGHTHOUSE_AS_GROUNDTRUTH``` in your config.mk.
This will default the position estimator for lighthouse to be crossing beam (which you should not change), and you will be able to get the X, Y, Z position from the logs ```lighthouse.x/.y/.z```