Skip to content
Merged
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
23 changes: 16 additions & 7 deletions .github/workflows/build_parallel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@ name: Build and Push Parallel

on:
workflow_dispatch:
push:
branches:
- master
tags:
- "*"
inputs:
create_release:
description: "Create the draft release after the full matrix passes"
required: true
type: boolean
default: true
pull_request:
types: [labeled]

concurrency:
group: full-hardware-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
compile_sketch:
name: build ${{ matrix.board.name }}
if: ${{ github.event_name == 'workflow_dispatch' || github.event.label.name == 'full-hardware-ci' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand All @@ -36,14 +43,16 @@ jobs:
- { name: "Marauder CYD 3.5inch", flag: "MARAUDER_CYD_3_5_INCH", fbqn: "esp32:esp32:d32:PartitionScheme=min_spiffs", file_name: "cyd_3_5_inch", tft: true, tft_file: "User_Setup_cyd_3_5_inch.h", build_dir: "d32", addr: "0x1000", idf_ver: "2.0.11", nimble_ver: "1.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "M5Cardputer", flag: "MARAUDER_CARDPUTER", fbqn: "esp32:esp32:esp32s3:PartitionScheme=min_spiffs,FlashSize=8M,PSRAM=disabled", file_name: "m5cardputer", tft: true, tft_file: "User_Setup_marauder_m5cardputer.h", build_dir: "esp32s3", addr: "0x1000", idf_ver: "2.0.11", nimble_ver: "1.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "M5Cardputer ADV", flag: "MARAUDER_CARDPUTER_ADV", fbqn: "esp32:esp32:esp32s3:PartitionScheme=min_spiffs,FlashSize=8M,PSRAM=disabled", file_name: "m5cardputer_adv", tft: true, tft_file: "User_Setup_marauder_m5cardputer_adv.h", build_dir: "esp32s3", addr: "0x1000", idf_ver: "2.0.11", nimble_ver: "1.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "ESP32-C5-DevKitC-1", flag: "MARAUDER_C5", fbqn: "esp32:esp32:esp32c5:FlashSize=8M,PartitionScheme=min_spiffs,PSRAM=enabled", file_name: "esp32c5devkitc1", tft: false, tft_file: "", build_dir: "esp32c5", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "ESP32-C5-DevKitC-1", flag: "MARAUDER_C5", fbqn: "esp32:esp32:esp32c5:FlashSize=8M,PartitionScheme=default_8MB,PSRAM=enabled", file_name: "esp32c5devkitc1", tft: false, tft_file: "", build_dir: "esp32c5", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "LilyGo T-Dongle C5", flag: "MARAUDER_T_DONGLE_C5", fbqn: "esp32:esp32:esp32c5:CDCOnBoot=cdc,FlashMode=qio,FlashSize=16M,PartitionScheme=custom,PSRAM=enabled", file_name: "t_dongle_c5", tft: true, tft_file: "User_Setup_marauder_t_dongle_c5.h", tft_repo: "H4W9/TFT_eSPI", tft_ref: "ESP32-C5", build_dir: "esp32c5", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "M5NanoC6", flag: "MARAUDER_M5_NANO_C6", fbqn: "esp32:esp32:esp32c6:CDCOnBoot=cdc,PartitionScheme=min_spiffs", file_name: "m5nanoc6", tft: false, tft_file: "", build_dir: "esp32c6", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "Marauder Pancake", flag: "MARAUDER_PANCAKE", fbqn: "esp32:esp32:esp32c5:FlashSize=8M,PartitionScheme=default_8MB,PSRAM=enabled", file_name: "pancake", tft: true, tft_file: "User_Setup_marauder_pancake.h", build_dir: "esp32c5", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master", tft_repo: "H4W9/TFT_eSPI", tft_ref: "ESP32-C5" }

steps:
- name: Checkout Code
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Install Arduino CLI
run: |
Expand Down Expand Up @@ -286,7 +295,7 @@ jobs:
name: Create Release
runs-on: ubuntu-latest
needs: [compile_sketch]
if: ${{ github.event_name == 'workflow_dispatch' }}
if: ${{ github.event_name == 'workflow_dispatch' && inputs.create_release }}
permissions:
contents: write
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightly_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ jobs:
- { name: "Marauder CYD 3.5inch", flag: "MARAUDER_CYD_3_5_INCH", fbqn: "esp32:esp32:d32:PartitionScheme=min_spiffs", file_name: "cyd_3_5_inch", tft: true, tft_file: "User_Setup_cyd_3_5_inch.h", build_dir: "d32", addr: "0x1000", idf_ver: "2.0.11", nimble_ver: "1.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "M5Cardputer", flag: "MARAUDER_CARDPUTER", fbqn: "esp32:esp32:esp32s3:PartitionScheme=min_spiffs,FlashSize=8M,PSRAM=disabled", file_name: "m5cardputer", tft: true, tft_file: "User_Setup_marauder_m5cardputer.h", build_dir: "esp32s3", addr: "0x1000", idf_ver: "2.0.11", nimble_ver: "1.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "M5Cardputer ADV", flag: "MARAUDER_CARDPUTER_ADV", fbqn: "esp32:esp32:esp32s3:PartitionScheme=min_spiffs,FlashSize=8M,PSRAM=disabled", file_name: "m5cardputer_adv", tft: true, tft_file: "User_Setup_marauder_m5cardputer_adv.h", build_dir: "esp32s3", addr: "0x1000", idf_ver: "2.0.11", nimble_ver: "1.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "ESP32-C5-DevKitC-1", flag: "MARAUDER_C5", fbqn: "esp32:esp32:esp32c5:FlashSize=8M,PartitionScheme=min_spiffs,PSRAM=enabled", file_name: "esp32c5devkitc1", tft: false, tft_file: "", build_dir: "esp32c5", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "ESP32-C5-DevKitC-1", flag: "MARAUDER_C5", fbqn: "esp32:esp32:esp32c5:FlashSize=8M,PartitionScheme=default_8MB,PSRAM=enabled", file_name: "esp32c5devkitc1", tft: false, tft_file: "", build_dir: "esp32c5", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "LilyGo T-Dongle C5", flag: "MARAUDER_T_DONGLE_C5", fbqn: "esp32:esp32:esp32c5:CDCOnBoot=cdc,FlashMode=qio,FlashSize=16M,PartitionScheme=custom,PSRAM=enabled", file_name: "t_dongle_c5", tft: true, tft_file: "User_Setup_marauder_t_dongle_c5.h", tft_repo: "H4W9/TFT_eSPI", tft_ref: "ESP32-C5", build_dir: "esp32c5", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }
- { name: "M5NanoC6", flag: "MARAUDER_M5_NANO_C6", fbqn: "esp32:esp32:esp32c6:CDCOnBoot=cdc,PartitionScheme=min_spiffs", file_name: "m5nanoc6", tft: false, tft_file: "", build_dir: "esp32c6", addr: "0x2000", idf_ver: "3.3.4", nimble_ver: "2.3.8", esp_async: "bigbrodude6119/ESPAsyncWebServer", esp_async_ver: "master" }

Expand Down
39 changes: 39 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Development workflow

ESP32 Marauder supports many hardware targets, so the full firmware matrix is
reserved for final validation and releases. It is not an iteration loop.

## Iteration

1. Work on a local branch without opening a pull request.
2. Run native unit tests and static checks locally.
3. Compile only the hardware targets requested for physical testing.
4. Clearly identify locally compiled test binaries by target and exact commit.
5. Revise locally until the requested hardware behavior is accepted.

## Pull request gate

Push the branch and open a pull request only when the change is ready for
review. Pull requests run the comparatively inexpensive native unit-test
workflow. Additional commits should be batched instead of pushed after every
small edit.

## Full-matrix gate

When a pull request is merge-ready, a maintainer applies the
`full-hardware-ci` label. That label runs **Build and Push Parallel** against the
pull request's exact current head. Any later source push invalidates that
evidence, so the label must be removed and applied again to validate the new
head before merge.

The workflow can also be dispatched manually against a final candidate branch
with `create_release` disabled. Do not use the full matrix for ordinary
iteration or to obtain one or two test binaries.

Untrusted fork code must not receive protected-build credentials. Private V8,
Mini V3, and Dual Mini C5 validation is performed only after review, from an
exact-head branch controlled by the maintainers.

For a firmware release, dispatch the same workflow against the exact release
source commit with `create_release` enabled. The workflow remains the
authoritative producer of the draft release and its public binaries.
22 changes: 12 additions & 10 deletions esp32_marauder/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ namespace {
#endif

void CommandLine::RunSetup() {
Serial.println(this->ascii_art);
#ifndef MARAUDER_V8
Serial.println(this->ascii_art);
#endif

Serial.println(F("\n\n--------------------------------\n"));
Serial.println(F(" ESP32 Marauder \n"));
Expand Down Expand Up @@ -275,11 +277,11 @@ void CommandLine::runCommand(String input) {
Serial.println(HELP_UPDATE_CMD_A);
Serial.println(HELP_LS_CMD);
// GCOVR_EXCL_START -- hardware-only command help entry.
Serial.println(PROTOCOL_INFO_CMD);
Serial.println(HELP_PROTOCOL_INFO_CMD);
#ifdef HAS_SD
Serial.println(BACKUP_SPIFFS_CMD);
Serial.println(BACKUP_STATUS_CMD);
Serial.println(RESTORE_SPIFFS_CMD);
Serial.println(HELP_BACKUP_SPIFFS_CMD);
Serial.println(HELP_BACKUP_STATUS_CMD);
Serial.println(HELP_RESTORE_SPIFFS_CMD);
#endif
// GCOVR_EXCL_STOP
Serial.println(HELP_LED_CMD);
Expand Down Expand Up @@ -1410,11 +1412,11 @@ void CommandLine::runCommand(String input) {
this->startScanFromCLI(WIFI_PING_SCAN, TFT_GREEN, "Ping Scan");
}

#ifndef HAS_DUAL_BAND
if (cmd_args.get(0) == ARP_SCAN_CMD) {
this->startScanFromCLI(WIFI_ARP_SCAN, TFT_CYAN, "ARP Scan");
}
#endif
// ARP discovery uses the active station netif on both legacy and C5
// dual-band hardware.
if (cmd_args.get(0) == ARP_SCAN_CMD) {
this->startScanFromCLI(WIFI_ARP_SCAN, TFT_CYAN, "ARP Scan");
}

// GPS POI
if (cmd_args.get(0) == GPS_POI_CMD) {
Expand Down
8 changes: 7 additions & 1 deletion esp32_marauder/CommandLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ const char PROGMEM HELP_REBOOT_CMD[] = "reboot";
const char PROGMEM HELP_UPDATE_CMD_A[] = "update -s/-w";
const char PROGMEM HELP_SETTINGS_CMD[] = "settings [-s <setting> enable/disable>]/[-r]";
const char PROGMEM HELP_LS_CMD[] = "ls <directory>";
const char PROGMEM HELP_PROTOCOL_INFO_CMD[] = "protocolinfo [--machine <transaction-id>]";
const char PROGMEM HELP_BACKUP_SPIFFS_CMD[] = "backupspiffs [--machine <transaction-id>] - copy SPIFFS to /spiffs on SD";
const char PROGMEM HELP_BACKUP_STATUS_CMD[] = "backupstatus [--machine <transaction-id>] - inspect /spiffs on SD";
const char PROGMEM HELP_RESTORE_SPIFFS_CMD[] = "restorespiffs [--machine <transaction-id>] - restore SPIFFS from /spiffs on SD";
const char PROGMEM HELP_LED_CMD[] = "led -s <hex color>/-p <rainbow>";
const char PROGMEM HELP_GPS_DATA_CMD[] = "gpsdata";
const char PROGMEM HELP_GPS_CMD[] = "gps [-t] [-g] <fix/sat/lon/lat/alt/date/accuracy/text/nmea>\r\n [-n] <native/all/gps/glonass/galileo/navic/qzss/beidou>\r\n [-b = use BD vs GB for beidou]";
Expand Down Expand Up @@ -221,7 +225,6 @@ class CommandLine {
LinkedList<String> parseCommand(String input, char* delim);
String toLowerCase(String str);
void filterAccessPoints(String filter);
void runCommand(String input);
bool checkValueExists(LinkedList<String>* cmd_args_list, int index);
bool inRange(int max, int index);
//bool apSelected();
Expand All @@ -230,6 +233,7 @@ class CommandLine {
int argSearch(LinkedList<String>* cmd_args, const char* key);
void startScanFromCLI(int scan_mode, uint16_t color, const char* scan_name);

#ifndef MARAUDER_V8
const char* ascii_art =
"\r\n"
" @@@@@@ \r\n"
Expand All @@ -256,11 +260,13 @@ class CommandLine {
" @@@@@@ \r\n"
" @@@@ \r\n"
"\r\n";
#endif

public:

void RunSetup();
void main(uint32_t currentTime);
void runCommand(String input);
};

#endif
43 changes: 43 additions & 0 deletions esp32_marauder/IPv4Range.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "IPv4Range.h"

namespace marauder {

namespace {

bool isContiguousMask(uint32_t mask) {
const uint32_t inverted = ~mask;
return (inverted & (inverted + 1U)) == 0U;
}

} // namespace

IPv4HostRange ipv4HostRange(uint32_t address, uint32_t subnetMask) {
const uint32_t network = address & subnetMask;
const uint32_t broadcast = network | ~subnetMask;
const bool valid = isContiguousMask(subnetMask) &&
(broadcast - network) >= 2U;

return {network, broadcast, valid ? network + 1U : 0U,
valid ? broadcast - 1U : 0U, valid};
}

uint32_t nextIPv4Host(uint32_t current, const IPv4HostRange& range) {
if (!range.valid || current >= range.last) {
return 0U;
}
if (current < range.first) {
return range.first;
}
return current + 1U;
}

uint32_t previousIPv4Host(uint32_t current, uint32_t steps,
const IPv4HostRange& range) {
if (!range.valid || current < range.first || current > range.last ||
steps > current - range.first) {
return 0U;
}
return current - steps;
}

} // namespace marauder
20 changes: 20 additions & 0 deletions esp32_marauder/IPv4Range.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <cstdint>

namespace marauder {

struct IPv4HostRange {
uint32_t network;
uint32_t broadcast;
uint32_t first;
uint32_t last;
bool valid;
};

IPv4HostRange ipv4HostRange(uint32_t address, uint32_t subnetMask);
uint32_t nextIPv4Host(uint32_t current, const IPv4HostRange& range);
uint32_t previousIPv4Host(uint32_t current, uint32_t steps,
const IPv4HostRange& range);

} // namespace marauder
24 changes: 17 additions & 7 deletions esp32_marauder/MenuFunctions.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "MenuFunctions.h"
#include "CommandLine.h"
#include "lang_var.h"

#ifdef HAS_SCREEN
Expand All @@ -9,6 +10,7 @@ extern LinkedList<Station>* stations;
extern LinkedList<AirTag>* airtags;
extern LinkedList<Flipper>* flippers;
extern LinkedList<BleDevice>* ble_devices;
extern CommandLine cli_obj;

#ifdef HAS_MINI_SCREEN
void MenuFunctions::drawMiniMenuButton(int b, int x, bool selected) {
Expand Down Expand Up @@ -2002,13 +2004,13 @@ void MenuFunctions::RunSetup()
this->drawStatusBar();
wifi_scan_obj.StartScan(WIFI_PING_SCAN, TFT_CYAN);
});
#ifndef HAS_DUAL_BAND
this->addNodes(&wifiScannerMenu, "ARP Scan", TFTCYAN, SCANNERS, [this]() {
display_obj.clearScreen();
this->drawStatusBar();
wifi_scan_obj.StartScan(WIFI_ARP_SCAN, TFT_CYAN);
});
#endif
// The C5 implementation uses the active station netif with lwIP core
// locking, so dual-band hardware supports the same ARP scanner.
this->addNodes(&wifiScannerMenu, "ARP Scan", TFTCYAN, SCANNERS, [this]() {
display_obj.clearScreen();
this->drawStatusBar();
wifi_scan_obj.StartScan(WIFI_ARP_SCAN, TFT_CYAN);
});
this->addNodes(&wifiScannerMenu, "Port Scan All", TFTMAGENTA, BEACON_LIST, [this](){
// Add the back button
wifiIPMenu.list->clear();
Expand Down Expand Up @@ -3487,6 +3489,14 @@ void MenuFunctions::RunSetup()
this->changeMenu(&sdDeleteMenu, true);
});

this->addNodes(&deviceMenu, "Backup SPIFFS", TFTGREEN, SD_UPDATE, [this]() {
cli_obj.runCommand(BACKUP_SPIFFS_CMD);
});

this->addNodes(&deviceMenu, "Restore SPIFFS", TFTORANGE, SD_UPDATE, [this]() {
cli_obj.runCommand(RESTORE_SPIFFS_CMD);
});

}
#endif

Expand Down
2 changes: 1 addition & 1 deletion esp32_marauder/MenuFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ class MenuFunctions

void buildUploadFileMenu();
void setupSDFileList(bool update = false);
void buildSDFileMenu(bool update = false);
void buildSDFileMenu(bool update = false);
void displayMenuButtons();
uint16_t getColor(uint16_t color);
void drawAvgLine(int16_t value);
Expand Down
37 changes: 25 additions & 12 deletions esp32_marauder/TargetListSort.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "TargetListSort.h"

#include <algorithm>
#include <ctype.h>
#include <string.h>

Expand All @@ -14,6 +13,21 @@ int caseInsensitiveCompare(const char* left, const char* right) {
}
return (unsigned char)*left - (unsigned char)*right;
}

bool targetComesBefore(const TargetListItem& a, const TargetListItem& b,
TargetSortMode mode) {
switch (mode) {
case TargetSortMode::SIGNAL_DESC:
return a.rssi != b.rssi ? a.rssi > b.rssi
: caseInsensitiveCompare(a.name, b.name) < 0;
case TargetSortMode::NAME_ASC:
return caseInsensitiveCompare(a.name, b.name) < 0;
case TargetSortMode::CHANNEL_ASC:
return a.channel != b.channel ? a.channel < b.channel
: caseInsensitiveCompare(a.name, b.name) < 0;
}
return false;
}
} // namespace

bool targetListItemMatchesFilter(const TargetListItem& item, TargetFilterMode filter,
Expand All @@ -32,16 +46,15 @@ bool targetListItemMatchesFilter(const TargetListItem& item, TargetFilterMode fi
}

void sortTargetList(std::vector<TargetListItem>& items, TargetSortMode mode) {
std::stable_sort(items.begin(), items.end(), [mode](const TargetListItem& a, const TargetListItem& b) {
switch (mode) {
case TargetSortMode::SIGNAL_DESC:
return a.rssi != b.rssi ? a.rssi > b.rssi : caseInsensitiveCompare(a.name, b.name) < 0;
case TargetSortMode::NAME_ASC:
return caseInsensitiveCompare(a.name, b.name) < 0;
case TargetSortMode::CHANNEL_ASC:
return a.channel != b.channel ? a.channel < b.channel : caseInsensitiveCompare(a.name, b.name) < 0;
// Target lists are screen-sized. Stable insertion sort avoids pulling the
// large libstdc++ stable_sort implementation into constrained firmware.
for (size_t i = 1; i < items.size(); ++i) {
TargetListItem item = items[i];
size_t position = i;
while (position > 0 && targetComesBefore(item, items[position - 1], mode)) {
items[position] = items[position - 1];
--position;
}
return false;
});
items[position] = item;
}
}

Loading
Loading