2 Commits

Author SHA1 Message Date
Oleg Kalachev 04122bcd3f Test failing simulator run 2026-05-27 13:22:35 +03:00
Oleg Kalachev 1f2f623f6a Test running simulation in ci 2026-05-27 12:31:37 +03:00
35 changed files with 114 additions and 239 deletions
+5
View File
@@ -76,6 +76,11 @@ jobs:
run: sudo apt-get install -y libsdl2-dev run: sudo apt-get install -y libsdl2-dev
- name: Build simulator - name: Build simulator
run: make build_simulator run: make build_simulator
- name: Run simulator
env:
GAZEBO_MODEL_PATH: ${{ github.workspace }}/gazebo/models
GAZEBO_PLUGIN_PATH: ${{ github.workspace }}/gazebo/build
run: timeout --preserve-status 120 gzserver --verbose gazebo/flix.world || [ $? -eq 143 ]
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: gazebo-plugin-binary name: gazebo-plugin-binary
+2 -3
View File
@@ -4,10 +4,9 @@ build/
tools/log/ tools/log/
tools/dist/ tools/dist/
*.egg-info/ *.egg-info/
.core .dependencies
.libs
.vscode/* .vscode/*
!.vscode/settings.default.json !.vscode/settings.json
!.vscode/c_cpp_properties.json !.vscode/c_cpp_properties.json
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
+21 -21
View File
@@ -6,18 +6,18 @@
"${workspaceFolder}/flix", "${workspaceFolder}/flix",
"${workspaceFolder}/gazebo", "${workspaceFolder}/gazebo",
"${workspaceFolder}/tools/**", "${workspaceFolder}/tools/**",
"~/.arduino15/packages/esp32/hardware/esp32/3.3.10/cores/esp32", "~/.arduino15/packages/esp32/hardware/esp32/3.3.6/cores/esp32",
"~/.arduino15/packages/esp32/hardware/esp32/3.3.10/libraries/**", "~/.arduino15/packages/esp32/hardware/esp32/3.3.6/libraries/**",
"~/.arduino15/packages/esp32/hardware/esp32/3.3.10/variants/d1_mini32", "~/.arduino15/packages/esp32/hardware/esp32/3.3.6/variants/d1_mini32",
"~/.arduino15/packages/esp32/tools/esp32-libs/3.3.10/include/**", "~/.arduino15/packages/esp32/tools/esp32-libs/3.3.6/include/**",
"~/Arduino/libraries/**", "~/Arduino/libraries/**",
"/usr/include/gazebo-11/", "/usr/include/gazebo-11/",
"/usr/include/ignition/math6/" "/usr/include/ignition/math6/"
], ],
"forcedInclude": [ "forcedInclude": [
"${workspaceFolder}/.vscode/intellisense.h", "${workspaceFolder}/.vscode/intellisense.h",
"~/.arduino15/packages/esp32/hardware/esp32/3.3.10/cores/esp32/Arduino.h", "~/.arduino15/packages/esp32/hardware/esp32/3.3.6/cores/esp32/Arduino.h",
"~/.arduino15/packages/esp32/hardware/esp32/3.3.10/variants/d1_mini32/pins_arduino.h", "~/.arduino15/packages/esp32/hardware/esp32/3.3.6/variants/d1_mini32/pins_arduino.h",
"${workspaceFolder}/flix/cli.ino", "${workspaceFolder}/flix/cli.ino",
"${workspaceFolder}/flix/control.ino", "${workspaceFolder}/flix/control.ino",
"${workspaceFolder}/flix/estimate.ino", "${workspaceFolder}/flix/estimate.ino",
@@ -33,7 +33,7 @@
"${workspaceFolder}/flix/parameters.ino", "${workspaceFolder}/flix/parameters.ino",
"${workspaceFolder}/flix/safety.ino" "${workspaceFolder}/flix/safety.ino"
], ],
"compilerPath": "~/.arduino15/packages/esp32/tools/esp-x32/2601/bin/xtensa-esp32-elf-g++", "compilerPath": "~/.arduino15/packages/esp32/tools/esp-x32/2511/bin/xtensa-esp32-elf-g++",
"cStandard": "c11", "cStandard": "c11",
"cppStandard": "c++17", "cppStandard": "c++17",
"defines": [ "defines": [
@@ -53,18 +53,18 @@
"name": "Mac", "name": "Mac",
"includePath": [ "includePath": [
"${workspaceFolder}/flix", "${workspaceFolder}/flix",
"~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.10/cores/esp32", "~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.6/cores/esp32",
"~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.10/libraries/**", "~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.6/libraries/**",
"~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.10/variants/d1_mini32", "~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.6/variants/d1_mini32",
"~/Library/Arduino15/packages/esp32/tools/esp32-libs/3.3.10/include/**", "~/Library/Arduino15/packages/esp32/tools/esp32-libs/3.3.6/include/**",
"~/Documents/Arduino/libraries/**", "~/Documents/Arduino/libraries/**",
"/opt/homebrew/include/gazebo-11/", "/opt/homebrew/include/gazebo-11/",
"/opt/homebrew/include/ignition/math6/" "/opt/homebrew/include/ignition/math6/"
], ],
"forcedInclude": [ "forcedInclude": [
"${workspaceFolder}/.vscode/intellisense.h", "${workspaceFolder}/.vscode/intellisense.h",
"~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.10/cores/esp32/Arduino.h", "~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.6/cores/esp32/Arduino.h",
"~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.10/variants/d1_mini32/pins_arduino.h", "~/Library/Arduino15/packages/esp32/hardware/esp32/3.3.6/variants/d1_mini32/pins_arduino.h",
"${workspaceFolder}/flix/flix.ino", "${workspaceFolder}/flix/flix.ino",
"${workspaceFolder}/flix/cli.ino", "${workspaceFolder}/flix/cli.ino",
"${workspaceFolder}/flix/control.ino", "${workspaceFolder}/flix/control.ino",
@@ -80,7 +80,7 @@
"${workspaceFolder}/flix/parameters.ino", "${workspaceFolder}/flix/parameters.ino",
"${workspaceFolder}/flix/safety.ino" "${workspaceFolder}/flix/safety.ino"
], ],
"compilerPath": "~/Library/Arduino15/packages/esp32/tools/esp-x32/2601/bin/xtensa-esp32-elf-g++", "compilerPath": "~/Library/Arduino15/packages/esp32/tools/esp-x32/2511/bin/xtensa-esp32-elf-g++",
"cStandard": "c11", "cStandard": "c11",
"cppStandard": "c++17", "cppStandard": "c++17",
"defines": [ "defines": [
@@ -103,16 +103,16 @@
"${workspaceFolder}/flix", "${workspaceFolder}/flix",
"${workspaceFolder}/gazebo", "${workspaceFolder}/gazebo",
"${workspaceFolder}/tools/**", "${workspaceFolder}/tools/**",
"~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.10/cores/esp32", "~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.6/cores/esp32",
"~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.10/libraries/**", "~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.6/libraries/**",
"~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.10/variants/d1_mini32", "~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.6/variants/d1_mini32",
"~/AppData/Local/Arduino15/packages/esp32/tools/esp32-libs/3.3.10/include/**", "~/AppData/Local/Arduino15/packages/esp32/tools/esp32-libs/3.3.6/include/**",
"~/Documents/Arduino/libraries/**" "~/Documents/Arduino/libraries/**"
], ],
"forcedInclude": [ "forcedInclude": [
"${workspaceFolder}/.vscode/intellisense.h", "${workspaceFolder}/.vscode/intellisense.h",
"~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.10/cores/esp32/Arduino.h", "~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.6/cores/esp32/Arduino.h",
"~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.10/variants/d1_mini32/pins_arduino.h", "~/AppData/Local/Arduino15/packages/esp32/hardware/esp32/3.3.6/variants/d1_mini32/pins_arduino.h",
"${workspaceFolder}/flix/cli.ino", "${workspaceFolder}/flix/cli.ino",
"${workspaceFolder}/flix/control.ino", "${workspaceFolder}/flix/control.ino",
"${workspaceFolder}/flix/estimate.ino", "${workspaceFolder}/flix/estimate.ino",
@@ -128,7 +128,7 @@
"${workspaceFolder}/flix/parameters.ino", "${workspaceFolder}/flix/parameters.ino",
"${workspaceFolder}/flix/safety.ino" "${workspaceFolder}/flix/safety.ino"
], ],
"compilerPath": "~/AppData/Local/Arduino15/packages/esp32/tools/esp-x32/2601/bin/xtensa-esp32-elf-g++.exe", "compilerPath": "~/AppData/Local/Arduino15/packages/esp32/tools/esp-x32/2511/bin/xtensa-esp32-elf-g++.exe",
"cStandard": "c11", "cStandard": "c11",
"cppStandard": "c++17", "cppStandard": "c++17",
"defines": [ "defines": [
-1
View File
@@ -1,7 +1,6 @@
{ {
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
"recommendations": [ "recommendations": [
"dangmai.workspace-default-settings",
"ms-vscode.cpptools", "ms-vscode.cpptools",
"ms-vscode.cmake-tools", "ms-vscode.cmake-tools",
"ms-python.python" "ms-python.python"
+10 -18
View File
@@ -1,32 +1,24 @@
BOARD = esp32:esp32:d1_mini32:DebugLevel=error BOARD = esp32:esp32:d1_mini32
PORT := $(strip $(wildcard /dev/serial/by-id/usb-Silicon_Labs_CP21* /dev/serial/by-id/usb-1a86_USB_Single_Serial_* /dev/cu.usbserial-* /dev/cu.usbmodem*)) PORT := $(strip $(wildcard /dev/serial/by-id/usb-Silicon_Labs_CP21* /dev/serial/by-id/usb-1a86_USB_Single_Serial_* /dev/cu.usbserial-* /dev/cu.usbmodem*))
export ARDUINO_NETWORK_CONNECTION_TIMEOUT := 1h build: .dependencies
build: .core .libs
arduino-cli compile --fqbn $(BOARD) flix arduino-cli compile --fqbn $(BOARD) flix
upload: build upload: build
arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" flix arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" flix
erase:
arduino-cli burn-bootloader --fqbn $(BOARD) -p "$(PORT)" -P esptool
monitor: monitor:
arduino-cli monitor -p "$(PORT)" -c baudrate=115200 arduino-cli monitor -p "$(PORT)" -c baudrate=115200
core .core: dependencies .dependencies:
arduino-cli core update-index --additional-urls https://espressif.github.io/arduino-esp32/package_esp32_index.json arduino-cli core update-index --config-file arduino-cli.yaml
arduino-cli core install esp32:esp32@3.3.10 --additional-urls https://espressif.github.io/arduino-esp32/package_esp32_index.json arduino-cli core install esp32:esp32@3.3.6 --config-file arduino-cli.yaml
touch .core
libs .libs:
arduino-cli lib update-index arduino-cli lib update-index
arduino-cli lib install "FlixPeriph" arduino-cli lib install "FlixPeriph"
arduino-cli lib install "MAVLink"@2.0.25 arduino-cli lib install "MAVLink"@2.0.25
touch .libs touch .dependencies
upload_proxy: .core .libs upload_proxy: .dependencies
arduino-cli compile --fqbn $(BOARD) tools/espnow-proxy arduino-cli compile --fqbn $(BOARD) tools/espnow-proxy
arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" tools/espnow-proxy arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" tools/espnow-proxy
@@ -34,7 +26,7 @@ gazebo/build cmake: gazebo/CMakeLists.txt
mkdir -p gazebo/build mkdir -p gazebo/build
cd gazebo/build && cmake .. cd gazebo/build && cmake ..
build_simulator: .libs gazebo/build build_simulator: .dependencies gazebo/build
make -C gazebo/build make -C gazebo/build
simulator: build_simulator simulator: build_simulator
@@ -49,6 +41,6 @@ plot:
plotjuggler -d $(shell ls -t tools/log/*.csv | head -n1) plotjuggler -d $(shell ls -t tools/log/*.csv | head -n1)
clean: clean:
rm -rf gazebo/build flix/build flix/cache .core .libs rm -rf gazebo/build flix/build flix/cache .dependencies
.PHONY: build upload monitor core libs cmake build_simulator simulator log clean .PHONY: build upload monitor dependencies cmake build_simulator simulator log clean
+5
View File
@@ -0,0 +1,5 @@
board_manager:
additional_urls:
- https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
network:
connection_timeout: 1h
-3
View File
@@ -79,9 +79,6 @@ To add a new parameter:
See examples of adding new parameters in commits: [c434107](https://github.com/okalachev/flix/commit/c434107), [a687303](https://github.com/okalachev/flix/commit/a687303). See examples of adding new parameters in commits: [c434107](https://github.com/okalachev/flix/commit/c434107), [a687303](https://github.com/okalachev/flix/commit/a687303).
> [!NOTE]
> Since all the parameters are internally stored and passed as floats, the safe range for `int` parameters is -16777216 to 16777215.
## Adding a subsystem ## Adding a subsystem
To add a new subsystem: To add a new subsystem:
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

+2 -12
View File
@@ -20,14 +20,13 @@ You can build and upload the firmware using either **Arduino IDE** (easier for b
1. Install [Arduino IDE](https://www.arduino.cc/en/software) (version 2 is recommended). 1. Install [Arduino IDE](https://www.arduino.cc/en/software) (version 2 is recommended).
2. *Windows users might need to install [USB to UART bridge driver from Silicon Labs](https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers).* 2. *Windows users might need to install [USB to UART bridge driver from Silicon Labs](https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers).*
3. Install ESP32 core, version 3.3.10. See the [official Espressif's instructions](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html#installing-using-arduino-ide) on installing ESP32 Core in Arduino IDE. 3. Install ESP32 core, version 3.3.6. See the [official Espressif's instructions](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html#installing-using-arduino-ide) on installing ESP32 Core in Arduino IDE.
4. Install the following libraries using [Library Manager](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-installing-a-library): 4. Install the following libraries using [Library Manager](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-installing-a-library):
* `FlixPeriph`, the latest version. * `FlixPeriph`, the latest version.
* `MAVLink`, version 2.0.25. * `MAVLink`, version 2.0.25.
5. Open the `flix/flix.ino` sketch from downloaded firmware sources in Arduino IDE. 5. Open the `flix/flix.ino` sketch from downloaded firmware sources in Arduino IDE.
6. Connect your ESP32 board to the computer and choose correct board type in Arduino IDE (*WEMOS D1 MINI ESP32* for ESP32 Mini) and the port. 6. Connect your ESP32 board to the computer and choose correct board type in Arduino IDE (*WEMOS D1 MINI ESP32* for ESP32 Mini) and the port.
7. Set *Tools**Core Debug Level* to *Error* to see the errors in the serial console. Set *Tools**USB CDC on Boot* to *Enabled* for ESP32-S3/ESP32-C3 boards. 7. [Build and upload](https://docs.arduino.cc/software/ide-v2/tutorials/getting-started/ide-v2-uploading-a-sketch) the firmware using Arduino IDE.
8. [Build and upload](https://docs.arduino.cc/software/ide-v2/tutorials/getting-started/ide-v2-uploading-a-sketch) the firmware using Arduino IDE.
### Command line (Windows, Linux, macOS) ### Command line (Windows, Linux, macOS)
@@ -58,12 +57,6 @@ You can build and upload the firmware using either **Arduino IDE** (easier for b
make upload monitor make upload monitor
``` ```
For ESP32-S3/ESP32-C3 boards, set the appropriate [FQBN](https://docs.arduino.cc/arduino-cli/FAQ/#whats-the-fqbn-string) using `BOARD` parameter:
```bash
make BOARD=esp32:esp32:esp32s3:DebugLevel=error,FlashSize=4M,CDCOnBoot=cdc upload
```
See other available Make commands in [Makefile](../Makefile). See other available Make commands in [Makefile](../Makefile).
> [!TIP] > [!TIP]
@@ -89,9 +82,6 @@ QGroundControl is a ground control station software that can be used to monitor
3. Connect your computer or smartphone to the appeared `flix` Wi-Fi network (password: `flixwifi`). 3. Connect your computer or smartphone to the appeared `flix` Wi-Fi network (password: `flixwifi`).
4. Launch QGroundControl app. It should connect and begin showing the drone's telemetry automatically. 4. Launch QGroundControl app. It should connect and begin showing the drone's telemetry automatically.
> [!TIP]
> If QGroundControl doesn't connect, try to disable the firewall and/or VPN on your computer, as they may block the connection.
### Access console ### Access console
The console is a command line interface (CLI) that allows to interact with the drone, change parameters, and perform various actions. There are two ways of accessing the console: using **serial port** or using **QGroundControl (wirelessly)**. The console is a command line interface (CLI) that allows to interact with the drone, change parameters, and perform various actions. There are two ways of accessing the console: using **serial port** or using **QGroundControl (wirelessly)**.
-41
View File
@@ -4,36 +4,6 @@ This page contains user-built drones based on the Flix project. Publish your pro
--- ---
Author: [Неруш Михаил](https://t.me/NerushMV).<br>
Description: custom frame made of 4 mm plywood, 8520 brushed motors, 75 mm propellers, MPU-6500. FlySky FS-i6X with ESP32-based adapter for ESP-NOW communication (using PPM output).
<img src="img/user/nerush/1.jpg" height=200> <img src="img/user/nerush/2.jpg" height=200>
[Flight video](https://drive.google.com/file/d/1jRXeGx34lJpUfw0GKLQeIzkWZvooQJSE/view?usp=sharing).
---
Author: [Konstantinos Paraskevas](https://github.com/Frapais).<br>
Description: drone with a custom single-boarded airframe, extending the [Sprig-C3 module](https://github.com/Frapais/Sprig-C3).
ESP32-C3 microcontroller, ICM-20948 IMU, on-board fuel-gauge, status LED indicator.<br>
Repository with all the code and PCB sources: https://github.com/Frapais/Sprig-Drone.
<img src="img/user/kostas/1.jpg" height=150> <img src="img/user/kostas/2.jpg" height=150>
Detailed video about making the drone:
<a href="https://youtu.be/82Q-uBq6s48"><img width=400 src="https://i3.ytimg.com/vi/82Q-uBq6s48/maxresdefault.jpg"></a>
---
Author: [Awab Anas](http://t.me/AW_VENOM).<br>
Description: ESP32 D1 Mini, MPU-6050, 8520 3.7V brushed motors, 55 mm propellers, battery li-po 1200 mAh, controlling via [Mavlink Joystick app](https://github.com/goldarte/mavlink-joystick/releases/latest).<br>
[Flight validation](https://drive.google.com/file/d/12z0jfctZDBA6b5UKCG0Uje5rAxj6DhF-/view?usp=sharing).
<img src="img/user/aw_venom/1.jpg" height=200>
---
Author: [Ina Tix](https://t.me/ina_tix).<br> Author: [Ina Tix](https://t.me/ina_tix).<br>
Description: XR2981 based DC-DC converter, ELRS MINI 2.4GHz RX SX1280 receiver (SBUS interface), Radiomaster TX12 remote control.<br> Description: XR2981 based DC-DC converter, ELRS MINI 2.4GHz RX SX1280 receiver (SBUS interface), Radiomaster TX12 remote control.<br>
[Flight validation](https://drive.google.com/file/d/1yqkKNuz4R_yxGqUNQxVpixJbXqEEcUSj/view?usp=share_link). [Flight validation](https://drive.google.com/file/d/1yqkKNuz4R_yxGqUNQxVpixJbXqEEcUSj/view?usp=share_link).
@@ -87,17 +57,6 @@ Author: [goldarte](https://t.me/goldarte).<br>
--- ---
Author: [malagis](https://oshwhub.com/malagis).<br>
A Chinese custom PCB version of Flix with a big community of users, lots of materials and modifications.
Main project's page: https://oshwhub.com/malagis/esp32-mini-plane.<br>
Video about the project: https://www.bilibili.com/video/BV14vyqBFEJn/.
<img src="img/user/malagis/1.jpg" height=200> <img src="img/user/malagis/2.jpg" height=200> <img src="img/user/malagis/3.jpg" height=200>
---
## School 548 course ## School 548 course
Special course on quadcopter design and engineering took place in october-november 2025 in School 548, Moscow. The course included UAV control theory, electronics, drone assembly and setup practice, using the Flix project. Special course on quadcopter design and engineering took place in october-november 2025 in School 548, Moscow. The course included UAV control theory, electronics, drone assembly and setup practice, using the Flix project.
+22 -23
View File
@@ -31,33 +31,33 @@ const char* motd =
"Commands:\n\n" "Commands:\n\n"
"help - show help\n" "help - show help\n"
"p - show all parameters\n" "p - show all parameters\n"
"p <str> - show parameters starting with str\n" "p <name> - show parameter\n"
"p <name> <value> - set parameter\n" "p <name> <value> - set parameter\n"
"preset - reset parameters\n" "preset - reset parameters\n"
"time - show time info\n" "time - show time info\n"
"ps - show pitch/roll/yaw\n"
"psq - show attitude quaternion\n"
"imu - show IMU data\n" "imu - show IMU data\n"
"ca - calibrate accel\n"
"st - show state estimation\n"
"arm - arm the drone\n" "arm - arm the drone\n"
"disarm - disarm the drone\n" "disarm - disarm the drone\n"
"raw/stab/acro/auto - set mode\n" "raw/stab/acro/auto - set mode\n"
"rc - show RC data\n" "rc - show RC data\n"
"cr - calibrate RC\n"
"pw - show power info\n" "pw - show power info\n"
"wifi - show Wi-Fi info\n" "wifi - show Wi-Fi info\n"
"wifi ap/sta/espnow/off - set Wi-Fi mode\n" "ap <ssid> <password> - setup Wi-Fi access point\n"
"ap <ssid> <password> - configure Wi-Fi access point\n" "sta <ssid> <password> - setup Wi-Fi client mode\n"
"sta <ssid> <password> - configure Wi-Fi client mode\n" "espnow <mac> [<key>] - setup ESP-NOW peer\n"
"espnow <mac> [<key>] - configure ESP-NOW peer\n"
"mot - show motor output\n" "mot - show motor output\n"
"log [dump] - print log header [and data]\n" "log [dump] - print log header [and data]\n"
"cr - calibrate RC\n"
"ca - calibrate accel\n"
"mfr, mfl, mrr, mrl - test motor (remove props)\n" "mfr, mfl, mrr, mrl - test motor (remove props)\n"
"sys - show system info\n" "sys - show system info\n"
"reset - reset drone's state\n" "reset - reset drone's state\n"
"reboot - reboot the drone\n"; "reboot - reboot the drone\n";
void print(const char* format, ...) { void print(const char* format, ...) {
char buf[3000]; char buf[1000];
va_list args; va_list args;
va_start(args, format); va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args); vsnprintf(buf, sizeof(buf), format, args);
@@ -92,8 +92,10 @@ void doCommand(String str, bool echo = false) {
// execute command // execute command
if (command == "help" || command == "motd") { if (command == "help" || command == "motd") {
print("%s\n", motd); print("%s\n", motd);
} else if (command == "p" && arg1 == "") { } else if (command == "p" && arg0 == "") {
printParameters(arg0.c_str()); printParameters();
} else if (command == "p" && arg0 != "" && arg1 == "") {
print("%s = %g\n", arg0.c_str(), getParameter(arg0.c_str()));
} else if (command == "p") { } else if (command == "p") {
bool success = setParameter(arg0.c_str(), arg1.toFloat()); bool success = setParameter(arg0.c_str(), arg1.toFloat());
if (success) { if (success) {
@@ -107,15 +109,15 @@ void doCommand(String str, bool echo = false) {
print("Time: %f\n", t); print("Time: %f\n", t);
print("Loop rate: %.0f\n", loopRate); print("Loop rate: %.0f\n", loopRate);
print("dt: %f\n", dt); print("dt: %f\n", dt);
} else if (command == "ps") {
Vector a = attitude.toEuler();
print("roll: %f pitch: %f yaw: %f\n", degrees(a.x), degrees(a.y), degrees(a.z));
} else if (command == "psq") {
print("qw: %f qx: %f qy: %f qz: %f\n", attitude.w, attitude.x, attitude.y, attitude.z);
} else if (command == "imu") { } else if (command == "imu") {
printIMUInfo(); printIMUInfo();
printIMUCalibration(); printIMUCalibration();
print("landed: %d\n", landed); print("landed: %d\n", landed);
} else if (command == "st") {
print("rates: %g %g %g\n", rates.x, rates.y, rates.z);
print("attitude: %g %g %g %g\n", attitude.w, attitude.x, attitude.y, attitude.z);
print("roll: %g° pitch: %g° yaw: %g°\n", degrees(attitude.getRoll()), degrees(attitude.getPitch()), degrees(attitude.getYaw()));
print("landed: %d\n", landed);
} else if (command == "arm") { } else if (command == "arm") {
armed = true; armed = true;
} else if (command == "disarm") { } else if (command == "disarm") {
@@ -140,10 +142,8 @@ void doCommand(String str, bool echo = false) {
print("armed: %d\n", armed); print("armed: %d\n", armed);
} else if (command == "pw") { } else if (command == "pw") {
print("Voltage: %.1f V\n", voltage); print("Voltage: %.1f V\n", voltage);
} else if (command == "wifi" && arg0 == "") {
printWiFiInfo();
} else if (command == "wifi") { } else if (command == "wifi") {
setWiFiMode(arg0); printWiFiInfo();
} else if (command == "ap") { } else if (command == "ap") {
configWiFi(W_AP, arg0.c_str(), arg1.c_str()); configWiFi(W_AP, arg0.c_str(), arg1.c_str());
} else if (command == "sta") { } else if (command == "sta") {
@@ -172,11 +172,10 @@ void doCommand(String str, bool echo = false) {
#ifdef ESP32 #ifdef ESP32
print("Chip: %s\n", ESP.getChipModel()); print("Chip: %s\n", ESP.getChipModel());
print("Temperature: %.1f °C\n", temperatureRead()); print("Temperature: %.1f °C\n", temperatureRead());
print("Total RAM: %d KB\n", ESP.getHeapSize() / 1024); print("Free heap: %d\n", ESP.getFreeHeap());
print("Free heap: %d KB\n", ESP.getFreeHeap() / 1024);
print("Firmware: " __DATE__ " " __TIME__ "\n"); print("Firmware: " __DATE__ " " __TIME__ "\n");
// Print tasks table // Print tasks table
print("Num Task MinSt Prio Core CPU%%\n"); print("Num Task Stack Prio Core CPU%%\n");
int taskCount = uxTaskGetNumberOfTasks(); int taskCount = uxTaskGetNumberOfTasks();
TaskStatus_t *systemState = new TaskStatus_t[taskCount]; TaskStatus_t *systemState = new TaskStatus_t[taskCount];
uint32_t totalRunTime; uint32_t totalRunTime;
@@ -210,7 +209,7 @@ void handleInput() {
while (Serial.available()) { while (Serial.available()) {
char c = Serial.read(); char c = Serial.read();
if (c == '\n' || c == '\r') { if (c == '\n') {
doCommand(input); doCommand(input);
input.clear(); input.clear();
} else { } else {
-1
View File
@@ -149,7 +149,6 @@ void controlTorque() {
motors[MOTOR_REAR_LEFT] = thrustTarget + torqueTarget.x + torqueTarget.y - torqueTarget.z; motors[MOTOR_REAR_LEFT] = thrustTarget + torqueTarget.x + torqueTarget.y - torqueTarget.z;
motors[MOTOR_REAR_RIGHT] = thrustTarget - torqueTarget.x + torqueTarget.y + torqueTarget.z; motors[MOTOR_REAR_RIGHT] = thrustTarget - torqueTarget.x + torqueTarget.y + torqueTarget.z;
// Prioritize angle control over thrust control
desaturate(motors[MOTOR_FRONT_LEFT], motors[MOTOR_FRONT_RIGHT], motors[MOTOR_REAR_LEFT], motors[MOTOR_REAR_RIGHT]); desaturate(motors[MOTOR_FRONT_LEFT], motors[MOTOR_FRONT_RIGHT], motors[MOTOR_REAR_LEFT], motors[MOTOR_REAR_RIGHT]);
motors[0] = constrain(motors[0], 0, 1); motors[0] = constrain(motors[0], 0, 1);
+2 -2
View File
@@ -32,7 +32,8 @@ void applyGyro() {
void applyAcc() { void applyAcc() {
// test should we apply accelerometer gravity correction // test should we apply accelerometer gravity correction
landed = !motorsActive() && abs(acc.norm() - ONE_G) < ONE_G * 0.1f; float accNorm = acc.norm();
landed = !motorsActive() && abs(accNorm - ONE_G) < ONE_G * 0.1f;
if (!landed) return; if (!landed) return;
@@ -46,7 +47,6 @@ void applyAcc() {
void applyLevel() { void applyLevel() {
if (landed) return; if (landed) return;
if (thrustTarget < 0.1) return; // skip at idle thrust
// assume the pilot keeps the drone more or less level in flight // assume the pilot keeps the drone more or less level in flight
Vector up = Quaternion::rotateVector(Vector(0, 0, 1), attitude); Vector up = Quaternion::rotateVector(Vector(0, 0, 1), attitude);
+2 -7
View File
@@ -40,12 +40,10 @@ void readIMU() {
imu.getGyro(gyro.x, gyro.y, gyro.z); imu.getGyro(gyro.x, gyro.y, gyro.z);
imu.getAccel(acc.x, acc.y, acc.z); imu.getAccel(acc.x, acc.y, acc.z);
calibrateGyroOnce(); calibrateGyroOnce();
// apply scale and bias
// Apply scale and bias
acc = (acc - accBias) / accScale; acc = (acc - accBias) / accScale;
gyro = gyro - gyroBias; gyro = gyro - gyroBias;
// rotate to body frame
// Rotate to body frame
Quaternion rotation = Quaternion::fromEuler(imuRotation); Quaternion rotation = Quaternion::fromEuler(imuRotation);
acc = Quaternion::rotateVector(acc, rotation.inversed()); acc = Quaternion::rotateVector(acc, rotation.inversed());
gyro = Quaternion::rotateVector(gyro, rotation.inversed()); gyro = Quaternion::rotateVector(gyro, rotation.inversed());
@@ -54,7 +52,6 @@ void readIMU() {
void calibrateGyroOnce() { void calibrateGyroOnce() {
static Delay landedDelay(2); static Delay landedDelay(2);
if (!landedDelay.update(landed)) return; // calibrate only if definitely stationary if (!landedDelay.update(landed)) return; // calibrate only if definitely stationary
gyroBias = gyroBiasFilter.update(gyro); gyroBias = gyroBiasFilter.update(gyro);
} }
@@ -108,7 +105,6 @@ void calibrateAccelOnce() {
if (acc.x < accMin.x) accMin.x = acc.x; if (acc.x < accMin.x) accMin.x = acc.x;
if (acc.y < accMin.y) accMin.y = acc.y; if (acc.y < accMin.y) accMin.y = acc.y;
if (acc.z < accMin.z) accMin.z = acc.z; if (acc.z < accMin.z) accMin.z = acc.z;
// Compute scale and bias // Compute scale and bias
accScale = (accMax - accMin) / 2 / ONE_G; accScale = (accMax - accMin) / 2 / ONE_G;
accBias = (accMax + accMin) / 2; accBias = (accMax + accMin) / 2;
@@ -125,7 +121,6 @@ void printIMUInfo() {
print("model: %s\n", imu.getModel()); print("model: %s\n", imu.getModel());
print("who am I: 0x%02X\n", imu.whoAmI()); print("who am I: 0x%02X\n", imu.whoAmI());
print("rate: %.0f\n", loopRate); print("rate: %.0f\n", loopRate);
print("temperature: %.1f °C\n", imu.getTemp());
print("gyro: %f %f %f\n", gyro.x, gyro.y, gyro.z); print("gyro: %f %f %f\n", gyro.x, gyro.y, gyro.z);
print("acc: %f %f %f\n", acc.x, acc.y, acc.z); print("acc: %f %f %f\n", acc.x, acc.y, acc.z);
imu.waitForData(); imu.waitForData();
+5 -19
View File
@@ -10,12 +10,8 @@ extern float controlTime;
extern float voltage; extern float voltage;
int mavlinkSysId = 1; int mavlinkSysId = 1;
Rate telemetryFast(10);
Rate telemetrySlow(2); Rate telemetrySlow(2);
Rate telemetryAttitude(20);
Rate telemetryRC(10);
Rate telemetryMotors(10);
Rate telemetryIMU(15);
bool mavlinkConnected = false; bool mavlinkConnected = false;
String mavlinkPrintBuffer; String mavlinkPrintBuffer;
@@ -38,46 +34,36 @@ void sendMavlink() {
((mode == AUTO) ? MAV_MODE_FLAG_AUTO_ENABLED : MAV_MODE_FLAG_MANUAL_INPUT_ENABLED), ((mode == AUTO) ? MAV_MODE_FLAG_AUTO_ENABLED : MAV_MODE_FLAG_MANUAL_INPUT_ENABLED),
mode, MAV_STATE_STANDBY); mode, MAV_STATE_STANDBY);
sendMessage(&msg); sendMessage(&msg);
}
if (!mavlinkConnected) return; // send only heartbeat until connected if (!mavlinkConnected) return; // send only heartbeat until connected
if (telemetrySlow) {
mavlink_msg_extended_sys_state_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, mavlink_msg_extended_sys_state_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg,
MAV_VTOL_STATE_UNDEFINED, landed ? MAV_LANDED_STATE_ON_GROUND : MAV_LANDED_STATE_IN_AIR); MAV_VTOL_STATE_UNDEFINED, landed ? MAV_LANDED_STATE_ON_GROUND : MAV_LANDED_STATE_IN_AIR);
sendMessage(&msg); sendMessage(&msg);
}
if (telemetrySlow && valid(voltage)) {
uint16_t voltages[] = {(uint16_t)(voltage * 1000), UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; uint16_t voltages[] = {(uint16_t)(voltage * 1000), UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
uint16_t voltagesExt[] = {0, 0, 0, 0}; uint16_t voltagesExt[] = {0, 0, 0, 0};
float remaining = constrain(mapf(voltage, 3.4, 4.2, 0, 1), 0, 1); float remaining = constrain(mapf(voltage, 3.4, 4.2, 0, 1), 0, 1);
mavlink_msg_battery_status_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, 0, MAV_BATTERY_FUNCTION_ALL, mavlink_msg_battery_status_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, 0, MAV_BATTERY_FUNCTION_ALL,
MAV_BATTERY_TYPE_LIPO, INT16_MAX, voltages, -1, -1, -1, remaining * 100, 0, MAV_BATTERY_CHARGE_STATE_OK, voltagesExt, 0, 0); MAV_BATTERY_TYPE_LIPO, INT16_MAX, voltages, -1, -1, -1, remaining * 100, 0, MAV_BATTERY_CHARGE_STATE_OK, voltagesExt, 0, 0);
sendMessage(&msg); if (valid(voltage)) sendMessage(&msg);
} }
if (telemetryAttitude) { if (telemetryFast && mavlinkConnected) {
const float offset[] = {0, 0, 0, 0}; const float offset[] = {0, 0, 0, 0};
mavlink_msg_attitude_quaternion_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, mavlink_msg_attitude_quaternion_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg,
time, attitude.w, attitude.x, -attitude.y, -attitude.z, rates.x, -rates.y, -rates.z, offset); // convert to frd time, attitude.w, attitude.x, -attitude.y, -attitude.z, rates.x, -rates.y, -rates.z, offset); // convert to frd
sendMessage(&msg); sendMessage(&msg);
}
if (telemetryRC && channels[0]) { // 0 means no RC input
mavlink_msg_rc_channels_raw_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, controlTime * 1000, 0, mavlink_msg_rc_channels_raw_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, controlTime * 1000, 0,
channels[0], channels[1], channels[2], channels[3], channels[4], channels[5], channels[6], channels[7], UINT8_MAX); channels[0], channels[1], channels[2], channels[3], channels[4], channels[5], channels[6], channels[7], UINT8_MAX);
sendMessage(&msg); if (channels[0] != 0) sendMessage(&msg); // 0 means no RC input
}
if (telemetryMotors) {
float controls[8]; float controls[8];
memcpy(controls, motors, sizeof(motors)); memcpy(controls, motors, sizeof(motors));
mavlink_msg_actuator_control_target_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, time, 0, controls); mavlink_msg_actuator_control_target_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, time, 0, controls);
sendMessage(&msg); sendMessage(&msg);
}
if (telemetryIMU) {
mavlink_msg_scaled_imu_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, time, mavlink_msg_scaled_imu_pack(mavlinkSysId, MAV_COMP_ID_AUTOPILOT1, &msg, time,
acc.x / ONE_G * 1000, -acc.y / ONE_G * 1000, -acc.z / ONE_G * 1000, // convert to frd acc.x / ONE_G * 1000, -acc.y / ONE_G * 1000, -acc.z / ONE_G * 1000, // convert to frd
gyro.x * 1000, -gyro.y * 1000, -gyro.z * 1000, gyro.x * 1000, -gyro.y * 1000, -gyro.z * 1000,
@@ -95,13 +81,13 @@ void sendMessage(const void *msg) {
void receiveMavlink() { void receiveMavlink() {
uint8_t buf[MAVLINK_MAX_PACKET_LEN]; uint8_t buf[MAVLINK_MAX_PACKET_LEN];
int len = receiveWiFi(buf, MAVLINK_MAX_PACKET_LEN); int len = receiveWiFi(buf, MAVLINK_MAX_PACKET_LEN);
if (len) mavlinkConnected = true;
// New packet, parse it // New packet, parse it
mavlink_message_t msg; mavlink_message_t msg;
mavlink_status_t status; mavlink_status_t status;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (mavlink_parse_char(MAVLINK_COMM_0, buf[i], &msg, &status)) { if (mavlink_parse_char(MAVLINK_COMM_0, buf[i], &msg, &status)) {
mavlinkConnected = true;
handleMavlink(&msg); handleMavlink(&msg);
} }
} }
+1 -4
View File
@@ -18,9 +18,8 @@ const int MOTOR_REAR_LEFT = 0, MOTOR_REAR_RIGHT = 1, MOTOR_FRONT_RIGHT = 2, MOTO
void setupMotors() { void setupMotors() {
print("Setup Motors\n"); print("Setup Motors\n");
// Configure pins // configure pins
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (motorPins[i] < 0) continue; // skip unassigned motors
ledcAttach(motorPins[i], pwmFrequency, pwmResolution); ledcAttach(motorPins[i], pwmFrequency, pwmResolution);
pwmFrequency = ledcChangeFrequency(motorPins[i], pwmFrequency, pwmResolution); // when reconfiguring pwmFrequency = ledcChangeFrequency(motorPins[i], pwmFrequency, pwmResolution); // when reconfiguring
} }
@@ -30,14 +29,12 @@ void setupMotors() {
void sendMotors() { void sendMotors() {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (motorPins[i] < 0) continue; // skip unassigned motors
ledcWrite(motorPins[i], getDutyCycle(motors[i])); ledcWrite(motorPins[i], getDutyCycle(motors[i]));
} }
} }
int getDutyCycle(float value) { int getDutyCycle(float value) {
value = constrain(value, 0, 1); value = constrain(value, 0, 1);
if (pwmMax >= 0) { // pwm mode if (pwmMax >= 0) { // pwm mode
float pwm = mapf(value, 0, 1, pwmMin, pwmMax); float pwm = mapf(value, 0, 1, pwmMin, pwmMax);
if (value == 0) pwm = pwmStop; if (value == 0) pwm = pwmStop;
+5 -16
View File
@@ -20,7 +20,6 @@ struct Parameter {
const char *name; // max length is 15 const char *name; // max length is 15
bool integer; bool integer;
union { float *f; int *i; }; // pointer to the variable union { float *f; int *i; }; // pointer to the variable
float inital; // default value
float cache; // what's stored in flash float cache; // what's stored in flash
void (*callback)(); // called after parameter change void (*callback)(); // called after parameter change
Parameter(const char *name, float *variable, void (*callback)() = nullptr) : name(name), integer(false), f(variable), callback(callback) {}; Parameter(const char *name, float *variable, void (*callback)() = nullptr) : name(name), integer(false), f(variable), callback(callback) {};
@@ -117,10 +116,7 @@ Parameter parameters[] = {
// mavlink // mavlink
{"MAV_SYS_ID", &mavlinkSysId}, {"MAV_SYS_ID", &mavlinkSysId},
{"MAV_RATE_SLOW", &telemetrySlow.rate}, {"MAV_RATE_SLOW", &telemetrySlow.rate},
{"MAV_RATE_ATT", &telemetryAttitude.rate}, {"MAV_RATE_FAST", &telemetryFast.rate},
{"MAV_RATE_RC", &telemetryRC.rate},
{"MAV_RATE_MOT", &telemetryMotors.rate},
{"MAV_RATE_IMU", &telemetryIMU.rate},
// power // power
{"PWR_VOLT_PIN", &voltagePin, setupPower}, {"PWR_VOLT_PIN", &voltagePin, setupPower},
{"PWR_VOLT_SCALE", &voltageScale}, {"PWR_VOLT_SCALE", &voltageScale},
@@ -138,7 +134,6 @@ void setupParameters() {
if (!storage.isKey(parameter.name)) { if (!storage.isKey(parameter.name)) {
storage.putFloat(parameter.name, parameter.getValue()); // store default value storage.putFloat(parameter.name, parameter.getValue()); // store default value
} }
parameter.inital = parameter.getValue();
parameter.setValue(storage.getFloat(parameter.name, 0)); parameter.setValue(storage.getFloat(parameter.name, 0));
parameter.cache = parameter.getValue(); parameter.cache = parameter.getValue();
} }
@@ -185,23 +180,17 @@ void syncParameters() {
if (motorsActive()) return; // don't use flash while flying, it may cause a delay if (motorsActive()) return; // don't use flash while flying, it may cause a delay
for (auto &parameter : parameters) { for (auto &parameter : parameters) {
if (floatEquals(parameter.getValue(), parameter.cache)) continue; // no change if (parameter.getValue() == parameter.cache) continue; // no change
if (isnan(parameter.getValue()) && isnan(parameter.cache)) continue; // both are NAN
storage.putFloat(parameter.name, parameter.getValue()); storage.putFloat(parameter.name, parameter.getValue());
parameter.cache = parameter.getValue(); // update cache parameter.cache = parameter.getValue(); // update cache
} }
} }
void printParameters(const char *filter) { void printParameters() {
print("Name Value [Default]\n");
for (auto &parameter : parameters) { for (auto &parameter : parameters) {
if (strncasecmp(parameter.name, filter, strlen(filter))) continue; print("%s = %g\n", parameter.name, parameter.getValue());
if (floatEquals(parameter.getValue(), parameter.inital)) { // parameter changed
print("%-15s %-13g\n", parameter.name, parameter.getValue());
} else {
print("%-15s %-13g [%g]\n", parameter.name, parameter.getValue(), parameter.inital);
}
} }
} }
-1
View File
@@ -20,7 +20,6 @@ void setupPower() {
void readVoltage() { void readVoltage() {
if (voltagePin < 0) return; if (voltagePin < 0) return;
static Rate rate(10); static Rate rate(10);
if (!rate) return; if (!rate) return;
+5 -4
View File
@@ -27,13 +27,15 @@ void setupRC() {
bool readRC() { bool readRC() {
if (rcRxPin < 0) return false; if (rcRxPin < 0) return false;
if (!rc.read()) return false; if (rc.read()) {
SBUSData data = rc.data();
rc.getChannels(channels); for (int i = 0; i < 16; i++) channels[i] = data.ch[i]; // copy channels data
normalizeRC(); normalizeRC();
controlTime = t; controlTime = t;
return true; return true;
} }
return false;
}
void normalizeRC() { void normalizeRC() {
float controls[16]; float controls[16];
@@ -53,7 +55,6 @@ void calibrateRC() {
print("RC_RX_PIN = %d, set the RC pin!\n", rcRxPin); print("RC_RX_PIN = %d, set the RC pin!\n", rcRxPin);
return; return;
} }
uint16_t zero[16]; // for zero positions uint16_t zero[16]; // for zero positions
uint16_t center[16]; // for center positions uint16_t center[16]; // for center positions
uint16_t _[16]; // for unused data uint16_t _[16]; // for unused data
+4 -14
View File
@@ -23,12 +23,6 @@ bool valid(float x) {
return isfinite(x); return isfinite(x);
} }
bool floatEquals(float a, float b, float epsilon = 0) {
if (isnan(a) && isnan(b)) return true;
if (a == b) return true;
return fabsf(a - b) <= epsilon;
}
// Wrap angle to [-PI, PI) // Wrap angle to [-PI, PI)
float wrapAngle(float angle) { float wrapAngle(float angle) {
angle = fmodf(angle, 2 * PI); angle = fmodf(angle, 2 * PI);
@@ -53,14 +47,13 @@ void splitString(String& str, String& token0, String& token1, String& token2) {
if (token2.c_str() == NULL) token2 = ""; if (token2.c_str() == NULL) token2 = "";
} }
// Simplified ESP-NOW Serial without resends // Simplified ESP-NOW Serial without tx buffering and resends
class ESPNOWSerial : public ESP_NOW_Serial_Class { class ESPNOWSerial : public ESP_NOW_Serial_Class {
public: public:
int lost = 0;
using ESP_NOW_Serial_Class::ESP_NOW_Serial_Class; using ESP_NOW_Serial_Class::ESP_NOW_Serial_Class;
void onSent(bool success) override { void onSent(bool success) override {} // disable resends
if (!success) lost++; size_t write(const uint8_t *data, size_t len) override {
ESP_NOW_Serial_Class::onSent(true); // always report success to avoid resends return ESP_NOW_Peer::send(data, len); // pure send without buffering
} }
}; };
@@ -72,9 +65,6 @@ public:
Rate(float rate) : rate(rate) {} Rate(float rate) : rate(rate) {}
operator bool() { operator bool() {
if (t == last) {
return true; // the same step
}
if (t - last >= 1 / rate) { if (t - last >= 1 / rate) {
last = t; last = t;
return true; return true;
-19
View File
@@ -56,7 +56,6 @@ void setupWiFi() {
void sendWiFi(const uint8_t *buf, int len) { void sendWiFi(const uint8_t *buf, int len) {
if (espnow) { if (espnow) {
espnow.write(buf, len); espnow.write(buf, len);
static Rate discovery(2); static Rate discovery(2);
if (discovery) espnowBroadcast.write((const uint8_t *)"flix", 4); // broadcast message to help finding this device if (discovery) espnowBroadcast.write((const uint8_t *)"flix", 4); // broadcast message to help finding this device
return; return;
@@ -90,7 +89,6 @@ void printWiFiInfo() {
print("Peer MAC: %s\n", MacAddress(espnow.addr()).toString().c_str()); print("Peer MAC: %s\n", MacAddress(espnow.addr()).toString().c_str());
print("Encrypted: %d\n", espnow.isEncrypted()); print("Encrypted: %d\n", espnow.isEncrypted());
print("Channel: %d\n", espnow.getChannel()); print("Channel: %d\n", espnow.getChannel());
print("Lost packets: %d\n", espnow.lost);
} else if (WiFi.getMode() == WIFI_MODE_AP) { } else if (WiFi.getMode() == WIFI_MODE_AP) {
print("Mode: Access Point (AP)\n"); print("Mode: Access Point (AP)\n");
print("MAC: %s\n", WiFi.softAPmacAddress().c_str()); print("MAC: %s\n", WiFi.softAPmacAddress().c_str());
@@ -133,20 +131,3 @@ void configWiFi(int mode, const char *first, const char *second) {
} }
print("✓ Reboot to apply new settings\n"); print("✓ Reboot to apply new settings\n");
} }
void setWiFiMode(const String& mode) {
if (mode == "ap") {
wifiMode = W_AP;
} else if (mode == "sta") {
wifiMode = W_STA;
} else if (mode == "espnow") {
wifiMode = W_ESPNOW;
} else if (mode == "off") {
wifiMode = W_DISABLED;
} else {
print("Invalid Wi-Fi mode\n");
return;
}
static const char *modes[] = {"Disabled", "Access Point (AP)", "Client (STA)", "ESP-NOW"};
print("✓ Wi-Fi mode set to %s, reboot to apply\n", modes[wifiMode]);
}
+5 -4
View File
@@ -15,11 +15,12 @@ public:
SBUS(HardwareSerial& bus, const int8_t rxpin, const int8_t txpin, const bool inv = true) {}; SBUS(HardwareSerial& bus, const int8_t rxpin, const int8_t txpin, const bool inv = true) {};
void begin(int rxpin = -1, int txpin = -1, bool inv = true, bool fast = false) {}; void begin(int rxpin = -1, int txpin = -1, bool inv = true, bool fast = false) {};
bool read() { return joystickInit(); }; bool read() { return joystickInit(); };
void getChannels(uint16_t (&channels)[16]) const { SBUSData data() {
int16_t ch[16]; SBUSData data;
joystickGet(ch); joystickGet(data.ch);
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
channels[i] = map(ch[i], -32768, 32767, 1000, 2000); // convert to pulse width style data.ch[i] = map(data.ch[i], -32768, 32767, 1000, 2000); // convert to pulse width style
} }
return data;
}; };
}; };
+1 -2
View File
@@ -68,7 +68,7 @@ const char *getParameterName(int index);
float getParameter(int index); float getParameter(int index);
float getParameter(const char *name); float getParameter(const char *name);
bool setParameter(const char *name, const float value); bool setParameter(const char *name, const float value);
void printParameters(const char *filter); void printParameters();
void resetParameters(); void resetParameters();
// mocks // mocks
@@ -79,4 +79,3 @@ void printIMUCalibration() { print("cal: N/A\n"); };
void printIMUInfo() {}; void printIMUInfo() {};
void printWiFiInfo() {}; void printWiFiInfo() {};
void configWiFi(bool, const char*, const char*) { print("Skip WiFi config\n"); }; void configWiFi(bool, const char*, const char*) { print("Skip WiFi config\n"); };
void setWiFiMode(const String& mode) { print("Skip WiFi mode set\n"); };
+1 -1
View File
@@ -14,7 +14,7 @@
// Mocks // Mocks
int wifiMode = 1; int wifiMode = 1;
int wifiLongRange = 0; int wifiLongRange = 0;
int espnowChannel = 6; // int espnowChannel = 6;
const int W_DISABLED = 0, W_AP = 1, W_STA = 2, W_ESPNOW = 3; const int W_DISABLED = 0, W_AP = 1, W_STA = 2, W_ESPNOW = 3;
int udpLocalPort = 14580; int udpLocalPort = 14580;
-2
View File
@@ -28,8 +28,6 @@ from pyflix import Flix
flix = Flix() # create a Flix object and wait for connection flix = Flix() # create a Flix object and wait for connection
``` ```
If using ESP-NOW connection, specify the proxy device name in `FLIX_DEVICE` environment variable or pass it to the constructor: `Flix(device='/dev/cu.usbserial-0001')`.
### Telemetry ### Telemetry
Basic telemetry is available through object properties. The property names generally match the corresponding variables in the firmware code: Basic telemetry is available through object properties. The property names generally match the corresponding variables in the firmware code:
+1 -6
View File
@@ -44,17 +44,12 @@ class Flix:
_print_buffer: str = '' _print_buffer: str = ''
_modes = ['RAW', 'ACRO', 'STAB', 'AUTO'] _modes = ['RAW', 'ACRO', 'STAB', 'AUTO']
def __init__(self, system_id: int=1, wait_connection: bool=True, device=os.getenv('FLIX_DEVICE')): def __init__(self, system_id: int=1, wait_connection: bool=True):
if not (0 <= system_id < 256): if not (0 <= system_id < 256):
raise ValueError('system_id must be in range [0, 255]') raise ValueError('system_id must be in range [0, 255]')
self._setup_mavlink() self._setup_mavlink()
self.system_id = system_id self.system_id = system_id
self._init_state() self._init_state()
if device is not None:
# User defined connection
logger.debug(f'Connecting to {device}')
self.connection: mavutil.mavfile = mavutil.mavlink_connection(device, source_system=255) # type: ignore
else:
try: try:
# Direct connection # Direct connection
logger.debug('Listening on port 14550') logger.debug('Listening on port 14550')
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "pyflix" name = "pyflix"
version = "0.16" version = "0.15"
description = "Python API for Flix drone" description = "Python API for Flix drone"
authors = [{ name="Oleg Kalachev", email="okalachev@gmail.com" }] authors = [{ name="Oleg Kalachev", email="okalachev@gmail.com" }]
license = "MIT" license = "MIT"