diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e8ca4a..2d2923e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: env: ARDUINO_SKETCH_ALWAYS_EXPORT_BINARIES: 1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Arduino CLI run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh - name: Build firmware for ESP32 @@ -33,17 +33,19 @@ jobs: arduino-cli core install STMicroelectronics:stm32 --additional-urls https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json make BOARD=STMicroelectronics:stm32:GenF4 EXTRA='--build-property compiler.cpp.extra_flags=-DENABLE_HWSERIAL1' - name: Upload binaries - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: firmware-binary path: flix/build + - name: Build espnow-proxy + run: arduino-cli compile --fqbn esp32:esp32:esp32 tools/espnow-proxy - name: Check c_cpp_properties.json run: tools/check_c_cpp_properties.py build_macos: runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Arduino CLI run: brew install arduino-cli - name: Build firmware @@ -54,7 +56,7 @@ jobs: build_windows: runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Arduino CLI run: choco install arduino-cli - name: Install Make @@ -75,7 +77,7 @@ jobs: DEBIAN_FRONTEND=noninteractive apt-get install -y curl wget build-essential cmake g++ pkg-config gnupg2 lsb-release sudo - name: Install Arduino CLI run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Gazebo run: | sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list' @@ -86,7 +88,16 @@ jobs: run: sudo apt-get install -y libsdl2-dev - name: Build simulator run: make build_simulator - - uses: actions/upload-artifact@v4 + - name: Run simulator + env: + GAZEBO_MODEL_PATH: ${{ github.workspace }}/gazebo/models + GAZEBO_PLUGIN_PATH: ${{ github.workspace }}/gazebo/build + run: | + OUT=$(timeout -k 10s 120s gzserver --verbose gazebo/flix.world 2>&1 | tee /dev/stderr) + if echo "$OUT" | grep -Pq "\[Err\](?! \[RenderEngine)"; then + exit 1 + fi + - uses: actions/upload-artifact@v7 with: name: gazebo-plugin-binary path: gazebo/build/*.so @@ -98,7 +109,7 @@ jobs: steps: - name: Install Arduino CLI run: brew install arduino-cli - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Clean up python binaries # Workaround for https://github.com/actions/setup-python/issues/577 run: | rm -f /usr/local/bin/2to3* diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5d7fa59..86b7ac7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,7 @@ jobs: markdownlint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install markdownlint run: npm install -g markdownlint-cli2 - name: Run markdownlint @@ -25,13 +25,16 @@ jobs: build_book: runs-on: ubuntu-latest needs: markdownlint + env: + BINARIES: ${{ github.event_name == 'push' && (github.ref_name == 'master' || github.ref_name == 'dev') && github.repository == 'okalachev/flix' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install mdBook run: cargo install mdbook --vers 0.4.43 --locked - name: Build book run: cd docs && mdbook build - name: Wait for Build to complete + if: ${{ env.BINARIES }} uses: lewagon/wait-on-check-action@v1.9.1 with: ref: ${{ github.sha }} @@ -39,6 +42,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} wait-interval: 30 - name: Find firmware binaries + if: ${{ env.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') @@ -46,7 +50,8 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Download firmware binaries - uses: actions/download-artifact@v4 + if: ${{ env.BINARIES }} + uses: actions/download-artifact@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} repository: ${{ github.repository }} @@ -54,6 +59,7 @@ jobs: name: firmware-binary path: docs/build - name: Create shortcuts for firmware binaries + if: ${{ env.BINARIES }} working-directory: docs/build run: | for FQBN in esp32.esp32.*; do @@ -64,12 +70,12 @@ jobs: ln -s "$FQBN/flix.ino.bootloader.bin" "flix.$BOARD.bootloader.bin" done - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: docs/build deploy: - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + if: ${{ github.event_name == 'push' && github.ref_name == 'master' }} concurrency: group: "pages" cancel-in-progress: true @@ -81,4 +87,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 88d1f25..2adcd6c 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -10,7 +10,7 @@ jobs: csv_to_ulog: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Build csv_to_ulog run: cd tools/csv_to_ulog && mkdir build && cd build && cmake .. && make - name: Test csv_to_ulog @@ -22,13 +22,13 @@ jobs: pyflix: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Python build tools run: pip install build - name: Build pyflix run: python3 -m build tools - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: pyflix path: | @@ -37,7 +37,7 @@ jobs: python_tools: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Python dependencies run: pip install -r tools/requirements.txt - name: Test csv_to_mcap tool @@ -46,3 +46,23 @@ jobs: echo -e "t,x,y,z\n0,1,2,3\n1,4,5,6" > log.csv ./csv_to_mcap.py log.csv test $(stat -c %s log.mcap) -eq 883 + sloc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - run: sudo apt-get install -y cloc jq + - name: Print source lines of code + run: cloc --by-file-by-lang flix + - name: Checkout previous revision + uses: actions/checkout@v7 + with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} + path: prev + - name: Annotate total source lines + run: | + SLOC_CURR=$(cloc flix --json | jq -r '.SUM.code') + SLOC_PREV=$(cloc prev/flix --json | jq -r '.SUM.code') + DIFF=$(printf '%+d' "$((SLOC_CURR - SLOC_PREV))") + echo "* Current SLOC: $SLOC_CURR" >> $GITHUB_STEP_SUMMARY + echo "* Previous SLOC: $SLOC_PREV" >> $GITHUB_STEP_SUMMARY + echo "* Diff: $DIFF" >> $GITHUB_STEP_SUMMARY diff --git a/Makefile b/Makefile index 448ba1f..941b530 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,13 @@ 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 $(EXTRA) + arduino-cli compile flix --fqbn $(BOARD) --build-property "build.core_debug_level=1" $(EXTRA) upload: build - arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" flix + arduino-cli upload flix --fqbn $(BOARD) -p "$(PORT)" + +erase: + arduino-cli burn-bootloader --fqbn $(BOARD) -p "$(PORT)" -P esptool erase: arduino-cli burn-bootloader --fqbn $(BOARD) -p "$(PORT)" -P esptool @@ -27,8 +30,8 @@ libs .libs: touch .libs upload_proxy: .core .libs - arduino-cli compile --fqbn $(BOARD) tools/espnow-proxy - arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" tools/espnow-proxy + arduino-cli compile tools/espnow-proxy --fqbn $(BOARD) + arduino-cli upload tools/espnow-proxy --fqbn $(BOARD) -p "$(PORT)" gazebo/build cmake: gazebo/CMakeLists.txt mkdir -p gazebo/build diff --git a/flix/cli.ino b/flix/cli.ino index 518b982..c6fc819 100644 --- a/flix/cli.ino +++ b/flix/cli.ino @@ -6,7 +6,7 @@ #include "pid.h" #include "vector.h" #include "util.h" -#include "lpf.h" +#include "filter.h" extern const int MOTOR_REAR_LEFT, MOTOR_REAR_RIGHT, MOTOR_FRONT_RIGHT, MOTOR_FRONT_LEFT; extern const int RAW, ACRO, STAB, AUTO; diff --git a/flix/control.ino b/flix/control.ino index dfa3661..43a2a4a 100644 --- a/flix/control.ino +++ b/flix/control.ino @@ -6,7 +6,7 @@ #include "vector.h" #include "quaternion.h" #include "pid.h" -#include "lpf.h" +#include "filter.h" #include "util.h" const int RAW = 0, ACRO = 1, STAB = 2, AUTO = 3; // flight modes diff --git a/flix/estimate.ino b/flix/estimate.ino index 75c2ec6..0e52114 100644 --- a/flix/estimate.ino +++ b/flix/estimate.ino @@ -5,7 +5,7 @@ #include "quaternion.h" #include "vector.h" -#include "lpf.h" +#include "filter.h" #include "util.h" Vector rates; // estimated angular rates, rad/s diff --git a/flix/lpf.h b/flix/filter.h similarity index 100% rename from flix/lpf.h rename to flix/filter.h diff --git a/flix/imu.ino b/flix/imu.ino index 7b02191..b1850a8 100644 --- a/flix/imu.ino +++ b/flix/imu.ino @@ -7,7 +7,7 @@ #include #include #include "vector.h" -#include "lpf.h" +#include "filter.h" #include "util.h" IMU *imu; diff --git a/flix/parameters.ino b/flix/parameters.ino index 1372217..17c793b 100644 --- a/flix/parameters.ino +++ b/flix/parameters.ino @@ -22,7 +22,7 @@ struct Parameter { const char *name; // max length is 15 bool integer; union { float *f; int *i; }; // pointer to the variable - float inital; // default value + float initial; // default value float cache; // what's stored in flash void (*callback)(); // called after parameter change Parameter(const char *name, float *variable, void (*callback)() = nullptr) : name(name), integer(false), f(variable), callback(callback) {}; @@ -150,7 +150,7 @@ void setupParameters() { storage.begin("flix"); // Read parameters from storage for (auto ¶meter : parameters) { - parameter.inital = parameter.getValue(); + parameter.initial = parameter.getValue(); if (storage.isKey(parameter.name)) { parameter.setValue(storage.getFloat(parameter.name)); } @@ -211,10 +211,10 @@ void printParameters(const char *filter) { for (auto ¶meter : parameters) { if (strncasecmp(parameter.name, filter, strlen(filter))) continue; - if (floatEquals(parameter.getValue(), parameter.inital)) { // parameter changed + if (floatEquals(parameter.getValue(), parameter.initial)) { // parameter changed print("%-15s %-13g\n", parameter.name, parameter.getValue()); } else { - print("%-15s %-13g [%g]\n", parameter.name, parameter.getValue(), parameter.inital); + print("%-15s %-13g [%g]\n", parameter.name, parameter.getValue(), parameter.initial); } } } diff --git a/flix/pid.h b/flix/pid.h index c82e009..071d274 100644 --- a/flix/pid.h +++ b/flix/pid.h @@ -5,7 +5,7 @@ #pragma once -#include "lpf.h" +#include "filter.h" class PID { public: @@ -18,7 +18,7 @@ public: LowPassFilter lpf; // low pass filter for derivative term - PID(float p, float i = 0, float d = 0, float windup = 0, float dAlpha = 1, float dtMax = 0.1) : + PID(float p, float i = 0, float d = 0, float windup = INFINITY, float dAlpha = 1, float dtMax = 0.1) : p(p), i(i), d(d), windup(windup), lpf(dAlpha), dtMax(dtMax) {} float update(float error) { diff --git a/flix/power.ino b/flix/power.ino index d41ecd0..184c88e 100644 --- a/flix/power.ino +++ b/flix/power.ino @@ -8,6 +8,7 @@ #include #endif #include "lpf.h" +#include "filter.h" #include "util.h" float voltage = NAN; diff --git a/flix/wifi.ino b/flix/wifi.ino index fec6d48..ffb4d47 100644 --- a/flix/wifi.ino +++ b/flix/wifi.ino @@ -27,6 +27,10 @@ int udpRemotePort = 14550; // ESPNOWSerial espnowBroadcast(ESP_NOW.BROADCAST_ADDR, 0, WIFI_IF_AP); int espnowChannel = 6; +ESPNOWSerial espnow(NULL, 0, WIFI_IF_AP); +ESPNOWSerial espnowBroadcast(ESP_NOW.BROADCAST_ADDR, 0, WIFI_IF_AP); +int espnowChannel = 6; + void setupWiFi() { // print("Setup Wi-Fi\n"); // WiFi.enableLongRange(wifiLongRange); diff --git a/gazebo/flix.h b/gazebo/flix.h index 62593e7..d9a33fc 100644 --- a/gazebo/flix.h +++ b/gazebo/flix.h @@ -9,7 +9,7 @@ #include "quaternion.h" #include "Arduino.h" #include "wifi.h" -#include "lpf.h" +#include "filter.h" extern float t, dt; extern float controlRoll, controlPitch, controlYaw, controlThrottle, controlMode; diff --git a/gazebo/simulator.cpp b/gazebo/simulator.cpp index 60bf190..63bcec7 100644 --- a/gazebo/simulator.cpp +++ b/gazebo/simulator.cpp @@ -23,7 +23,7 @@ #include "estimate.ino" #include "safety.ino" #include "log.ino" -#include "lpf.h" +#include "filter.h" #include "mavlink.ino" #include "motors.ino" #include "parameters.ino" diff --git a/gazebo/wifi.h b/gazebo/wifi.h index b01bac0..1ec8b23 100644 --- a/gazebo/wifi.h +++ b/gazebo/wifi.h @@ -14,6 +14,7 @@ // Mocks int wifiMode = 1; int wifiLongRange = 0; +int wifiBroadcast = 0; int espnowChannel = 6; const int W_DISABLED = 0, W_AP = 1, W_STA = 2, W_ESPNOW = 3; diff --git a/tools/pyflix/flix.py b/tools/pyflix/flix.py index b7166f1..00de7d6 100644 --- a/tools/pyflix/flix.py +++ b/tools/pyflix/flix.py @@ -243,7 +243,7 @@ class Flix: time.sleep(1) @staticmethod - def _mavlink_to_flu(v: List[float]) -> List[float]: + def _mavlink_to_flu(v: Sequence[float]) -> List[float]: if len(v) == 3: # vector return [v[0], -v[1], -v[2]] elif len(v) == 4: # quaternion @@ -252,8 +252,8 @@ class Flix: raise ValueError(f'List must have 3 (vector) or 4 (quaternion) elements') @staticmethod - def _flu_to_mavlink(v: List[float]) -> List[float]: - return Flix._mavlink_to_flu(v) + def _flu_to_mavlink(v: Sequence[float]) -> List[float]: + return Flix._mavlink_to_flu(v) # flu to mavlink is the same as mavlink to flu def _command_send(self, command: int, params: Sequence[float]): if len(params) != 7: @@ -320,13 +320,13 @@ class Flix: def set_armed(self, armed: bool): self._command_send(mavlink.MAV_CMD_COMPONENT_ARM_DISARM, (1 if armed else 0, 0, 0, 0, 0, 0, 0)) - def set_position(self, position: List[float], yaw: Optional[float] = None, wait: bool = False, tolerance: float = 0.1): + def set_position(self, position: Sequence[float], yaw: Optional[float] = None, wait: bool = False, tolerance: float = 0.1): raise NotImplementedError('Position control is not implemented yet') - def set_velocity(self, velocity: List[float], yaw: Optional[float] = None): + def set_velocity(self, velocity: Sequence[float], yaw: Optional[float] = None): raise NotImplementedError('Velocity control is not implemented yet') - def set_attitude(self, attitude: List[float], thrust: float): + def set_attitude(self, attitude: Sequence[float], thrust: float): if len(attitude) == 3: attitude = Quaternion([attitude[0], attitude[1], attitude[2]]).q # type: ignore elif len(attitude) != 4: @@ -339,7 +339,7 @@ class Flix: [attitude[0], attitude[1], attitude[2], attitude[3]], 0, 0, 0, thrust) - def set_rates(self, rates: List[float], thrust: float): + def set_rates(self, rates: Sequence[float], thrust: float): if len(rates) != 3: raise ValueError('Rates must be [roll_rate, pitch_rate, yaw_rate]') if not (0 <= thrust <= 1): @@ -351,7 +351,7 @@ class Flix: [1, 0, 0, 0], rates[0], rates[1], rates[2], thrust) - def set_motors(self, motors: List[float]): + def set_motors(self, motors: Sequence[float]): if len(motors) != 4: raise ValueError('motors must have 4 values') if not all(0 <= m <= 1 for m in motors):