mirror of
https://github.com/okalachev/flix.git
synced 2026-08-16 00:38:56 +00:00
Compare commits
8
Commits
master
..
6064ee18f3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6064ee18f3 | ||
|
|
dc26abe975 | ||
|
|
5ee828dc01 | ||
|
|
b8c687f3ed | ||
|
|
c3b818c2ae | ||
|
|
531b3f4d04 | ||
|
|
795b248b94 | ||
|
|
77c4b5fc5b |
+11
-18
@@ -13,7 +13,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
ARDUINO_SKETCH_ALWAYS_EXPORT_BINARIES: 1
|
ARDUINO_SKETCH_ALWAYS_EXPORT_BINARIES: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Install Arduino CLI
|
- name: Install Arduino CLI
|
||||||
run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh
|
run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh
|
||||||
- name: Build firmware for ESP32
|
- name: Build firmware for ESP32
|
||||||
@@ -28,20 +28,22 @@ jobs:
|
|||||||
run: make BOARD=esp32:esp32:esp32s3:CDCOnBoot=cdc,PSRAM=opi EXTRA=--output-dir=flix/build/esp32.esp32.esp32s3.opi
|
run: make BOARD=esp32:esp32:esp32s3:CDCOnBoot=cdc,PSRAM=opi EXTRA=--output-dir=flix/build/esp32.esp32.esp32s3.opi
|
||||||
- name: Build firmware for Flix2
|
- 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'
|
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: Build firmware for STM32F4
|
||||||
|
run: |
|
||||||
|
arduino-cli core install STMicroelectronics:stm32 --additional-urls https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json
|
||||||
|
make BOARD=STMicroelectronics:stm32:GenF4
|
||||||
- name: Upload binaries
|
- name: Upload binaries
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: firmware-binary
|
name: firmware-binary
|
||||||
path: flix/build
|
path: flix/build
|
||||||
- name: Build espnow-proxy
|
|
||||||
run: arduino-cli compile --fqbn esp32:esp32:esp32 tools/espnow-proxy
|
|
||||||
- name: Check c_cpp_properties.json
|
- name: Check c_cpp_properties.json
|
||||||
run: tools/check_c_cpp_properties.py
|
run: tools/check_c_cpp_properties.py
|
||||||
|
|
||||||
build_macos:
|
build_macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Install Arduino CLI
|
- name: Install Arduino CLI
|
||||||
run: brew install arduino-cli
|
run: brew install arduino-cli
|
||||||
- name: Build firmware
|
- name: Build firmware
|
||||||
@@ -52,7 +54,7 @@ jobs:
|
|||||||
build_windows:
|
build_windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Install Arduino CLI
|
- name: Install Arduino CLI
|
||||||
run: choco install arduino-cli
|
run: choco install arduino-cli
|
||||||
- name: Install Make
|
- name: Install Make
|
||||||
@@ -73,7 +75,7 @@ jobs:
|
|||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y curl wget build-essential cmake g++ pkg-config gnupg2 lsb-release sudo
|
DEBIAN_FRONTEND=noninteractive apt-get install -y curl wget build-essential cmake g++ pkg-config gnupg2 lsb-release sudo
|
||||||
- name: Install Arduino CLI
|
- name: Install Arduino CLI
|
||||||
run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh
|
run: curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Install Gazebo
|
- name: Install Gazebo
|
||||||
run: |
|
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'
|
sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list'
|
||||||
@@ -84,16 +86,7 @@ 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
|
- uses: actions/upload-artifact@v4
|
||||||
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:
|
with:
|
||||||
name: gazebo-plugin-binary
|
name: gazebo-plugin-binary
|
||||||
path: gazebo/build/*.so
|
path: gazebo/build/*.so
|
||||||
@@ -105,7 +98,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install Arduino CLI
|
- name: Install Arduino CLI
|
||||||
run: brew install arduino-cli
|
run: brew install arduino-cli
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Clean up python binaries # Workaround for https://github.com/actions/setup-python/issues/577
|
- name: Clean up python binaries # Workaround for https://github.com/actions/setup-python/issues/577
|
||||||
run: |
|
run: |
|
||||||
rm -f /usr/local/bin/2to3*
|
rm -f /usr/local/bin/2to3*
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
markdownlint:
|
markdownlint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Install markdownlint
|
- name: Install markdownlint
|
||||||
run: npm install -g markdownlint-cli2
|
run: npm install -g markdownlint-cli2
|
||||||
- name: Run markdownlint
|
- name: Run markdownlint
|
||||||
@@ -25,16 +25,13 @@ jobs:
|
|||||||
build_book:
|
build_book:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: markdownlint
|
needs: markdownlint
|
||||||
env:
|
|
||||||
BINARIES: ${{ github.event_name == 'push' && (github.ref_name == 'master' || github.ref_name == 'dev') && github.repository == 'okalachev/flix' }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Install mdBook
|
- name: Install mdBook
|
||||||
run: cargo install mdbook --vers 0.4.43 --locked
|
run: cargo install mdbook --vers 0.4.43 --locked
|
||||||
- name: Build book
|
- name: Build book
|
||||||
run: cd docs && mdbook build
|
run: cd docs && mdbook build
|
||||||
- name: Wait for Build to complete
|
- name: Wait for Build to complete
|
||||||
if: ${{ env.BINARIES }}
|
|
||||||
uses: lewagon/wait-on-check-action@v1.9.1
|
uses: lewagon/wait-on-check-action@v1.9.1
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.sha }}
|
ref: ${{ github.sha }}
|
||||||
@@ -42,7 +39,6 @@ jobs:
|
|||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
wait-interval: 30
|
wait-interval: 30
|
||||||
- name: Find firmware binaries
|
- name: Find firmware binaries
|
||||||
if: ${{ env.BINARIES }}
|
|
||||||
id: build_run
|
id: build_run
|
||||||
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')
|
RUN_ID=$(gh api "repos/${{ github.repository }}/actions/workflows/build.yml/runs?head_sha=${{ github.sha }}&per_page=1" --jq '.workflow_runs[0].id')
|
||||||
@@ -50,8 +46,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Download firmware binaries
|
- name: Download firmware binaries
|
||||||
if: ${{ env.BINARIES }}
|
uses: actions/download-artifact@v4
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
repository: ${{ github.repository }}
|
repository: ${{ github.repository }}
|
||||||
@@ -59,7 +54,6 @@ jobs:
|
|||||||
name: firmware-binary
|
name: firmware-binary
|
||||||
path: docs/build
|
path: docs/build
|
||||||
- name: Create shortcuts for firmware binaries
|
- name: Create shortcuts for firmware binaries
|
||||||
if: ${{ env.BINARIES }}
|
|
||||||
working-directory: docs/build
|
working-directory: docs/build
|
||||||
run: |
|
run: |
|
||||||
for FQBN in esp32.esp32.*; do
|
for FQBN in esp32.esp32.*; do
|
||||||
@@ -70,12 +64,12 @@ jobs:
|
|||||||
ln -s "$FQBN/flix.ino.bootloader.bin" "flix.$BOARD.bootloader.bin"
|
ln -s "$FQBN/flix.ino.bootloader.bin" "flix.$BOARD.bootloader.bin"
|
||||||
done
|
done
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v5
|
uses: actions/upload-pages-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: docs/build
|
path: docs/build
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
if: ${{ github.event_name == 'push' && github.ref_name == 'master' }}
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
concurrency:
|
concurrency:
|
||||||
group: "pages"
|
group: "pages"
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
@@ -87,4 +81,4 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v5
|
uses: actions/deploy-pages@v4
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
csv_to_ulog:
|
csv_to_ulog:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Build csv_to_ulog
|
- name: Build csv_to_ulog
|
||||||
run: cd tools/csv_to_ulog && mkdir build && cd build && cmake .. && make
|
run: cd tools/csv_to_ulog && mkdir build && cd build && cmake .. && make
|
||||||
- name: Test csv_to_ulog
|
- name: Test csv_to_ulog
|
||||||
@@ -22,13 +22,13 @@ jobs:
|
|||||||
pyflix:
|
pyflix:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Install Python build tools
|
- name: Install Python build tools
|
||||||
run: pip install build
|
run: pip install build
|
||||||
- name: Build pyflix
|
- name: Build pyflix
|
||||||
run: python3 -m build tools
|
run: python3 -m build tools
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: pyflix
|
name: pyflix
|
||||||
path: |
|
path: |
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
python_tools:
|
python_tools:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v4
|
||||||
- name: Install Python dependencies
|
- name: Install Python dependencies
|
||||||
run: pip install -r tools/requirements.txt
|
run: pip install -r tools/requirements.txt
|
||||||
- name: Test csv_to_mcap tool
|
- name: Test csv_to_mcap tool
|
||||||
@@ -46,23 +46,3 @@ jobs:
|
|||||||
echo -e "t,x,y,z\n0,1,2,3\n1,4,5,6" > log.csv
|
echo -e "t,x,y,z\n0,1,2,3\n1,4,5,6" > log.csv
|
||||||
./csv_to_mcap.py log.csv
|
./csv_to_mcap.py log.csv
|
||||||
test $(stat -c %s log.mcap) -eq 883
|
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
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ PORT := $(strip $(wildcard /dev/serial/by-id/usb-Silicon_Labs_CP21* /dev/serial/
|
|||||||
export ARDUINO_NETWORK_CONNECTION_TIMEOUT := 1h
|
export ARDUINO_NETWORK_CONNECTION_TIMEOUT := 1h
|
||||||
|
|
||||||
build: .core .libs
|
build: .core .libs
|
||||||
arduino-cli compile flix --fqbn $(BOARD) --build-property "build.core_debug_level=1" $(EXTRA)
|
arduino-cli compile --fqbn $(BOARD) --build-property "build.core_debug_level=1" flix $(EXTRA)
|
||||||
|
|
||||||
upload: build
|
upload: build
|
||||||
arduino-cli upload flix --fqbn $(BOARD) -p "$(PORT)"
|
arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" flix
|
||||||
|
|
||||||
erase:
|
erase:
|
||||||
arduino-cli burn-bootloader --fqbn $(BOARD) -p "$(PORT)" -P esptool
|
arduino-cli burn-bootloader --fqbn $(BOARD) -p "$(PORT)" -P esptool
|
||||||
@@ -27,8 +27,8 @@ libs .libs:
|
|||||||
touch .libs
|
touch .libs
|
||||||
|
|
||||||
upload_proxy: .core .libs
|
upload_proxy: .core .libs
|
||||||
arduino-cli compile tools/espnow-proxy --fqbn $(BOARD)
|
arduino-cli compile --fqbn $(BOARD) tools/espnow-proxy
|
||||||
arduino-cli upload tools/espnow-proxy --fqbn $(BOARD) -p "$(PORT)"
|
arduino-cli upload --fqbn $(BOARD) -p "$(PORT)" tools/espnow-proxy
|
||||||
|
|
||||||
gazebo/build cmake: gazebo/CMakeLists.txt
|
gazebo/build cmake: gazebo/CMakeLists.txt
|
||||||
mkdir -p gazebo/build
|
mkdir -p gazebo/build
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
* [`vector.h`](https://github.com/okalachev/flix/blob/master/flix/vector.h), [`quaternion.h`](https://github.com/okalachev/flix/blob/master/flix/quaternion.h) — библиотеки векторов и кватернионов.
|
* [`vector.h`](https://github.com/okalachev/flix/blob/master/flix/vector.h), [`quaternion.h`](https://github.com/okalachev/flix/blob/master/flix/quaternion.h) — библиотеки векторов и кватернионов.
|
||||||
* [`pid.h`](https://github.com/okalachev/flix/blob/master/flix/pid.h) — ПИД-регулятор.
|
* [`pid.h`](https://github.com/okalachev/flix/blob/master/flix/pid.h) — ПИД-регулятор.
|
||||||
* [`filter.h`](https://github.com/okalachev/flix/blob/master/flix/filter.h) — фильтр нижних частот.
|
* [`lpf.h`](https://github.com/okalachev/flix/blob/master/flix/lpf.h) — фильтр нижних частот.
|
||||||
|
|
||||||
### Подсистема управления
|
### Подсистема управления
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ Utility files:
|
|||||||
|
|
||||||
* [`vector.h`](../flix/vector.h), [`quaternion.h`](../flix/quaternion.h) — vector and quaternion libraries.
|
* [`vector.h`](../flix/vector.h), [`quaternion.h`](../flix/quaternion.h) — vector and quaternion libraries.
|
||||||
* [`pid.h`](../flix/pid.h) — generic PID controller.
|
* [`pid.h`](../flix/pid.h) — generic PID controller.
|
||||||
* [`filter.h`](../flix/filter.h) — generic low-pass filter.
|
* [`lpf.h`](../flix/lpf.h) — generic low-pass filter.
|
||||||
|
|
||||||
### Control subsystem
|
### Control subsystem
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
#include "pid.h"
|
#include "pid.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "filter.h"
|
#include "lpf.h"
|
||||||
|
|
||||||
extern const int MOTOR_REAR_LEFT, MOTOR_REAR_RIGHT, MOTOR_FRONT_RIGHT, MOTOR_FRONT_LEFT;
|
extern const int MOTOR_REAR_LEFT, MOTOR_REAR_RIGHT, MOTOR_FRONT_RIGHT, MOTOR_FRONT_LEFT;
|
||||||
extern const int RAW, ACRO, STAB, AUTO;
|
extern const int RAW, ACRO, STAB, AUTO;
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "quaternion.h"
|
#include "quaternion.h"
|
||||||
#include "pid.h"
|
#include "pid.h"
|
||||||
#include "filter.h"
|
#include "lpf.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
const int RAW = 0, ACRO = 1, STAB = 2, AUTO = 3; // flight modes
|
const int RAW = 0, ACRO = 1, STAB = 2, AUTO = 3; // flight modes
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "quaternion.h"
|
#include "quaternion.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "filter.h"
|
#include "lpf.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
Vector rates; // estimated angular rates, rad/s
|
Vector rates; // estimated angular rates, rad/s
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#include <FlixPeriph.h>
|
#include <FlixPeriph.h>
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "filter.h"
|
#include "lpf.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
IMU *imu;
|
IMU *imu;
|
||||||
|
|||||||
@@ -19,18 +19,27 @@ 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
|
||||||
|
#ifdef ESP32
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (motorPins[i] < 0) continue; // skip unassigned motors
|
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
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
analogWriteResolution(pwmResolution);
|
||||||
|
analogWriteFrequency(pwmFrequency);
|
||||||
|
#endif
|
||||||
sendMotors();
|
sendMotors();
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
if (motorPins[i] < 0) continue; // skip unassigned motors
|
||||||
|
#ifdef ESP32
|
||||||
ledcWrite(motorPins[i], getDutyCycle(motors[i]));
|
ledcWrite(motorPins[i], getDutyCycle(motors[i]));
|
||||||
|
#else
|
||||||
|
analogWrite(motorPins[i], getDutyCycle(motors[i]));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
// Parameters storage in flash memory
|
// Parameters storage in flash memory
|
||||||
|
|
||||||
#include <Preferences.h>
|
#include "prefs.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
extern int channelZero[16], channelMax[16];
|
extern int channelZero[16], channelMax[16];
|
||||||
@@ -22,7 +22,7 @@ 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 initial; // default value
|
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) {};
|
||||||
@@ -150,7 +150,7 @@ void setupParameters() {
|
|||||||
storage.begin("flix");
|
storage.begin("flix");
|
||||||
// Read parameters from storage
|
// Read parameters from storage
|
||||||
for (auto ¶meter : parameters) {
|
for (auto ¶meter : parameters) {
|
||||||
parameter.initial = parameter.getValue();
|
parameter.inital = parameter.getValue();
|
||||||
if (storage.isKey(parameter.name)) {
|
if (storage.isKey(parameter.name)) {
|
||||||
parameter.setValue(storage.getFloat(parameter.name));
|
parameter.setValue(storage.getFloat(parameter.name));
|
||||||
}
|
}
|
||||||
@@ -211,10 +211,10 @@ void printParameters(const char *filter) {
|
|||||||
for (auto ¶meter : parameters) {
|
for (auto ¶meter : parameters) {
|
||||||
if (strncasecmp(parameter.name, filter, strlen(filter))) continue;
|
if (strncasecmp(parameter.name, filter, strlen(filter))) continue;
|
||||||
|
|
||||||
if (floatEquals(parameter.getValue(), parameter.initial)) { // parameter changed
|
if (floatEquals(parameter.getValue(), parameter.inital)) { // parameter changed
|
||||||
print("%-15s %-13g\n", parameter.name, parameter.getValue());
|
print("%-15s %-13g\n", parameter.name, parameter.getValue());
|
||||||
} else {
|
} else {
|
||||||
print("%-15s %-13g [%g]\n", parameter.name, parameter.getValue(), parameter.initial);
|
print("%-15s %-13g [%g]\n", parameter.name, parameter.getValue(), parameter.inital);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "filter.h"
|
#include "lpf.h"
|
||||||
|
|
||||||
class PID {
|
class PID {
|
||||||
public:
|
public:
|
||||||
@@ -18,7 +18,7 @@ public:
|
|||||||
|
|
||||||
LowPassFilter<float> lpf; // low pass filter for derivative term
|
LowPassFilter<float> lpf; // low pass filter for derivative term
|
||||||
|
|
||||||
PID(float p, float i = 0, float d = 0, float windup = INFINITY, 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) {}
|
p(p), i(i), d(d), windup(windup), lpf(dAlpha), dtMax(dtMax) {}
|
||||||
|
|
||||||
float update(float error) {
|
float update(float error) {
|
||||||
|
|||||||
+5
-1
@@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
// Power management
|
// Power management
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
#include <soc/soc.h>
|
#include <soc/soc.h>
|
||||||
#include <soc/rtc_cntl_reg.h>
|
#include <soc/rtc_cntl_reg.h>
|
||||||
#include "filter.h"
|
#endif
|
||||||
|
#include "lpf.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
float voltage = NAN;
|
float voltage = NAN;
|
||||||
@@ -14,7 +16,9 @@ int voltagePin = -1;
|
|||||||
float voltageScale = 2;
|
float voltageScale = 2;
|
||||||
|
|
||||||
void setupPower() {
|
void setupPower() {
|
||||||
|
#ifdef ESP32
|
||||||
REG_CLR_BIT(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ENA); // disable reset on low voltage
|
REG_CLR_BIT(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ENA); // disable reset on low voltage
|
||||||
|
#endif
|
||||||
if (digitalPinToAnalogChannel(voltagePin) == -1) voltagePin = -1; // test ADC pin
|
if (digitalPinToAnalogChannel(voltagePin) == -1) voltagePin = -1; // test ADC pin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+283
@@ -0,0 +1,283 @@
|
|||||||
|
#ifdef ESP32
|
||||||
|
#include <Preferences.h>
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
class Preferences {
|
||||||
|
public:
|
||||||
|
Preferences() = default;
|
||||||
|
~Preferences() = default;
|
||||||
|
|
||||||
|
bool begin(const char *name, bool readOnly = false, const char *partition_label = NULL) {
|
||||||
|
(void)name;
|
||||||
|
(void)readOnly;
|
||||||
|
(void)partition_label;
|
||||||
|
load();
|
||||||
|
started = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void end() {
|
||||||
|
started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clear() {
|
||||||
|
if (!started) return false;
|
||||||
|
for (auto &entry : entries) {
|
||||||
|
entry = Entry();
|
||||||
|
}
|
||||||
|
return save();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t putFloat(const char *key, float value) {
|
||||||
|
if (!started) return 0;
|
||||||
|
int index = ensureKey(key);
|
||||||
|
if (index < 0) return 0;
|
||||||
|
entries[index].hasFloat = true;
|
||||||
|
entries[index].hasString = false;
|
||||||
|
entries[index].stringValue = "";
|
||||||
|
entries[index].floatValue = value;
|
||||||
|
if (!save()) return 0;
|
||||||
|
return sizeof(float);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getFloat(const char *key, float defaultValue = NAN) {
|
||||||
|
if (!started) return defaultValue;
|
||||||
|
int index = findKey(key);
|
||||||
|
if (index < 0 || !entries[index].hasFloat) return defaultValue;
|
||||||
|
return entries[index].floatValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t putString(const char *key, const char *value) {
|
||||||
|
if (!started) return 0;
|
||||||
|
int index = ensureKey(key);
|
||||||
|
if (index < 0) return 0;
|
||||||
|
if (!value) value = "";
|
||||||
|
if (strlen(value) > MAX_STRING_LEN) return 0;
|
||||||
|
|
||||||
|
entries[index].hasString = true;
|
||||||
|
entries[index].hasFloat = false;
|
||||||
|
entries[index].stringValue = value;
|
||||||
|
if (!save()) return 0;
|
||||||
|
return entries[index].stringValue.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t putString(const char *key, String value) {
|
||||||
|
return putString(key, value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
String getString(const char *key, String defaultValue = String()) {
|
||||||
|
if (!started) return defaultValue;
|
||||||
|
int index = findKey(key);
|
||||||
|
if (index < 0 || !entries[index].hasString) return defaultValue;
|
||||||
|
return entries[index].stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isKey(const char *key) {
|
||||||
|
if (!started) return false;
|
||||||
|
return findKey(key) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int MAX_ENTRIES = 128;
|
||||||
|
static const int MAX_KEY_LEN = 15;
|
||||||
|
static const int MAX_STRING_LEN = 95;
|
||||||
|
static const uint32_t MAGIC = 0x46504B56; // "VKPF"
|
||||||
|
static const uint8_t VERSION = 1;
|
||||||
|
static const uint8_t TYPE_FLOAT = 1;
|
||||||
|
static const uint8_t TYPE_STRING = 2;
|
||||||
|
static const int STORAGE_SIZE = 4096;
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
bool used = false;
|
||||||
|
char key[MAX_KEY_LEN + 1] = {};
|
||||||
|
bool hasFloat = false;
|
||||||
|
float floatValue = NAN;
|
||||||
|
bool hasString = false;
|
||||||
|
String stringValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool started = false;
|
||||||
|
Entry entries[MAX_ENTRIES];
|
||||||
|
|
||||||
|
static void writeU16(uint8_t *dst, uint16_t value) {
|
||||||
|
dst[0] = static_cast<uint8_t>(value & 0xFF);
|
||||||
|
dst[1] = static_cast<uint8_t>((value >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeU32(uint8_t *dst, uint32_t value) {
|
||||||
|
dst[0] = static_cast<uint8_t>(value & 0xFF);
|
||||||
|
dst[1] = static_cast<uint8_t>((value >> 8) & 0xFF);
|
||||||
|
dst[2] = static_cast<uint8_t>((value >> 16) & 0xFF);
|
||||||
|
dst[3] = static_cast<uint8_t>((value >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t readU16(const uint8_t *src) {
|
||||||
|
return static_cast<uint16_t>(src[0]) |
|
||||||
|
(static_cast<uint16_t>(src[1]) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t readU32(const uint8_t *src) {
|
||||||
|
return static_cast<uint32_t>(src[0]) |
|
||||||
|
(static_cast<uint32_t>(src[1]) << 8) |
|
||||||
|
(static_cast<uint32_t>(src[2]) << 16) |
|
||||||
|
(static_cast<uint32_t>(src[3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
int availableStorageSize() {
|
||||||
|
int n = STORAGE_SIZE;
|
||||||
|
if (EEPROM.length() > 0 && EEPROM.length() < n) n = EEPROM.length();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool save() {
|
||||||
|
const int storageSize = availableStorageSize();
|
||||||
|
if (storageSize < 16) return false;
|
||||||
|
|
||||||
|
uint8_t buffer[STORAGE_SIZE] = {};
|
||||||
|
int pos = 0;
|
||||||
|
writeU32(buffer + pos, MAGIC);
|
||||||
|
pos += 4;
|
||||||
|
buffer[pos++] = VERSION;
|
||||||
|
buffer[pos++] = 0;
|
||||||
|
int lengthPos = pos;
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ENTRIES; i++) {
|
||||||
|
if (!entries[i].used) continue;
|
||||||
|
if (!entries[i].hasFloat && !entries[i].hasString) continue;
|
||||||
|
|
||||||
|
const uint8_t keyLen = static_cast<uint8_t>(strnlen(entries[i].key, MAX_KEY_LEN));
|
||||||
|
if (keyLen == 0) continue;
|
||||||
|
|
||||||
|
if (entries[i].hasFloat) {
|
||||||
|
const int recordSize = 3 + keyLen + 4;
|
||||||
|
if (pos + recordSize > storageSize) return false;
|
||||||
|
|
||||||
|
buffer[pos++] = TYPE_FLOAT;
|
||||||
|
buffer[pos++] = keyLen;
|
||||||
|
buffer[pos++] = 4;
|
||||||
|
memcpy(buffer + pos, entries[i].key, keyLen);
|
||||||
|
pos += keyLen;
|
||||||
|
float value = entries[i].floatValue;
|
||||||
|
memcpy(buffer + pos, &value, sizeof(value));
|
||||||
|
pos += sizeof(value);
|
||||||
|
} else if (entries[i].hasString) {
|
||||||
|
const uint8_t valueLen = static_cast<uint8_t>(entries[i].stringValue.length());
|
||||||
|
const int recordSize = 3 + keyLen + valueLen;
|
||||||
|
if (pos + recordSize > storageSize) return false;
|
||||||
|
|
||||||
|
buffer[pos++] = TYPE_STRING;
|
||||||
|
buffer[pos++] = keyLen;
|
||||||
|
buffer[pos++] = valueLen;
|
||||||
|
memcpy(buffer + pos, entries[i].key, keyLen);
|
||||||
|
pos += keyLen;
|
||||||
|
if (valueLen > 0) {
|
||||||
|
memcpy(buffer + pos, entries[i].stringValue.c_str(), valueLen);
|
||||||
|
pos += valueLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeU16(buffer + lengthPos, static_cast<uint16_t>(pos));
|
||||||
|
|
||||||
|
for (int i = 0; i < storageSize; i++) {
|
||||||
|
EEPROM.write(i, buffer[i]);
|
||||||
|
}
|
||||||
|
EEPROM.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load() {
|
||||||
|
const int storageSize = availableStorageSize();
|
||||||
|
if (storageSize < 16) return;
|
||||||
|
|
||||||
|
for (auto &entry : entries) entry = Entry();
|
||||||
|
|
||||||
|
uint8_t buffer[STORAGE_SIZE] = {};
|
||||||
|
for (int i = 0; i < storageSize; i++) {
|
||||||
|
buffer[i] = EEPROM.read(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
if (readU32(buffer + pos) != MAGIC) return;
|
||||||
|
pos += 4;
|
||||||
|
if (buffer[pos++] != VERSION) return;
|
||||||
|
pos++; // flags
|
||||||
|
|
||||||
|
const uint16_t totalLen = readU16(buffer + pos);
|
||||||
|
pos += 2;
|
||||||
|
if (totalLen < pos || totalLen > storageSize) return;
|
||||||
|
|
||||||
|
while (pos + 3 <= totalLen) {
|
||||||
|
const uint8_t type = buffer[pos++];
|
||||||
|
const uint8_t keyLen = buffer[pos++];
|
||||||
|
const uint8_t valueLen = buffer[pos++];
|
||||||
|
if (keyLen == 0 || keyLen > MAX_KEY_LEN) return;
|
||||||
|
if (pos + keyLen + valueLen > totalLen) return;
|
||||||
|
|
||||||
|
char key[MAX_KEY_LEN + 1] = {};
|
||||||
|
memcpy(key, buffer + pos, keyLen);
|
||||||
|
key[keyLen] = '\0';
|
||||||
|
pos += keyLen;
|
||||||
|
|
||||||
|
int index = ensureKey(key);
|
||||||
|
if (index < 0) return;
|
||||||
|
|
||||||
|
if (type == TYPE_FLOAT && valueLen == 4) {
|
||||||
|
float value = NAN;
|
||||||
|
memcpy(&value, buffer + pos, sizeof(value));
|
||||||
|
entries[index].hasFloat = true;
|
||||||
|
entries[index].hasString = false;
|
||||||
|
entries[index].stringValue = "";
|
||||||
|
entries[index].floatValue = value;
|
||||||
|
} else if (type == TYPE_STRING && valueLen <= MAX_STRING_LEN) {
|
||||||
|
char value[MAX_STRING_LEN + 1] = {};
|
||||||
|
if (valueLen > 0) memcpy(value, buffer + pos, valueLen);
|
||||||
|
value[valueLen] = '\0';
|
||||||
|
entries[index].hasString = true;
|
||||||
|
entries[index].hasFloat = false;
|
||||||
|
entries[index].stringValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += valueLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int findKey(const char *key) {
|
||||||
|
if (!key) return -1;
|
||||||
|
for (int i = 0; i < MAX_ENTRIES; i++) {
|
||||||
|
if (!entries[i].used) continue;
|
||||||
|
if (strncmp(entries[i].key, key, MAX_KEY_LEN + 1) == 0) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ensureKey(const char *key) {
|
||||||
|
if (!key) return -1;
|
||||||
|
if (strlen(key) > MAX_KEY_LEN) return -1;
|
||||||
|
|
||||||
|
int index = findKey(key);
|
||||||
|
if (index >= 0) return index;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ENTRIES; i++) {
|
||||||
|
if (entries[i].used) continue;
|
||||||
|
entries[i].used = true;
|
||||||
|
entries[i].hasFloat = false;
|
||||||
|
entries[i].hasString = false;
|
||||||
|
entries[i].floatValue = NAN;
|
||||||
|
entries[i].stringValue = "";
|
||||||
|
strncpy(entries[i].key, key, MAX_KEY_LEN);
|
||||||
|
entries[i].key[MAX_KEY_LEN] = '\0';
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -6,7 +6,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#ifdef ESP32
|
||||||
|
#include <soc/soc.h>
|
||||||
|
#include <soc/rtc_cntl_reg.h>
|
||||||
#include <ESP32_NOW_Serial.h>
|
#include <ESP32_NOW_Serial.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
const float ONE_G = 9.80665;
|
const float ONE_G = 9.80665;
|
||||||
extern float t;
|
extern float t;
|
||||||
@@ -53,6 +57,7 @@ void splitString(String& str, String& token0, String& token1, String& token2) {
|
|||||||
if (token2.c_str() == NULL) token2 = "";
|
if (token2.c_str() == NULL) token2 = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
// Simplified ESP-NOW Serial without resends
|
// Simplified ESP-NOW Serial without resends
|
||||||
class ESPNOWSerial : public ESP_NOW_Serial_Class {
|
class ESPNOWSerial : public ESP_NOW_Serial_Class {
|
||||||
public:
|
public:
|
||||||
@@ -63,6 +68,7 @@ public:
|
|||||||
ESP_NOW_Serial_Class::onSent(true); // always report success to avoid resends
|
ESP_NOW_Serial_Class::onSent(true); // always report success to avoid resends
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
// Rate limiter
|
// Rate limiter
|
||||||
class Rate {
|
class Rate {
|
||||||
|
|||||||
+97
-96
@@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
// Wi-Fi and ESP-NOW communication
|
// Wi-Fi and ESP-NOW communication
|
||||||
|
|
||||||
#include <WiFi.h>
|
// #include <WiFi.h>
|
||||||
#include <WiFiAP.h>
|
// #include <WiFiAP.h>
|
||||||
#include <WiFiUdp.h>
|
// #include <WiFiUdp.h>
|
||||||
#include <MacAddress.h>
|
// #include <MacAddress.h>
|
||||||
#include <ESP32_NOW_Serial.h>
|
// #include <ESP32_NOW_Serial.h>
|
||||||
#include <Preferences.h>
|
// #include "prefs.h"
|
||||||
#include "util.h"
|
// #include "util.h"
|
||||||
|
|
||||||
extern Preferences storage; // use the main preferences storage
|
extern Preferences storage; // use the main preferences storage
|
||||||
|
|
||||||
@@ -20,120 +20,121 @@ int wifiLongRange = 0;
|
|||||||
int wifiBroadcast = 0; // 0 - broadcast until connected, 1 - always broadcast
|
int wifiBroadcast = 0; // 0 - broadcast until connected, 1 - always broadcast
|
||||||
int udpLocalPort = 14550;
|
int udpLocalPort = 14550;
|
||||||
int udpRemotePort = 14550;
|
int udpRemotePort = 14550;
|
||||||
IPAddress udpRemoteIP = "255.255.255.255";
|
// IPAddress udpRemoteIP = "255.255.255.255";
|
||||||
WiFiUDP udp;
|
// WiFiUDP udp;
|
||||||
|
|
||||||
ESPNOWSerial espnow(NULL, 0, WIFI_IF_AP);
|
// ESPNOWSerial espnow(NULL, 0, WIFI_IF_AP);
|
||||||
ESPNOWSerial espnowBroadcast(ESP_NOW.BROADCAST_ADDR, 0, WIFI_IF_AP);
|
// ESPNOWSerial espnowBroadcast(ESP_NOW.BROADCAST_ADDR, 0, WIFI_IF_AP);
|
||||||
int espnowChannel = 6;
|
int espnowChannel = 6;
|
||||||
|
|
||||||
void setupWiFi() {
|
void setupWiFi() {
|
||||||
print("Setup Wi-Fi\n");
|
// print("Setup Wi-Fi\n");
|
||||||
WiFi.enableLongRange(wifiLongRange);
|
// WiFi.enableLongRange(wifiLongRange);
|
||||||
|
|
||||||
if (wifiMode == W_AP) {
|
// if (wifiMode == W_AP) {
|
||||||
WiFi.softAP(storage.getString("WIFI_AP_SSID", "flix").c_str(), storage.getString("WIFI_AP_PASS", "flixwifi").c_str());
|
// WiFi.softAP(storage.getString("WIFI_AP_SSID", "flix").c_str(), storage.getString("WIFI_AP_PASS", "flixwifi").c_str());
|
||||||
udp.begin(udpLocalPort);
|
// udp.begin(udpLocalPort);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (wifiMode == W_STA) {
|
// if (wifiMode == W_STA) {
|
||||||
WiFi.begin(storage.getString("WIFI_STA_SSID", "").c_str(), storage.getString("WIFI_STA_PASS", "").c_str());
|
// WiFi.begin(storage.getString("WIFI_STA_SSID", "").c_str(), storage.getString("WIFI_STA_PASS", "").c_str());
|
||||||
udp.begin(udpLocalPort);
|
// udp.begin(udpLocalPort);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (wifiMode == W_ESPNOW) {
|
// if (wifiMode == W_ESPNOW) {
|
||||||
WiFi.mode(WIFI_AP);
|
// WiFi.mode(WIFI_AP);
|
||||||
WiFi.setChannel(espnowChannel);
|
// WiFi.setChannel(espnowChannel);
|
||||||
espnow.addr(MacAddress(storage.getString("ESPNOW_PEER_MAC", "FF:FF:FF:FF:FF:FF").c_str()));
|
// espnow.addr(MacAddress(storage.getString("ESPNOW_PEER_MAC", "FF:FF:FF:FF:FF:FF").c_str()));
|
||||||
String key = storage.getString("ESPNOW_PEER_KEY", "");
|
// String key = storage.getString("ESPNOW_PEER_KEY", "");
|
||||||
espnow.setKey(key.isEmpty() ? nullptr : (const uint8_t *)key.c_str());
|
// espnow.setKey(key.isEmpty() ? nullptr : (const uint8_t *)key.c_str());
|
||||||
espnow.begin();
|
// espnow.begin();
|
||||||
espnowBroadcast.begin();
|
// espnowBroadcast.begin();
|
||||||
}
|
// }
|
||||||
|
|
||||||
WiFi.setSleep(false); // disable power save
|
// WiFi.setSleep(false); // disable power save
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (espnow.isEncrypted() && discovery) espnowBroadcast.write((const uint8_t *)"flix", 4); // broadcast message to help finding this device
|
// if (espnow.isEncrypted() && discovery) espnowBroadcast.write((const uint8_t *)"flix", 4); // broadcast message to help finding this device
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (WiFi.softAPgetStationNum() == 0 && !WiFi.isConnected()) return;
|
// if (WiFi.softAPgetStationNum() == 0 && !WiFi.isConnected()) return;
|
||||||
|
|
||||||
bool broadcast = wifiBroadcast || !(t - mavlinkTime < 5); // broadcast if lost connection
|
// bool broadcast = wifiBroadcast || !(t - mavlinkTime < 5); // broadcast if lost connection
|
||||||
udp.beginPacket(broadcast ? IPAddress(255, 255, 255, 255) : udpRemoteIP, udpRemotePort);
|
// udp.beginPacket(broadcast ? IPAddress(255, 255, 255, 255) : udpRemoteIP, udpRemotePort);
|
||||||
udp.write(buf, len);
|
// udp.write(buf, len);
|
||||||
udp.endPacket();
|
// udp.endPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
int receiveWiFi(uint8_t *buf, int len) {
|
int receiveWiFi(uint8_t *buf, int len) {
|
||||||
if (espnow) {
|
// if (espnow) {
|
||||||
return espnow.read(buf, len);
|
// return espnow.read(buf, len);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (WiFi.softAPgetStationNum() == 0 && !WiFi.isConnected()) return 0;
|
// if (WiFi.softAPgetStationNum() == 0 && !WiFi.isConnected()) return 0;
|
||||||
|
|
||||||
udp.parsePacket();
|
// udp.parsePacket();
|
||||||
if (udp.remoteIP()) udpRemoteIP = udp.remoteIP();
|
// if (udp.remoteIP()) udpRemoteIP = udp.remoteIP();
|
||||||
return udp.read(buf, len);
|
// return udp.read(buf, len);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printWiFiInfo() {
|
void printWiFiInfo() {
|
||||||
if (espnow) {
|
// if (espnow) {
|
||||||
print("Mode: ESP-NOW\n");
|
// print("Mode: ESP-NOW\n");
|
||||||
print("ESP-NOW version: %d\n", ESP_NOW.getVersion());
|
// print("ESP-NOW version: %d\n", ESP_NOW.getVersion());
|
||||||
print("Max packet size: %d\n", ESP_NOW.getMaxDataLen());
|
// print("Max packet size: %d\n", ESP_NOW.getMaxDataLen());
|
||||||
print("MAC: %s\n", WiFi.softAPmacAddress().c_str());
|
// print("MAC: %s\n", WiFi.softAPmacAddress().c_str());
|
||||||
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);
|
// 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());
|
||||||
print("SSID: %s\n", WiFi.softAPSSID().c_str());
|
// print("SSID: %s\n", WiFi.softAPSSID().c_str());
|
||||||
print("Password: ***\n");
|
// print("Password: ***\n");
|
||||||
print("Channel: %d\n", WiFi.channel());
|
// print("Channel: %d\n", WiFi.channel());
|
||||||
print("Clients: %d\n", WiFi.softAPgetStationNum());
|
// print("Clients: %d\n", WiFi.softAPgetStationNum());
|
||||||
print("IP: %s\n", WiFi.softAPIP().toString().c_str());
|
// print("IP: %s\n", WiFi.softAPIP().toString().c_str());
|
||||||
print("Remote IP: %s\n", udpRemoteIP.toString().c_str());
|
// print("Remote IP: %s\n", udpRemoteIP.toString().c_str());
|
||||||
} else if (WiFi.getMode() == WIFI_MODE_STA) {
|
// } else if (WiFi.getMode() == WIFI_MODE_STA) {
|
||||||
print("Mode: Client (STA)\n");
|
// print("Mode: Client (STA)\n");
|
||||||
print("Connected: %d\n", WiFi.isConnected());
|
// print("Connected: %d\n", WiFi.isConnected());
|
||||||
print("MAC: %s\n", WiFi.macAddress().c_str());
|
// print("MAC: %s\n", WiFi.macAddress().c_str());
|
||||||
print("SSID: %s\n", WiFi.SSID().c_str());
|
// print("SSID: %s\n", WiFi.SSID().c_str());
|
||||||
print("Password: ***\n");
|
// print("Password: ***\n");
|
||||||
print("Channel: %d\n", WiFi.channel());
|
// print("Channel: %d\n", WiFi.channel());
|
||||||
print("RSSI: %d dBm\n", WiFi.RSSI());
|
// print("RSSI: %d dBm\n", WiFi.RSSI());
|
||||||
print("IP: %s\n", WiFi.localIP().toString().c_str());
|
// print("IP: %s\n", WiFi.localIP().toString().c_str());
|
||||||
print("Remote IP: %s\n", udpRemoteIP.toString().c_str());
|
// print("Remote IP: %s\n", udpRemoteIP.toString().c_str());
|
||||||
} else {
|
// } else {
|
||||||
print("Mode: Disabled\n");
|
// print("Mode: Disabled\n");
|
||||||
}
|
// }
|
||||||
print("MAVLink connected: %d\n", valid(mavlinkTime));
|
// print("MAVLink connected: %d\n", valid(mavlinkTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
void configWiFi(int mode, const char *first, const char *second) {
|
void configWiFi(int mode, const char *first, const char *second) {
|
||||||
MacAddress mac;
|
// MacAddress mac;
|
||||||
if (mode == W_AP && strlen(first) > 0 && strlen(second) >= 8) {
|
// if (mode == W_AP && strlen(first) > 0 && strlen(second) >= 8) {
|
||||||
storage.putString("WIFI_AP_SSID", first);
|
// storage.putString("WIFI_AP_SSID", first);
|
||||||
storage.putString("WIFI_AP_PASS", second);
|
// storage.putString("WIFI_AP_PASS", second);
|
||||||
} else if (mode == W_STA && strlen(first) > 0 && strlen(second) >= 8) {
|
// } else if (mode == W_STA && strlen(first) > 0 && strlen(second) >= 8) {
|
||||||
storage.putString("WIFI_STA_SSID", first);
|
// storage.putString("WIFI_STA_SSID", first);
|
||||||
storage.putString("WIFI_STA_PASS", second);
|
// storage.putString("WIFI_STA_PASS", second);
|
||||||
} else if (mode == W_ESPNOW && mac.fromString(first)) {
|
// } else if (mode == W_ESPNOW && mac.fromString(first)) {
|
||||||
storage.putString("ESPNOW_PEER_MAC", first);
|
// storage.putString("ESPNOW_PEER_MAC", first);
|
||||||
storage.putString("ESPNOW_PEER_KEY", strlen(second) == ESP_NOW_KEY_LEN ? second : "");
|
// storage.putString("ESPNOW_PEER_KEY", strlen(second) == ESP_NOW_KEY_LEN ? second : "");
|
||||||
} else {
|
// } else {
|
||||||
print("Invalid configuration\n");
|
// print("Invalid configuration\n");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
print("✓ Reboot to apply new settings\n");
|
// print("✓ Reboot to apply new settings\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWiFiMode(const String& mode) {
|
void setWiFiMode(const String& mode) {
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
#include "quaternion.h"
|
#include "quaternion.h"
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "wifi.h"
|
#include "wifi.h"
|
||||||
#include "filter.h"
|
#include "lpf.h"
|
||||||
|
|
||||||
extern float t, dt;
|
extern float t, dt;
|
||||||
extern float controlRoll, controlPitch, controlYaw, controlThrottle, controlMode;
|
extern float controlRoll, controlPitch, controlYaw, controlThrottle, controlMode;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include "estimate.ino"
|
#include "estimate.ino"
|
||||||
#include "safety.ino"
|
#include "safety.ino"
|
||||||
#include "log.ino"
|
#include "log.ino"
|
||||||
#include "filter.h"
|
#include "lpf.h"
|
||||||
#include "mavlink.ino"
|
#include "mavlink.ino"
|
||||||
#include "motors.ino"
|
#include "motors.ino"
|
||||||
#include "parameters.ino"
|
#include "parameters.ino"
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
// Mocks
|
// Mocks
|
||||||
int wifiMode = 1;
|
int wifiMode = 1;
|
||||||
int wifiLongRange = 0;
|
int wifiLongRange = 0;
|
||||||
int wifiBroadcast = 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;
|
||||||
|
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ class Flix:
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _mavlink_to_flu(v: Sequence[float]) -> List[float]:
|
def _mavlink_to_flu(v: List[float]) -> List[float]:
|
||||||
if len(v) == 3: # vector
|
if len(v) == 3: # vector
|
||||||
return [v[0], -v[1], -v[2]]
|
return [v[0], -v[1], -v[2]]
|
||||||
elif len(v) == 4: # quaternion
|
elif len(v) == 4: # quaternion
|
||||||
@@ -252,8 +252,8 @@ class Flix:
|
|||||||
raise ValueError(f'List must have 3 (vector) or 4 (quaternion) elements')
|
raise ValueError(f'List must have 3 (vector) or 4 (quaternion) elements')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _flu_to_mavlink(v: Sequence[float]) -> List[float]:
|
def _flu_to_mavlink(v: List[float]) -> List[float]:
|
||||||
return Flix._mavlink_to_flu(v) # flu to mavlink is the same as mavlink to flu
|
return Flix._mavlink_to_flu(v)
|
||||||
|
|
||||||
def _command_send(self, command: int, params: Sequence[float]):
|
def _command_send(self, command: int, params: Sequence[float]):
|
||||||
if len(params) != 7:
|
if len(params) != 7:
|
||||||
@@ -320,13 +320,13 @@ class Flix:
|
|||||||
def set_armed(self, armed: bool):
|
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))
|
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: Sequence[float], yaw: Optional[float] = None, wait: bool = False, tolerance: float = 0.1):
|
def set_position(self, position: List[float], yaw: Optional[float] = None, wait: bool = False, tolerance: float = 0.1):
|
||||||
raise NotImplementedError('Position control is not implemented yet')
|
raise NotImplementedError('Position control is not implemented yet')
|
||||||
|
|
||||||
def set_velocity(self, velocity: Sequence[float], yaw: Optional[float] = None):
|
def set_velocity(self, velocity: List[float], yaw: Optional[float] = None):
|
||||||
raise NotImplementedError('Velocity control is not implemented yet')
|
raise NotImplementedError('Velocity control is not implemented yet')
|
||||||
|
|
||||||
def set_attitude(self, attitude: Sequence[float], thrust: float):
|
def set_attitude(self, attitude: List[float], thrust: float):
|
||||||
if len(attitude) == 3:
|
if len(attitude) == 3:
|
||||||
attitude = Quaternion([attitude[0], attitude[1], attitude[2]]).q # type: ignore
|
attitude = Quaternion([attitude[0], attitude[1], attitude[2]]).q # type: ignore
|
||||||
elif len(attitude) != 4:
|
elif len(attitude) != 4:
|
||||||
@@ -339,7 +339,7 @@ class Flix:
|
|||||||
[attitude[0], attitude[1], attitude[2], attitude[3]],
|
[attitude[0], attitude[1], attitude[2], attitude[3]],
|
||||||
0, 0, 0, thrust)
|
0, 0, 0, thrust)
|
||||||
|
|
||||||
def set_rates(self, rates: Sequence[float], thrust: float):
|
def set_rates(self, rates: List[float], thrust: float):
|
||||||
if len(rates) != 3:
|
if len(rates) != 3:
|
||||||
raise ValueError('Rates must be [roll_rate, pitch_rate, yaw_rate]')
|
raise ValueError('Rates must be [roll_rate, pitch_rate, yaw_rate]')
|
||||||
if not (0 <= thrust <= 1):
|
if not (0 <= thrust <= 1):
|
||||||
@@ -351,7 +351,7 @@ class Flix:
|
|||||||
[1, 0, 0, 0],
|
[1, 0, 0, 0],
|
||||||
rates[0], rates[1], rates[2], thrust)
|
rates[0], rates[1], rates[2], thrust)
|
||||||
|
|
||||||
def set_motors(self, motors: Sequence[float]):
|
def set_motors(self, motors: List[float]):
|
||||||
if len(motors) != 4:
|
if len(motors) != 4:
|
||||||
raise ValueError('motors must have 4 values')
|
raise ValueError('motors must have 4 values')
|
||||||
if not all(0 <= m <= 1 for m in motors):
|
if not all(0 <= m <= 1 for m in motors):
|
||||||
|
|||||||
Reference in New Issue
Block a user