Add Bootloader

This commit is contained in:
Jonas Niesner
2025-07-04 20:51:52 +02:00
committed by GitHub
parent dd07094be9
commit b8f491e0f6
10 changed files with 11844 additions and 59 deletions

View File

@@ -5,29 +5,17 @@
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
[
"0x239A",
"0x8029"
],
[
"0x239A",
"0x0029"
],
[
"0x239A",
"0x002A"
],
[
"0x239A",
"0x802A"
]
],
"usb_product": "Feather nRF52840 Express",
"usb_product": "NRF52 DK",
"mcu": "nrf52840",
"variant": "feather_nrf52840_express",
"variant": "pca10056",
"bsp": {
"name": "adafruit"
},
@@ -46,30 +34,29 @@
],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": [
"jlink"
],
"svd_path": "nrf52840.svd"
},
"frameworks": [
"arduino",
"zephyr"
"arduino"
],
"name": "Adafruit Feather nRF52840 Express",
"name": "Nordic nRF52840-DK (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"require_upload_port": true,
"speed": 115200,
"protocol": "nrfutil",
"protocol": "jlink",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink",
"cmsis-dap",
"blackmagic"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
]
},
"url": "https://www.adafruit.com/product/4062",
"vendor": "Adafruit"
"url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/",
"vendor": "Nordic"
}

View File

@@ -0,0 +1,19 @@
import os
Import("env")
custom_hex_path = env.subst(os.path.join("$BUILD_DIR", "production.hex"))
env.Replace(
UPLOADER="JLinkExe",
UPLOADCMD=f"JLinkExe -device nrf52840_xxaa -if swd -speed 4000 -autoconnect 1 -CommanderScript upload.jlink"
)
# Auto-generate the JLink command file from script if needed:
with open("upload.jlink", "w") as f:
f.write(f"""r
loadfile {custom_hex_path}
r
g
exit
""")

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,35 @@
import os
Import("env")
def generate_production_binary(source, target, env):
signature_bin = "data/signature.bin"
firmware_hex = "$BUILD_DIR/${PROGNAME}.hex"
bootloader_hex = "data/bootloader.hex"
production_image = os.path.join("$BUILD_DIR", "production.hex")
dfu_package = os.path.join("$BUILD_DIR", "dfu.zip")
merge_tool = os.path.join(
env.PioPlatform().get_package_dir("tool-sreccat") or "", "srec_cat"
)
if not os.path.isfile(env.subst(production_image)):
assert os.path.isfile(bootloader_hex), "Missing bootloader image!"
assert os.path.isfile(signature_bin), "Missing signature file!"
env.Execute(
env.VerboseAction(
f'"{merge_tool}" "{bootloader_hex}" -intel "{signature_bin}" -Binary -offset 0xFF000 "{firmware_hex}" -intel -offset 0x00 -o "{production_image}" -intel',
f"Generating production image {production_image}",
)
)
# Generate DFU package
if not os.path.isfile(env.subst(dfu_package)):
env.Execute(
env.VerboseAction(
f'adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application "{firmware_hex}" "{dfu_package}"',
f"Generating DFU package {dfu_package}",
)
)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", generate_production_binary)

View File

@@ -19,3 +19,6 @@ lib_compat_mode = soft
upload_protocol = jlink
debug_tool = jlink
monitor_port = socket://localhost:19021
extra_scripts =
generate_production_image.py
custom_upload.py

View File

@@ -4,15 +4,16 @@
ModemConfig deviceConfig;
//set if config should be saved to modem
bool saveConfig = false;
bool saveConfig = true;
void setup() {
rtt.trimDownBufferFull();
rtt.println("|---------Starting setup---------|");
rtt.println("|x-------XStarting setupX-------x|");
gpioinit();
sensorData.wakeup_reason = NRF_POWER->RESETREAS;
delay(200);
initializeModem();
sensorpwr(true);
blinkLED(1, 200, 200, true);
//checkModemStorageSpace();
LoadDeviceConfig();
@@ -24,7 +25,6 @@ void setup() {
void loop() {
lastWaitCheck = millis();
rtt.println("|---------Starting loop---------|");
sensorpwr(true);
rtt.println("Collecting sensor data");
collectAllSensorData();
rtt.print("WD counter ");
@@ -38,9 +38,10 @@ void loop() {
ScanForWiFiNetworks();
rtt.println("Managing modem lifecycle");
manageModemLifecycle();
sensorpwr(false);
rtt.println("Waiting for next transmission");
rtt.println("Loop cycle completed.");
globalMotionDetected = false;
readAccelerometers(lastAcc1X, lastAcc1Y, lastAcc1Z, lastAcc2X, lastAcc2Y, lastAcc2Z);
while (!waitForNextTransmission(deviceConfig.transmission_interval)) {
performBackgroundTasks();
}
@@ -570,6 +571,8 @@ bool readAccelerometers(float& acc1_x, float& acc1_y, float& acc1_z, float& acc2
if (!acc1.begin() || !acc2.begin()) {
return false;
}
acc1.setRange(LIS3DH_RANGE_2_G);
acc2.setRange(LIS3DH_RANGE_2_G);
acc1.read();
acc2.read();
acc1_x = acc1.x;
@@ -919,8 +922,6 @@ bool sendSensorDataToServer(const SensorData& data) {
void manageModemLifecycle() {
unsigned long currentTime = millis();
if (lastTransmissionTime == 0 || (currentTime - lastTransmissionTime >= deviceConfig.transmission_interval)) {
rtt.flush();
rtt.println("Transmitting sensor data...");
if (!initializeModemIfNeeded()) {
rtt.println("Failed to initialize modem");
@@ -969,10 +970,15 @@ void manageModemLifecycle() {
// Shutdown modem between loops if configured
if (deviceConfig.modem_shutdown_between_loops) {
rtt.println("Shutting down modem between loops...");
shutdownModem();
// Check if we should keep modem on due to motion
if (shouldKeepModemPowered()) {
rtt.println("Keeping modem powered on due to motion or configuration");
logPowerManagementStatus();
} else {
rtt.println("Shutting down modem between loops...");
shutdownModem();
}
}
}
}
bool readModemInfo() {
@@ -1424,6 +1430,7 @@ bool saveConfigToModem(const ModemConfig& config) {
DynamicJsonDocument doc(16000);
doc["device_name"] = config.device_name;
doc["transmission_interval"] = config.transmission_interval;
doc["transmission_interval_motion"] = config.transmission_interval_motion;
doc["enable_gps"] = config.enable_gps;
doc["gps_timeout"] = config.gps_timeout;
doc["server_url"] = config.server_url;
@@ -1440,6 +1447,9 @@ bool saveConfigToModem(const ModemConfig& config) {
doc["ble_scan_max_beacons"] = config.ble_scan_max_beacons;
doc["ble_scan_duration"] = config.ble_scan_duration;
doc["modem_shutdown_between_loops"] = config.modem_shutdown_between_loops;
doc["motion_detection_enabled"] = config.motion_detection_enabled;
doc["motion_threshold"] = config.motion_threshold;
doc["modem_shutdown_on_motion"] = config.modem_shutdown_on_motion;
String configJson = "";
serializeJson(doc, configJson);
delay(10);
@@ -1493,6 +1503,9 @@ bool loadConfigFromModem(ModemConfig& config) {
if (doc.containsKey("transmission_interval")) {
config.transmission_interval = doc["transmission_interval"].as<unsigned long>();
}
if (doc.containsKey("transmission_interval_motion")) {
config.transmission_interval_motion = doc["transmission_interval_motion"].as<unsigned long>();
}
if (doc.containsKey("enable_gps")) {
config.enable_gps = doc["enable_gps"].as<bool>();
}
@@ -1541,6 +1554,15 @@ bool loadConfigFromModem(ModemConfig& config) {
if (doc.containsKey("modem_shutdown_between_loops")) {
config.modem_shutdown_between_loops = doc["modem_shutdown_between_loops"].as<bool>();
}
if (doc.containsKey("motion_detection_enabled")) {
config.motion_detection_enabled = doc["motion_detection_enabled"].as<bool>();
}
if (doc.containsKey("motion_threshold")) {
config.motion_threshold = doc["motion_threshold"].as<float>();
}
if (doc.containsKey("modem_shutdown_on_motion")) {
config.modem_shutdown_on_motion = doc["modem_shutdown_on_motion"].as<bool>();
}
//rtt.println("Configuration loaded successfully");
return true;
}
@@ -1548,6 +1570,7 @@ bool loadConfigFromModem(ModemConfig& config) {
void printConfig(const ModemConfig& config) {
rtt.print("Device Name: "); rtt.println(config.device_name);
rtt.print("Transmission Interval: "); rtt.print(config.transmission_interval); rtt.println(" ms");
rtt.print("Transmission Interval (Motion): "); rtt.print(config.transmission_interval_motion); rtt.println(" ms");
rtt.print("GPS Enabled: "); rtt.println(config.enable_gps ? "Yes" : "No");
rtt.print("GPS Timeout: "); rtt.print(config.gps_timeout); rtt.println(" ms");
rtt.print("WiFi Scan Enabled: "); rtt.println(config.wifi_scan_enabled ? "Yes" : "No");
@@ -1556,6 +1579,9 @@ void printConfig(const ModemConfig& config) {
rtt.print("BLE Max Beacons: "); rtt.println(config.ble_scan_max_beacons);
rtt.print("BLE Scan Duration: "); rtt.print(config.ble_scan_duration); rtt.println(" ms");
rtt.print("Modem Shutdown Between Loops: "); rtt.println(config.modem_shutdown_between_loops ? "Yes" : "No");
rtt.print("Motion Detection Enabled: "); rtt.println(config.motion_detection_enabled ? "Yes" : "No");
rtt.print("Motion Threshold: "); rtt.print(config.motion_threshold, 3); rtt.println(" g");
rtt.print("Modem Shutdown On Motion: "); rtt.println(config.modem_shutdown_on_motion ? "Yes" : "No");
rtt.print("Server URL: "); rtt.println(config.server_url);
rtt.print("Server Endpoint: "); rtt.println(config.server_endpoint);
rtt.print("Server Port: "); rtt.println(config.server_port);
@@ -2182,8 +2208,13 @@ void optimizeScanData(DynamicJsonDocument& doc, size_t maxSize) {
bool waitForNextTransmission(unsigned long interval) {
unsigned long currentTime = millis();
// Choose interval based on motion detection
unsigned long effectiveInterval = interval;
if (deviceConfig.motion_detection_enabled && globalMotionDetected) {
effectiveInterval = deviceConfig.transmission_interval_motion;
}
// Check if it's time for the next transmission
if (currentTime - lastWaitCheck >= interval) {
if (currentTime - lastWaitCheck >= effectiveInterval) {
lastWaitCheck = currentTime;
return true; // Time to transmit
}
@@ -2199,20 +2230,39 @@ void performBackgroundTasks() {
lastBatteryCheck = millis();
}
// Check for any pending modem operations
if (modemInitialized && !modem.isGprsConnected()) {
// Try to reconnect if disconnected
static unsigned long lastReconnectAttempt = 0;
if (millis() - lastReconnectAttempt > 60000) { // Try every minute
rtt.println("Attempting to reconnect to network...");
connectToNetwork();
lastReconnectAttempt = millis();
if (deviceConfig.motion_detection_enabled && millis() - lastMotionCheck > 100) {
// Read current accelerometer values
if (readAccelerometers(currentAcc1X, currentAcc1Y, currentAcc1Z, currentAcc2X, currentAcc2Y, currentAcc2Z)) {
// Calculate acceleration differences
float acc1Diff = sqrt(pow(currentAcc1X - lastAcc1X, 2) +
pow(currentAcc1Y - lastAcc1Y, 2) +
pow(currentAcc1Z - lastAcc1Z, 2));
float acc2Diff = sqrt(pow(currentAcc2X - lastAcc2X, 2) +
pow(currentAcc2Y - lastAcc2Y, 2) +
pow(currentAcc2Z - lastAcc2Z, 2));
// Check if motion is detected using configured threshold
if (acc1Diff > deviceConfig.motion_threshold || acc2Diff > deviceConfig.motion_threshold) {
if (!globalMotionDetected) {
globalMotionDetected = true;
globalLastMotionTime = millis();
rtt.println("Motion detected!");
rtt.print("Acc1 diff: "); rtt.print(acc1Diff, 3);
rtt.print(" Acc2 diff: "); rtt.println(acc2Diff, 3);
}
}
lastAcc1X = currentAcc1X;
lastAcc1Y = currentAcc1Y;
lastAcc1Z = currentAcc1Z;
lastAcc2X = currentAcc2X;
lastAcc2Y = currentAcc2Y;
lastAcc2Z = currentAcc2Z;
}
lastMotionCheck = millis();
}
rtt.println("Performing background tasks");
// Small delay to prevent busy waiting
delay(1000); // 1 second delay between background task checks
delay(10);
}
// Function to parse JSON response from server
@@ -2353,4 +2403,28 @@ bool initializeModemIfNeeded() {
return initializeModem();
}
return true;
}
// Function to check if modem should be kept powered on based on motion
bool shouldKeepModemPowered() {
if (!deviceConfig.modem_shutdown_between_loops) {
return true; // Always keep modem on if shutdown is disabled
}
if (deviceConfig.modem_shutdown_on_motion && globalMotionDetected) {
return true; // Keep modem on if motion detected and configured to do so
}
return false; // Shutdown modem normally
}
// Function to get current power management status
void logPowerManagementStatus() {
rtt.print("Power Management - Modem Shutdown: ");
rtt.print(deviceConfig.modem_shutdown_between_loops ? "Enabled" : "Disabled");
rtt.print(", Motion Shutdown: ");
rtt.print(deviceConfig.modem_shutdown_on_motion ? "Enabled" : "Disabled");
rtt.print(", Motion Detected: ");
rtt.print(globalMotionDetected ? "Yes" : "No");
rtt.print(", Keep Modem On: ");
rtt.println(shouldKeepModemPowered() ? "Yes" : "No");
}

View File

@@ -35,6 +35,7 @@ BLEBas blebas; // battery
struct ModemConfig {
String device_name;
unsigned long transmission_interval;
unsigned long transmission_interval_motion;
bool enable_gps;
unsigned long gps_timeout;
String server_url;
@@ -51,11 +52,15 @@ struct ModemConfig {
int ble_scan_max_beacons;
unsigned long ble_scan_duration;
bool modem_shutdown_between_loops;
bool motion_detection_enabled;
float motion_threshold;
bool modem_shutdown_on_motion;
ModemConfig() {
device_name = "Dev 1";
transmission_interval = 30000;
enable_gps = false;
transmission_interval = 3600000;
transmission_interval_motion = 60000;
enable_gps = true;
gps_timeout = 15000;
server_url = "api.64eng.de";
server_endpoint = "/sensordata.php";
@@ -66,16 +71,19 @@ struct ModemConfig {
enable_storage = true;
max_stored_files = 10;
wifi_scan_enabled = true;
wifi_scan_max_networks = 8;
wifi_scan_max_networks = 20;
ble_scan_enabled = true;
ble_scan_max_beacons = 9;
ble_scan_duration = 10000;
modem_shutdown_between_loops = false;
ble_scan_max_beacons = 20;
ble_scan_duration = 5000;
modem_shutdown_between_loops = true;
motion_detection_enabled = true;
motion_threshold = 500;
modem_shutdown_on_motion = false;
}
};
// BLE scanning variables
DynamicJsonDocument bleScanDoc(8000);
DynamicJsonDocument bleScanDoc(16000);
JsonArray bleBeacons;
bool bleScanning = false;
unsigned long bleScanStartTime = 0;
@@ -85,6 +93,17 @@ void ble_scan_callback(ble_gap_evt_adv_report_t* report);
void scanforbeacons();
bool initializeBLE();
// Global motion detection variables
static bool globalMotionDetected = false;
static unsigned long globalLastMotionTime = 0;
// Motion detection using accelerometers
static unsigned long lastMotionCheck = 0;
static float lastAcc1X = 0, lastAcc1Y = 0, lastAcc1Z = 0;
static float lastAcc2X = 0, lastAcc2Y = 0, lastAcc2Z = 0;
float currentAcc1X, currentAcc1Y, currentAcc1Z;
float currentAcc2X, currentAcc2Y, currentAcc2Z;
bool espmodemrail = 0;
bool espon = 0;
bool modemon = 0;
@@ -195,4 +214,12 @@ String readCompleteHttpResponse(HttpClient& http, unsigned long timeout);
// Non-blocking wait functions
bool waitForNextTransmission(unsigned long interval);
void performBackgroundTasks();
void performBackgroundTasks();
// Motion detection functions
bool isMotionDetected();
void triggerMotionTransmission();
// Power management functions
bool shouldKeepModemPowered();
void logPowerManagementStatus();

View File

@@ -0,0 +1,5 @@
r
loadfile /home/jonas/Desktop/dd/HGD4_reversed-main/NRF_firmware/.pio/build/hgd6/production.hex
r
g
exit

View File

@@ -50,9 +50,6 @@ extern "C"
#define LED_BUILTIN GREEN_LED
#define PIN_SERIAL_RX MODEM_RXD
#define PIN_SERIAL_TX MODEM_TXD
#define PIN_SERIAL1_RX MODEM_RXD
#define PIN_SERIAL1_TX MODEM_TXD