Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,21 @@ pio run -t uploadfs
`pack-localhost-dev-firmware.sh` bundles the build output into the site's local
web flasher for development.

### ESP32-C3 Super Mini

The firmware also builds for the ESP32-C3 Super Mini (Nologo / Tenstar Robot
clones). The web flasher only ships ESP32-S3 images, so flash it from source:

```bash
pio run -e esp32c3-supermini -t upload
pio run -e esp32c3-supermini -t uploadfs
```

Wire the flight controller UART to pins **20 (RX)** and **21 (TX)**. The board
has a plain blue LED (GPIO8) instead of an RGB one, so status is shown by blink
pattern: slow blink = searching for the camera, double flash = someone is on the
config Wi-Fi, solid = camera connected, fast blink = recording.

Planning to contribute? See [CONTRIBUTING.md](CONTRIBUTING.md) for the dev setup,
and how to submit changes.

Expand Down
19 changes: 13 additions & 6 deletions firmware/data/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const OSD_FIELDS = ["Off", "Rec status", "Battery %", "Mode", "SD free", "Time left", "Resolution", "FPS"];
const CAM_TYPE_NAMES = ["DJI Osmo (Nano)", "DJI Action / 360", "GoPro"];
const OSMO_TYPE = 0, ACTION_TYPE = 1, GOPRO_TYPE = 2;
const CAM_TYPE_NAMES = ["DJI Osmo (Nano)", "DJI Action / 360", "GoPro", "DJI Action 2"];
const OSMO_TYPE = 0, ACTION_TYPE = 1, GOPRO_TYPE = 2, ACTION2_TYPE = 3;
const CH_MIN = 900, CH_MAX = 2100, STEP = 25, GAP = 25;
const PIPS = [900, 1000, 1200, 1500, 1800, 2000, 2100];

Expand Down Expand Up @@ -34,8 +34,11 @@ let chVals = [];
let boundMac = "";
let boundType = 0;

// Resolution and FPS aren't reported by the Osmo Nano
const osdFieldAvail = (k) => !(boundType === OSMO_TYPE && (k === 6 || k === 7));
// Resolution and FPS aren't reported by the Osmo Nano; the Action 2 reports only
// record status (from our own commands), battery and a fixed video mode
const osdFieldAvail = (k) =>
!(boundType === OSMO_TYPE && (k === 6 || k === 7)) &&
!(boundType === ACTION2_TYPE && k >= 4);

// ---- Tabs ----
document.querySelectorAll(".tab").forEach((t) =>
Expand Down Expand Up @@ -136,7 +139,8 @@ function renderBound() {
// Channels: hide functions the bound camera can't do.
if (cfg) cfg.modes.forEach((m, i) => {
if (m.name.startsWith("Preset")) $("mode" + i).hidden = boundType !== GOPRO_TYPE;
if (m.name === "Camera Mode") $("mode" + i).hidden = boundType === OSMO_TYPE;
if (m.name === "Camera Mode")
$("mode" + i).hidden = boundType === OSMO_TYPE || boundType === ACTION2_TYPE;
});
}

Expand Down Expand Up @@ -166,7 +170,10 @@ async function wizScan() {
else await sleep(500);
} catch (e) { await sleep(500); }
}
list = (list || []).filter((d) => d.type === wizType);
// All DJI backends see the same advert (the scan can't tell an Action 2 apart), so list
// every DJI camera for any DJI type
const isDji = (t) => t === OSMO_TYPE || t === ACTION_TYPE || t === ACTION2_TYPE;
list = (list || []).filter((d) => d.type === wizType || (isDji(d.type) && isDji(wizType)));
if (!list.length) {
$("wizList").innerHTML =
`<p class="hint">No matching camera found. Power it on, disconnect any phone app, then <a href="#" id="wizRetry">scan again</a>.</p>`;
Expand Down
1 change: 1 addition & 0 deletions firmware/data/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ <h2>Device</h2>
<button type="button" data-type="0">DJI Osmo (Nano)</button>
<button type="button" data-type="1">DJI Action / 360</button>
<button type="button" data-type="2">GoPro</button>
<button type="button" data-type="3">DJI Action 2</button>
</div>
<button type="button" class="wide wiz-cancel">Cancel</button>
</div>
Expand Down
25 changes: 19 additions & 6 deletions firmware/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@
[platformio]
default_envs = esp32s3-zero

; ESP32-S3FH4R2: 4 MB flash, 2 MB QUAD PSRAM, native USB.
[env:esp32s3-zero]
; --- Shared by every board ---
[env]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200

; --- Match the 4 MB FH4R2 chip (default board is 8 MB) ---
board_upload.flash_size = 4MB
board_build.flash_size = 4MB
board_build.partitions = huge_app.csv ; single 3 MB app (BLE+Wi-Fi is big; no OTA)
board_build.filesystem = littlefs ; web UI assets in data/ -> `pio run -t uploadfs`
board_build.flash_mode = dio
board_build.arduino.memory_type = dio_qspi ; DIO flash + QUAD PSRAM (never opi here)

build_flags =
-std=gnu++17
Expand All @@ -27,4 +24,20 @@ build_flags =
build_unflags = -std=gnu++11
lib_deps =
h2zero/NimBLE-Arduino@^2.2.0
bblanchon/ArduinoJson@^7.2.0
bblanchon/ArduinoJson@^7.2.0

; ESP32-S3FH4R2: 4 MB flash, 2 MB QUAD PSRAM, native USB.
[env:esp32s3-zero]
board = esp32-s3-devkitc-1 ; default board is 8 MB - flash size overridden above
board_build.arduino.memory_type = dio_qspi ; DIO flash + QUAD PSRAM (never opi here)
build_flags =
${env.build_flags}
-DBOARD_ESP32S3_ZERO

; ESP32-C3 Super Mini (Nologo / Tenstar Robot clones): ESP32-C3FH4 - 4 MB flash, no PSRAM,
; single RISC-V core, USB-Serial/JTAG on the USB-C port.
[env:esp32c3-supermini]
board = nologo_esp32c3_super_mini
build_flags =
${env.build_flags}
-DBOARD_ESP32C3_SUPERMINI
40 changes: 37 additions & 3 deletions firmware/src/StatusLed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
void StatusLed::begin() {
if (pin_ < 0)
return;
write(0, 0, 0);
if (rgb_) {
write(0, 0, 0);
} else {
pinMode(pin_, OUTPUT);
writeMono(false);
}
}

void StatusLed::write(uint8_t r, uint8_t g, uint8_t b) {
if (pin_ < 0)
return;
if (r == lr_ && g == lg_ && b == lb_)
return;
lr_ = r;
Expand All @@ -19,11 +22,24 @@ void StatusLed::write(uint8_t r, uint8_t g, uint8_t b) {
neopixelWrite(pin_, g, r, b);
}

void StatusLed::writeMono(bool on) {
if (lon_ == (int8_t)on)
return;
lon_ = on;
digitalWrite(pin_, on != activeLow_ ? HIGH : LOW);
}

void StatusLed::update(bool camOnline, bool recording, bool wifiClient) {
if (pin_ < 0)
return;
const uint32_t now = millis();
if (rgb_)
updateRgb(camOnline, recording, wifiClient, now);
else
updateMono(camOnline, recording, wifiClient, now);
}

void StatusLed::updateRgb(bool camOnline, bool recording, bool wifiClient, uint32_t now) {
uint8_t r, g, b;
if (camOnline) {
if (recording) {
Expand Down Expand Up @@ -53,3 +69,21 @@ void StatusLed::update(bool camOnline, bool recording, bool wifiClient) {

write(r, g, b);
}

// Same states as updateRgb(), mapped onto blink rhythms for a single-colour LED
void StatusLed::updateMono(bool camOnline, bool recording, bool wifiClient, uint32_t now) {
bool on;
if (camOnline) {
// fast blink = recording, solid = camera connected, idle
on = recording ? (now / 125) % 2 : true;
} else if (wifiClient) {
// double flash = a device is connected to the config Wi-Fi (camera offline)
const uint32_t t = now % 1500;
on = t < 100 || (t >= 250 && t < 350);
} else {
// slow blink = searching for the camera
on = (now / 500) % 2;
}

writeMono(on);
}
10 changes: 9 additions & 1 deletion firmware/src/StatusLed.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@

class StatusLed {
public:
explicit StatusLed(int pin) : pin_(pin) {
// rgb: addressable WS2812 (states told apart by colour). Otherwise a plain single-colour
// LED on a GPIO, where the states are told apart by blink pattern instead
StatusLed(int pin, bool rgb, bool activeLow) : pin_(pin), rgb_(rgb), activeLow_(activeLow) {
}
void begin();
void update(bool camOnline, bool recording, bool wifiClient);

private:
void updateRgb(bool camOnline, bool recording, bool wifiClient, uint32_t now);
void updateMono(bool camOnline, bool recording, bool wifiClient, uint32_t now);
void write(uint8_t r, uint8_t g, uint8_t b);
void writeMono(bool on);

int pin_;
bool rgb_;
bool activeLow_;
uint8_t lr_ = 1, lg_ = 1, lb_ = 1;
int8_t lon_ = -1;
};
Loading
Loading