diff --git a/README.md b/README.md
index 8d277c7..f1bc75e 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,12 @@ Outdoor flights demo video of the current prototype:
+### Position control
+
+The position control feature is in development. RoboCamp 2026 demo (using an overhead camera, [sources](https://github.com/xTimop/flix-poscontrol/compare/robolager2026...xTimop:flix-poscontrol:poscontrol)):
+
+
+
## Simulation
The simulator is implemented using Gazebo and runs the original Arduino code:
diff --git a/docs/usage.md b/docs/usage.md
index 02e3a63..9c45103 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -25,7 +25,7 @@ You can build and upload the firmware using either **Arduino IDE** (easier for b
* `FlixPeriph`, the latest version.
* `MAVLink`, version 2.0.25.
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, *ESP32S3 Dev Module* for ESP32-S3 Super 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.
8. [Build and upload](https://docs.arduino.cc/software/ide-v2/tutorials/getting-started/ide-v2-uploading-a-sketch) the firmware using Arduino IDE.
@@ -96,7 +96,7 @@ To access the console using serial port:
To access the console using QGroundControl:
1. Connect to the drone using QGroundControl app.
-2. Go to the QGroundControl menu ⇒ *Vehicle Setup* ⇒ *Analyze Tools* ⇒ *MAVLink Console*.
+2. Go to the QGroundControl menu ⇒ *Analyze Tools* ⇒ *MAVLink Console*.
@@ -337,7 +337,7 @@ To setup ESP-NOW communication:
1. Flash the second ESP32 board with ESP-NOW proxy sketch: [`tools/espnow-proxy/espnow-proxy.ino`](../tools/espnow-proxy/espnow-proxy.ino). Use Arduino IDE or command line: `make upload_proxy`.
-2. Open Serial Monitor or use `make monitor` command. The ESP32 will print its MAC address and generated encryption key, for example:
+2. Open Serial Monitor in Arduino IDE or use `make monitor` command. The ESP32 will print its MAC address and generated encryption key, for example:
```
espnow 7a:c8:e3:eb:bf:e9 &PiuSysxP9+$L&5E
@@ -356,7 +356,10 @@ To setup ESP-NOW communication:
* Type: Serial.
* Serial Port: choose the port of the proxy ESP32 board, e. g. `/dev/cu.usbserial-0001`.
* Baud Rate: 115200.
-5. Click *Save*. QGroundControl should connect to the drone using ESP-NOW and begin showing the telemetry.
+5. Click *Save*, click *Connect*. QGroundControl should connect to the drone using ESP-NOW and begin showing the telemetry.
+
+> [!TIP]
+> Make sure Arduino IDE is not running when using ESP-NOW proxy board, as it may block the serial port.
## Flight log
diff --git a/flix/parameters.ino b/flix/parameters.ino
index 0c7da90..6cf9070 100644
--- a/flix/parameters.ino
+++ b/flix/parameters.ino
@@ -10,7 +10,7 @@ extern int channelZero[16], channelMax[16];
extern int rollChannel, pitchChannel, throttleChannel, yawChannel, armedChannel, modeChannel;
extern int rcRxPin, voltagePin;
extern int wifiMode, wifiLongRange, udpLocalPort, udpRemotePort, espnowChannel;
-extern float rcLossTimeout, descendTime;
+extern float rcLossTimeout, descendTime, disarmTilt;
extern float voltageScale;
extern LowPassFilter voltageFilter;
@@ -153,6 +153,7 @@ Parameter parameters[] = {
// safety
{"SF_RC_LOSS_TIME", &rcLossTimeout},
{"SF_DESCEND_TIME", &descendTime},
+ {"SF_DISARM_TILT", &disarmTilt},
};
void setupParameters() {
diff --git a/flix/safety.ino b/flix/safety.ino
index 05fd758..4e7e9dc 100644
--- a/flix/safety.ino
+++ b/flix/safety.ino
@@ -8,10 +8,12 @@ extern float controlRoll, controlPitch, controlThrottle, controlYaw;
float rcLossTimeout = 1;
float descendTime = 10;
+float disarmTilt = radians(120);
void failsafe() {
rcLossFailsafe();
autoFailsafe();
+ tiltFailsafe();
}
// RC loss failsafe
@@ -36,7 +38,7 @@ void descend() {
// Allow pilot to interrupt automatic flight
void autoFailsafe() {
static float roll, pitch, yaw, throttle;
- if (roll != controlRoll || pitch != controlPitch || yaw != controlYaw || abs(throttle - controlThrottle) > 0.05) {
+ if (abs(roll - controlRoll) > 0.05 || abs(pitch - controlPitch) > 0.05 || abs(yaw - controlYaw) > 0.05 || abs(throttle - controlThrottle) > 0.05) {
// controls changed and mode switch is not configured
if (mode == AUTO && invalid(controlMode)) mode = STAB; // regain control by the pilot
}
@@ -45,3 +47,15 @@ void autoFailsafe() {
yaw = controlYaw;
throttle = controlThrottle;
}
+
+// Disarm if tilted too much
+void tiltFailsafe() {
+ if (!armed) return;
+ if (mode != STAB) return;
+
+ Vector up = Quaternion::rotateVector(Vector(0, 0, 1), attitude);
+ float tilt = acos(up.z);
+ if (disarmTilt && tilt > disarmTilt) {
+ armed = false;
+ }
+}
diff --git a/flix/wifi.ino b/flix/wifi.ino
index eea9d8a..f316f83 100644
--- a/flix/wifi.ino
+++ b/flix/wifi.ino
@@ -58,7 +58,7 @@ void sendWiFi(const uint8_t *buf, int len) {
espnow.write(buf, len);
static Rate discovery(2);
- if (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;
}
diff --git a/gazebo/flix.h b/gazebo/flix.h
index a85e068..20afe52 100644
--- a/gazebo/flix.h
+++ b/gazebo/flix.h
@@ -76,6 +76,7 @@ void failsafe();
void rcLossFailsafe();
void descend();
void autoFailsafe();
+void tiltFailsafe();
int parametersCount();
const char *getParameterName(int index);
float getParameter(int index);