This repository contains the software running on the ESP32 chips that are part of the Modular Flight System (MFS). We are currently supporting 4 different board types / PCBs (Printed Circuit Boards):
All boards share a common (this) code base, since they all use very similar driver / boilerplate software.
Install PlatformIO Core using the guide provided here. More specifically, run this command in your console python3 -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)"
and add this line export PATH=$PATH:~/.platformio/penv/bin
to your ~/.profile
.
You should now be able to compile and upload code to the boards via the 6 pin UART program header using the ESP-Prog. Power up the boards using a battery or a power supply (make sure the voltage is around 12v). Ensure that the ESP-Prog does not provide any power to the boards via the 6 pin header (set the jumpers accordingly). Connect the board of interest (if its not the PSU board itself) to the PSU boad via the CAN D-SUB connector and ensure that at least one LED lights up, indicating that the board is powered up. Finally connect the ESP-Prog to your computer and the board via the 6 pin header to the ESP-Prog.
pio run -t upload -e <BOARD>
pio device monitor --raw
pio run -t upload -e <BOARD> && pio device monitor --raw
pio device list
pio run --t menuconfig
pio check
<BOARD>
can be any of the following:
RAI
PSU
AHRS
GPS
The Cannot open /dev/ttyUSBx: Permission denied
error is caused by the user not having access to the serial ports. More specifically, the user is not in the dialout
group. Run this sudo usermod -a -G dialout $USER
and log out and back in again to fix it.
For anyone who likes knowing what they're running before they run it:
usermod
- modify a user account-a
- add the user to supplementary groups-G
- a list of supplementary groups (man page says to use -a
only with -G
)dialout
- group that controls access to serial ports (and other hardware too)$USER
- Bash variable containing current username (not a builtin, usually automatically set env variable)The AHRS system needs to be calibrated in order to function properly. More specifically, one needs to calibrate the IMU sensors on the AHRS board. That is:
The magnetometer requires the correction of the hard- and softiron error. The hardiron error consists of 3 values, while the softiron error consists of a 3x3 correction matrix. Additionally, 3 current correction scaling values need to be determined using linear regression.
IMPORTANT: When calibrating the system you need to select the "NATIVE" orientation in board.h
and uncomment (activate) the "CALIBRATION" flag in drv_ak8963.c
.
TODO
The RAI (Radio Actuator Interface) is the most important board of all boards. It enables communication from the RC (Radio Control) receiver, which receives information form the pilots transmitter, to the servos and the motor. It is also able to transmit and receive control data over the CAN bus. This way the pilot can enable the autopilot / controller running on the Flight Control Computer (FCC). The software running on the RAI needs to be extremely reliable and bug free. Any fault could cause a loss of communication with the pilot which would inevitably lead to a loss of the aircraft.
The main tasks of the RAI include:
The AHRS (Altitude Heading Reference System) provides orientation (attitude) information.
Steps needed for IMU / AHRS calibration:
Data needed:
PSU (Power Supply Unit) supplies power to the system via the CAN bus. It also provides information about motor (and system) voltage, current and power consumption.
The GPS (Global Positioning System) provides the system with positioning information. It is actually using the GNSS (Global Navigation Satellite System), i.e. it is using not only the GPS, but also the Galileo, GLONASS, etc. systems.
This table provides a high level overview over what CAN IDs are used by which subsystem:
Range Dec | Range Hex | Function |
---|---|---|
0 - 99 | 0x000 - 0x063 | SYS/PRIO |
100 - 199 | 0x064 - 0x0C7 | SYS/PRIO |
200 - 299 | 0x0C8 - 0x12B | ... |
300 - 399 | 0x12C - 0x18F | FCC |
400 - 499 | 0x190 - 0x1F3 | RAI |
500 - 599 | 0x1F4 - 0x257 | AHRS |
600 - 699 | 0x258 - 0x2BB | ... |
700 - 799 | 0x2BC - 0x31F | AIR |
800 - 899 | 0x320 - 0x383 | GPS |
900 - 999 | 0x384 - 0x3E7 | ... |
1000 - 1099 | 0x3E8 - 0x44B | PSU |
1100 - 1199 | 0x44C - 0x4AF | ... |
1200 - 1299 | 0x4B0 - 0x513 | ... |
1300 - 1399 | 0x514 - 0x577 | ... |
1400 - 1499 | 0x578 - 0x5DB | ... |
1500 - 1599 | 0x5DC - 0x63F | ... |
1600 - 1699 | 0x640 - 0x6A3 | ... |
1700 - 1799 | 0x6A4 - 0x707 | TELE/LOG |
1800 - 1899 | 0x708 - 0x76B | ... |
1900 - 1999 | 0x76C - 0x7CF | LIGHTS |
2000 - 2048 | 0x7D0 - 0x800 | AUX |
For more information about the actual messages send over the CAN protocol have a look at can_ids.h and can_meta.h. There, you will find the actual IDs of the individual CAN messages, as well as meta information, such as the length of the data chunk send (i.e. the number of CAN messages) and more.
x <= 2 && y > 10
is way easier to read than x<=2&&y>10
.if
, switch
, case
, for
, do
, while
.For this, we will follow the good old prophets of C : Kernighan and Ritchie. It also happens to be the rule of the Linux Kernel. The way the prophets taught us is to put the opening brace last on the line and the closing brace first :
if (x == 42) {
// do stuff
}
Note that the closing brace is always on its own line except if it is followed by a continuation of the same statement, for example in a do..while
block or in an if..else
block :
if (x == 42) {
// do stuff
} else if (x == 34) {
// do something else
} else {
// do yet another thingie
}
do {
// do stuff
} while (x < 42);
Function definitions look like this :
void do_something_useful(void) {
// Code goes here
}
Always use braces. This avoids mistakes and improves readability, without costing much of your time. Don't do this :
if (x == 42)
do_stuff();
But this :
if (x == 42) {
do_stuff();
}
i
is ok for a loop counter, but foo
for a function name isn't.static
keyword.#define
s in a .h
file.#define
s in a .c
file with the same name..c
file with the same name. #ifndef
include guards in header files (as seen below).(Only applicable if using C and C++ together.)
To allow for easy inclusion of your headers in C++ files, you should put extern "C"
in your header. If you don't do this, you will get weird link time error because of C++ name mangling.
For example:
#ifndef MYHEADER_H_
#define MYHEADER_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Snip */
#ifdef __cplusplus
}
#endif
#endif
README.md