diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 303e73c..4043a31 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,12 @@ jobs: run: make BOARD=esp32:esp32:esp32c3 - name: Build firmware for ESP32-S3 run: make BOARD=esp32:esp32:esp32s3:CDCOnBoot=cdc + - name: Build firmware for ESP32-S3 with QSPI PSRAM + run: make BOARD=esp32:esp32:esp32s3:CDCOnBoot=cdc,PSRAM=enabled EXTRA=--output-dir=flix/build/esp32.esp32.esp32s3.qspi + - name: Build firmware for ESP32-S3 with OPI PSRAM + run: make BOARD=esp32:esp32:esp32s3:CDCOnBoot=cdc,PSRAM=opi EXTRA=--output-dir=flix/build/esp32.esp32.esp32s3.opi + - name: Build firmware for Flix2 + run: make BOARD=esp32:esp32:esp32s3:FlashSize=4M,CDCOnBoot=cdc,PSRAM=opi EXTRA='--build-property "compiler.cpp.extra_flags=-DFLIX2" --output-dir=flix/build/esp32.esp32.flix2' - name: Upload binaries uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 038e4ef..5d7fa59 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,6 +8,7 @@ on: permissions: contents: read + actions: read pages: write id-token: write @@ -30,6 +31,38 @@ jobs: run: cargo install mdbook --vers 0.4.43 --locked - name: Build book run: cd docs && mdbook build + - name: Wait for Build to complete + uses: lewagon/wait-on-check-action@v1.9.1 + with: + ref: ${{ github.sha }} + check-name: build_linux + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 30 + - name: Find firmware binaries + id: build_run + run: | + RUN_ID=$(gh api "repos/${{ github.repository }}/actions/workflows/build.yml/runs?head_sha=${{ github.sha }}&per_page=1" --jq '.workflow_runs[0].id') + echo "id=$RUN_ID" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Download firmware binaries + uses: actions/download-artifact@v4 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + run-id: ${{ steps.build_run.outputs.id }} + name: firmware-binary + path: docs/build + - name: Create shortcuts for firmware binaries + working-directory: docs/build + run: | + for FQBN in esp32.esp32.*; do + zip -r $FQBN.zip $FQBN + BOARD="${FQBN#esp32.esp32.}" + ln -s "$FQBN/flix.ino.merged.bin" "flix.$BOARD.merged.bin" + ln -s "$FQBN/flix.ino.bin" "flix.$BOARD.bin" + ln -s "$FQBN/flix.ino.bootloader.bin" "flix.$BOARD.bootloader.bin" + done - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: diff --git a/Makefile b/Makefile index 105d2f1..448ba1f 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ PORT := $(strip $(wildcard /dev/serial/by-id/usb-Silicon_Labs_CP21* /dev/serial/ export ARDUINO_NETWORK_CONNECTION_TIMEOUT := 1h build: .core .libs - arduino-cli compile --fqbn $(BOARD) --build-property "build.core_debug_level=1" flix + arduino-cli compile --fqbn $(BOARD) --build-property "build.core_debug_level=1" flix $(EXTRA) upload: build arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" flix diff --git a/docs/img/web-flasher.png b/docs/img/web-flasher.png new file mode 100644 index 0000000..56dbaca Binary files /dev/null and b/docs/img/web-flasher.png differ diff --git a/docs/usage.md b/docs/usage.md index c6914c3..1b8b13f 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,20 +1,48 @@ # Usage: build, setup and flight -To fly Flix quadcopter, you need to build the firmware, upload it to the ESP32 board, and set up the drone for flight. +To fly Flix quadcopter, you need to upload the firmware to the ESP32 board, and set up the drone for flight. -To get the firmware sources, clone the repository using git: +## Uploading the firmware + +You can either use the **prebuilt binaries** or **build the firmware** from sources — this will let you modify the firmware and add new features. + +### Prebuilt binaries (the easiest way) + +1. Download the latest firmware file using the following links: + + + |Type|Boards|Link| + |-|-|-| + |ESP32|DevKit, D1 Mini|[`quadcopter.dev/flix.esp32.merged.bin`](https://quadcopter.dev/flix.esp32.merged.bin)| + |ESP32-S3|Most S3 based|[`quadcopter.dev/flix.esp32s3.merged.bin`](https://quadcopter.dev/flix.esp32s3.merged.bin)| + |ESP32-S3 (2MB PSRAM)|S3 Super Mini, S3 Zero (2MB PSRAM)|[`quadcopter.dev/flix.esp32s3.qspi.merged.bin`](https://quadcopter.dev/flix.esp32s3.qspi.merged.bin)| + |ESP32-S3 (8/16MB PSRAM)|S3 Zero (8MB PSRAM)|[`quadcopter.dev/flix.esp32s3.opi.merged.bin`](https://quadcopter.dev/flix.esp32s3.opi.merged.bin)| + |ESP32-C3|C3 Super Mini|[`quadcopter.dev/flix.esp32c3.merged.bin`](https://quadcopter.dev/flix.esp32c3.merged.bin)| + |Flix2|Flix2 board|[`quadcopter.dev/flix.flix2.merged.bin`](https://quadcopter.dev/flix.flix2.merged.bin)| + + +2. Flash your ESP32 board using [ESP32 Web Flasher](https://www.espboards.dev/tools/program/): + + + + * Connect the board to your computer, press *Connect to ESP*, choose the serial port. + * Go to the *Flash* tab. + * Choose the downloaded firmware file, set *Flash address* to *0* (important). + * Click *Program* button and wait until the process is finished. + +### Building from sources (flexible) + +You can build and upload the firmware using either **Arduino IDE** (easier for beginners) or **command line**. + +Get the sources using git: ```bash git clone https://github.com/okalachev/flix.git && cd flix ``` -Beginners can [download the source code as a ZIP archive](https://github.com/okalachev/flix/archive/refs/heads/master.zip). +Beginners can [download the sources as a ZIP archive](https://github.com/okalachev/flix/archive/refs/heads/master.zip). -## Building the firmware - -You can build and upload the firmware using either **Arduino IDE** (easier for beginners) or **command line**. - -### Arduino IDE (Windows, Linux, macOS) +#### Arduino IDE (Windows, Linux, macOS) Flix firmware open in Arduino IDE @@ -29,7 +57,7 @@ You can build and upload the firmware using either **Arduino IDE** (easier for b 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. 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) 1. [Install Arduino CLI](https://arduino.github.io/arduino-cli/installation/). @@ -150,8 +178,6 @@ Before flight you need to calibrate the accelerometer: If using non-default motor pins, set the pin numbers using the parameters: `MOTOR_PIN_FL`, `MOTOR_PIN_FR`, `MOTOR_PIN_RL`, `MOTOR_PIN_RR` (front-left, front-right, rear-left, rear-right respectively). -Certain ESP32 models (such as ESP32-S3 and ESP32-C3) support a lower maximum PWM frequency; on these boards the parameter `MOT_PWM_FREQ` should be set to 38000 Hz. - #### Brushless motors If using brushless motors with ESCs: diff --git a/flix/config.h b/flix/config.h new file mode 100644 index 0000000..badea19 --- /dev/null +++ b/flix/config.h @@ -0,0 +1,27 @@ +// Copyright (c) 2026 Oleg Kalachev +// Repository: https://github.com/okalachev/flix + +// Parameter defaults + +#pragma once + +void setDefaults() { + // Set defaults here + + #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) + pwmFrequency = 38000; + #endif + + #ifdef FLIX2 + imuModel = 4; // ICM-40609-D + imuIntPin = 10; + imuCsPin = 14; + + motorPins[MOTOR_REAR_LEFT] = 41; + motorPins[MOTOR_REAR_RIGHT] = 7; + motorPins[MOTOR_FRONT_RIGHT] = 18; + motorPins[MOTOR_FRONT_LEFT] = 38; + + voltagePin = 3; + #endif +} diff --git a/flix/control.ino b/flix/control.ino index 2b7e3ca..dfa3661 100644 --- a/flix/control.ino +++ b/flix/control.ino @@ -9,31 +9,6 @@ #include "lpf.h" #include "util.h" -#define PITCHRATE_P 0.05 -#define PITCHRATE_I 0.2 -#define PITCHRATE_D 0.001 -#define PITCHRATE_I_LIM 0.3 -#define ROLLRATE_P PITCHRATE_P -#define ROLLRATE_I PITCHRATE_I -#define ROLLRATE_D PITCHRATE_D -#define ROLLRATE_I_LIM PITCHRATE_I_LIM -#define YAWRATE_P 0.3 -#define YAWRATE_I 0.0 -#define YAWRATE_D 0.0 -#define YAWRATE_I_LIM 0.3 -#define ROLL_P 6 -#define ROLL_I 0 -#define ROLL_D 0 -#define PITCH_P ROLL_P -#define PITCH_I ROLL_I -#define PITCH_D ROLL_D -#define YAW_P 3 -#define PITCHRATE_MAX radians(360) -#define ROLLRATE_MAX radians(360) -#define YAWRATE_MAX radians(300) -#define TILT_MAX radians(30) -#define RATES_D_LPF_ALPHA 0.2 // cutoff frequency ~ 40 Hz - const int RAW = 0, ACRO = 1, STAB = 2, AUTO = 3; // flight modes int mode = STAB; bool armed = false; @@ -44,14 +19,14 @@ Vector ratesExtra; // feedforward rates Vector torqueTarget; float thrustTarget; -PID rollRatePID(ROLLRATE_P, ROLLRATE_I, ROLLRATE_D, ROLLRATE_I_LIM, RATES_D_LPF_ALPHA); -PID pitchRatePID(PITCHRATE_P, PITCHRATE_I, PITCHRATE_D, PITCHRATE_I_LIM, RATES_D_LPF_ALPHA); -PID yawRatePID(YAWRATE_P, YAWRATE_I, YAWRATE_D); -PID rollPID(ROLL_P, ROLL_I, ROLL_D); -PID pitchPID(PITCH_P, PITCH_I, PITCH_D); -PID yawPID(YAW_P, 0, 0); -Vector maxRate(ROLLRATE_MAX, PITCHRATE_MAX, YAWRATE_MAX); -float tiltMax = TILT_MAX; +PID rollRatePID(0.05, 0.2, 0.001, 0.3, 0.2); +PID pitchRatePID(0.05, 0.2, 0.001, 0.3, 0.2); +PID yawRatePID(0.3, 0, 0, 0.3); +PID rollPID(6); +PID pitchPID(6); +PID yawPID(3); +Vector maxRate(radians(360), radians(360), radians(360)); +float tiltMax = radians(30); int flightModes[] = {STAB, STAB, STAB}; // map for rc mode switch extern const int MOTOR_REAR_LEFT, MOTOR_REAR_RIGHT, MOTOR_FRONT_RIGHT, MOTOR_FRONT_LEFT; diff --git a/flix/parameters.ino b/flix/parameters.ino index daed501..4594c3e 100644 --- a/flix/parameters.ino +++ b/flix/parameters.ino @@ -14,6 +14,8 @@ extern float rcLossTimeout, descendTime, disarmTilt; extern float voltageScale; extern LowPassFilter voltageFilter; +#include "config.h" + Preferences storage; struct Parameter { @@ -144,6 +146,7 @@ Parameter parameters[] = { void setupParameters() { print("Setup parameters\n"); + setDefaults(); storage.begin("flix"); // Read parameters from storage for (auto ¶meter : parameters) { diff --git a/flix/pid.h b/flix/pid.h index ac7a66b..c82e009 100644 --- a/flix/pid.h +++ b/flix/pid.h @@ -18,7 +18,7 @@ public: LowPassFilter lpf; // low pass filter for derivative term - PID(float p, float i, float d, float windup = 0, float dAlpha = 1, float dtMax = 0.1) : + PID(float p, float i = 0, float d = 0, float windup = 0, float dAlpha = 1, float dtMax = 0.1) : p(p), i(i), d(d), windup(windup), lpf(dAlpha), dtMax(dtMax) {} float update(float error) {