Keep power-saving sleep within queued work deadlines
This commit is contained in:
1 parent
6b12f4c0a4
commit
32e920250a
10 files changed
+193
-26
No files matched your search
@@ -265,6 +265,7 @@ jobs:
|
||||
python3 -B test/test_sensor_tracker_client.py -v
|
||||
python3 -B test/test_sensor_tracker_gps.py -v
|
||||
python3 -B test/test_sensor_tracker_sleep.py -v
|
||||
python3 -B test/test_power_saving_deadlines.py -v
|
||||
|
||||
- name: Verify Companion contact and channel TX routing
|
||||
run: |
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <helpers/FloodFilterPolicy.h>
|
||||
#include <helpers/FloodRuleCLI.h>
|
||||
#include <helpers/LazyPersistence.h>
|
||||
#include <helpers/PowerManagementUtils.h>
|
||||
#include <helpers/RegionNameUtils.h>
|
||||
#if MESH_PACKET_LOGGING
|
||||
#include <helpers/SerialPacketLog.h>
|
||||
@@ -5149,12 +5150,12 @@ uint32_t MyMesh::limitSleepToMillisTimer(unsigned long timestamp, uint32_t sleep
|
||||
if (!timestamp || sleep_secs == 0) {
|
||||
return sleep_secs;
|
||||
}
|
||||
unsigned long now = millis();
|
||||
if ((long)(now - timestamp) >= 0) {
|
||||
const uint32_t now = millis();
|
||||
if ((int32_t)(now - (uint32_t)timestamp) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
unsigned long remaining_ms = timestamp - now;
|
||||
uint32_t remaining_secs = (remaining_ms + 999UL) / 1000UL;
|
||||
const uint32_t remaining_ms = (uint32_t)timestamp - now;
|
||||
uint32_t remaining_secs = mesh::power::powerSaveSleepSeconds(remaining_ms);
|
||||
return remaining_secs < sleep_secs ? remaining_secs : sleep_secs;
|
||||
}
|
||||
|
||||
@@ -5184,12 +5185,12 @@ uint32_t MyMesh::getPowerSaveSleepSeconds(uint32_t max_secs) const {
|
||||
uint32_t sleep_secs = max_secs;
|
||||
uint32_t queue_delay_ms;
|
||||
if (getNextQueueWakeDelay(queue_delay_ms)) {
|
||||
uint32_t queue_delay_secs = (queue_delay_ms + 999UL) / 1000UL;
|
||||
uint32_t queue_delay_secs = mesh::power::powerSaveSleepSeconds(queue_delay_ms);
|
||||
if (queue_delay_secs < sleep_secs) sleep_secs = queue_delay_secs;
|
||||
}
|
||||
uint32_t retry_delay_ms;
|
||||
if (getNextRetryWakeDelay(retry_delay_ms)) {
|
||||
uint32_t retry_delay_secs = (retry_delay_ms + 999UL) / 1000UL;
|
||||
uint32_t retry_delay_secs = mesh::power::powerSaveSleepSeconds(retry_delay_ms);
|
||||
if (retry_delay_secs < sleep_secs) sleep_secs = retry_delay_secs;
|
||||
}
|
||||
sleep_secs = limitSleepToMillisTimer(next_flood_advert, sleep_secs);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <helpers/RoomClientPathCommand.h>
|
||||
#include <helpers/RoomCatchUp.h>
|
||||
#include <helpers/LazyPersistence.h>
|
||||
#include <helpers/PowerManagementUtils.h>
|
||||
#if MESH_PACKET_LOGGING
|
||||
#include <helpers/SerialPacketLog.h>
|
||||
#endif
|
||||
@@ -4185,12 +4186,12 @@ uint32_t MyMesh::limitSleepToMillisTimer(unsigned long timestamp, uint32_t sleep
|
||||
if (!timestamp || sleep_secs == 0) {
|
||||
return sleep_secs;
|
||||
}
|
||||
unsigned long now = millis();
|
||||
if ((long)(now - timestamp) >= 0) {
|
||||
const uint32_t now = millis();
|
||||
if ((int32_t)(now - (uint32_t)timestamp) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
unsigned long remaining_ms = timestamp - now;
|
||||
uint32_t remaining_secs = (remaining_ms + 999UL) / 1000UL;
|
||||
const uint32_t remaining_ms = (uint32_t)timestamp - now;
|
||||
uint32_t remaining_secs = mesh::power::powerSaveSleepSeconds(remaining_ms);
|
||||
return remaining_secs < sleep_secs ? remaining_secs : sleep_secs;
|
||||
}
|
||||
|
||||
@@ -4202,12 +4203,12 @@ uint32_t MyMesh::getPowerSaveSleepSeconds(uint32_t max_secs) const {
|
||||
uint32_t sleep_secs = max_secs;
|
||||
uint32_t queue_delay_ms;
|
||||
if (getNextQueueWakeDelay(queue_delay_ms)) {
|
||||
uint32_t queue_delay_secs = (queue_delay_ms + 999UL) / 1000UL;
|
||||
uint32_t queue_delay_secs = mesh::power::powerSaveSleepSeconds(queue_delay_ms);
|
||||
if (queue_delay_secs < sleep_secs) sleep_secs = queue_delay_secs;
|
||||
}
|
||||
uint32_t retry_delay_ms;
|
||||
if (getNextRetryWakeDelay(retry_delay_ms)) {
|
||||
uint32_t retry_delay_secs = (retry_delay_ms + 999UL) / 1000UL;
|
||||
uint32_t retry_delay_secs = mesh::power::powerSaveSleepSeconds(retry_delay_ms);
|
||||
if (retry_delay_secs < sleep_secs) sleep_secs = retry_delay_secs;
|
||||
}
|
||||
if (acl.getNumClients() > 0) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <helpers/ClientLoginPersistence.h>
|
||||
#include <helpers/ClientPathPersistence.h>
|
||||
#include <helpers/LazyPersistence.h>
|
||||
#include <helpers/PowerManagementUtils.h>
|
||||
#include <helpers/RoutingPolicy.h>
|
||||
#include <helpers/radiolib/RXPowerSaving.h>
|
||||
#include <helpers/sensors/LPPDataHelpers.h>
|
||||
@@ -1676,10 +1677,12 @@ uint32_t SensorMesh::limitSleepToMillisTimer(unsigned long timestamp,
|
||||
uint32_t sleep_secs) const {
|
||||
if (!timestamp || sleep_secs == 0) return sleep_secs;
|
||||
|
||||
unsigned long now = millis();
|
||||
if ((long)(now - timestamp) >= 0) return 0;
|
||||
const uint32_t now = millis();
|
||||
if ((int32_t)(now - (uint32_t)timestamp) >= 0) return 0;
|
||||
|
||||
uint32_t remaining_secs = (timestamp - now + 999UL) / 1000UL;
|
||||
// Keep the millis arithmetic 32-bit, including on native test hosts.
|
||||
const uint32_t remaining_ms = (uint32_t)timestamp - now;
|
||||
uint32_t remaining_secs = mesh::power::powerSaveSleepSeconds(remaining_ms);
|
||||
return remaining_secs < sleep_secs ? remaining_secs : sleep_secs;
|
||||
}
|
||||
|
||||
@@ -1725,11 +1728,11 @@ uint32_t SensorMesh::getPowerSaveSleepSeconds(uint32_t max_secs) const {
|
||||
#endif
|
||||
uint32_t wake_delay_ms;
|
||||
if (getNextQueueWakeDelay(wake_delay_ms)) {
|
||||
uint32_t wake_delay_secs = (wake_delay_ms + 999UL) / 1000UL;
|
||||
uint32_t wake_delay_secs = mesh::power::powerSaveSleepSeconds(wake_delay_ms);
|
||||
if (wake_delay_secs < sleep_secs) sleep_secs = wake_delay_secs;
|
||||
}
|
||||
if (getNextRetryWakeDelay(wake_delay_ms)) {
|
||||
uint32_t wake_delay_secs = (wake_delay_ms + 999UL) / 1000UL;
|
||||
uint32_t wake_delay_secs = mesh::power::powerSaveSleepSeconds(wake_delay_ms);
|
||||
if (wake_delay_secs < sleep_secs) sleep_secs = wake_delay_secs;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,18 @@
|
||||
namespace mesh {
|
||||
namespace power {
|
||||
|
||||
inline uint32_t powerSaveSleepSeconds(uint32_t delay_ms) {
|
||||
const uint32_t seconds = delay_ms / 1000UL;
|
||||
#if defined(NRF52_PLATFORM) || defined(RP2040_PLATFORM) || defined(STM32_PLATFORM)
|
||||
// These boards ignore seconds and idle until an interrupt. Keep sub-second
|
||||
// idle eligible; periodic clock interrupts service the remaining deadline.
|
||||
return seconds + (delay_ms % 1000UL != 0);
|
||||
#else
|
||||
// Timed sleepers must wake before a deadline, not round past its last fraction.
|
||||
return seconds;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint16_t medianVoltage(uint16_t a, uint16_t b, uint16_t c) {
|
||||
if (a > b) { uint16_t t = a; a = b; b = t; }
|
||||
if (b > c) { uint16_t t = b; b = c; c = t; }
|
||||
|
||||
@@ -24,6 +24,7 @@ FIXTURE = r'''
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <helpers/WebConfigBatch.h>
|
||||
#include <helpers/PowerManagementUtils.h>
|
||||
#include <helpers/WiFiNetworkPowerPolicy.h>
|
||||
#include <helpers/WirelessControl.h>
|
||||
uint32_t now = 0;
|
||||
|
||||
@@ -26,6 +26,7 @@ HARNESS = r'''
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <helpers/PowerManagementUtils.h>
|
||||
#define WITH_BRIDGE 1
|
||||
struct AbstractBridge { virtual bool isRunning() const = 0; };
|
||||
struct MQTTBridge : AbstractBridge {
|
||||
@@ -153,7 +154,7 @@ class LoggingSleepContractTest(unittest.TestCase):
|
||||
.replace("@METHODS@", methods))
|
||||
built = subprocess.run([
|
||||
os.environ.get("CXX", "c++"), "-std=c++17", "-Wall", "-Wextra",
|
||||
str(cpp), "-o", str(binary),
|
||||
"-I", str(ROOT / "src"), str(cpp), "-o", str(binary),
|
||||
], capture_output=True, text=True)
|
||||
self.assertEqual(built.returncode, 0, built.stderr)
|
||||
result = subprocess.run([str(binary)], capture_output=True, text=True)
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
"""Run real role deadlines while preserving timed sleep and interrupt idle."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from test_radio_receive_contract import method
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
HARNESS = r'''
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <helpers/PowerManagementUtils.h>
|
||||
#define SENSOR_READ_INTERVAL_SECS 60
|
||||
static uint32_t now_ms = 1000;
|
||||
uint32_t millis() { return now_ms; }
|
||||
struct Node {
|
||||
bool pending = false, queue = false, retry = false;
|
||||
uint32_t queue_ms = 0, retry_ms = 0;
|
||||
struct { bool battery_alert_enabled = false; } _prefs;
|
||||
struct { int getNumClients() const { return 0; } } acl;
|
||||
unsigned long next_flood_advert = 0, next_local_advert = 0;
|
||||
unsigned long dirty_contacts_expiry = 0, next_recent_repeater_sweep = 0;
|
||||
unsigned long next_battery_alert_check = 0, next_push = 0;
|
||||
unsigned long radio_apply_retry_at = 0, set_radio_at = 0, revert_radio_at = 0;
|
||||
uint32_t last_read_time = 100;
|
||||
struct Alert { unsigned long send_expiry = 0; } alert;
|
||||
Alert* alert_tasks[1] = {&alert};
|
||||
int num_alert_tasks = 0;
|
||||
struct Clock { uint32_t getCurrentTime() const { return 100; } } clock;
|
||||
const Clock* getRTCClock() const { return &clock; }
|
||||
bool hasPendingWork() const { return pending; }
|
||||
bool getNextQueueWakeDelay(uint32_t& out) const { out = queue_ms; return queue; }
|
||||
bool getNextRetryWakeDelay(uint32_t& out) const { out = retry_ms; return retry; }
|
||||
bool millisHasNowPassed(unsigned long at) const {
|
||||
return int32_t(now_ms - uint32_t(at)) > 0;
|
||||
}
|
||||
uint32_t limitSleepToScheduledRadioWork(uint32_t seconds) const { return seconds; }
|
||||
uint32_t limitSleepToMillisTimer(unsigned long, uint32_t) const;
|
||||
uint32_t getPowerSaveSleepSeconds(uint32_t) const;
|
||||
};
|
||||
@METHODS@
|
||||
int main() {
|
||||
Node node;
|
||||
assert(mesh::power::powerSaveSleepSeconds(0) == 0);
|
||||
#if EXPECT_INTERRUPT_IDLE
|
||||
assert(mesh::power::powerSaveSleepSeconds(UINT32_MAX) == 4294968U);
|
||||
#else
|
||||
assert(mesh::power::powerSaveSleepSeconds(UINT32_MAX) == 4294967U);
|
||||
#endif
|
||||
assert(node.getPowerSaveSleepSeconds(30) == 30);
|
||||
assert(node.getPowerSaveSleepSeconds(0) == 0);
|
||||
assert(node.limitSleepToMillisTimer(0, 30) == 30); // Disabled timer sentinel.
|
||||
assert(node.limitSleepToMillisTimer(1001, 0) == 0);
|
||||
node.pending = true;
|
||||
assert(node.getPowerSaveSleepSeconds(30) == 0);
|
||||
node.pending = false;
|
||||
for (uint32_t now : {1000U, UINT32_MAX - 15U}) {
|
||||
now_ms = now;
|
||||
// Timed sleep ends before the deadline. Event-driven boards instead permit
|
||||
// interrupt idle for every positive remainder, including across rollover.
|
||||
for (uint32_t ms : {0U, 1U, 999U, 1000U, 1001U, 1999U, 2000U,
|
||||
2500U, 29999U, 30000U, 30001U, 60000U}) {
|
||||
const uint32_t whole_seconds = ms / 1000;
|
||||
#if EXPECT_INTERRUPT_IDLE
|
||||
// Event-driven boards ignore seconds and wake at the next IRQ.
|
||||
const uint32_t idle_seconds = whole_seconds + (ms % 1000 != 0);
|
||||
#else
|
||||
const uint32_t idle_seconds = whole_seconds;
|
||||
#endif
|
||||
const uint32_t expected = idle_seconds < 30 ? idle_seconds : 30;
|
||||
node.queue = true; node.queue_ms = ms;
|
||||
assert(node.getPowerSaveSleepSeconds(30) == expected);
|
||||
node.queue = false; node.retry = true; node.retry_ms = ms;
|
||||
assert(node.getPowerSaveSleepSeconds(30) == expected);
|
||||
node.retry = false;
|
||||
const uint32_t deadline = now + ms;
|
||||
assert(deadline != 0); // Zero has a separate intentional sentinel meaning.
|
||||
assert(node.limitSleepToMillisTimer(deadline, 30) == expected);
|
||||
node.next_flood_advert = deadline;
|
||||
assert(node.getPowerSaveSleepSeconds(30) == expected);
|
||||
node.next_flood_advert = 0;
|
||||
}
|
||||
assert(node.limitSleepToMillisTimer(uint32_t(now - 1), 30) == 0);
|
||||
node.queue = true; node.queue_ms = 2500;
|
||||
node.retry = true; node.retry_ms = 1001;
|
||||
#if EXPECT_INTERRUPT_IDLE
|
||||
assert(node.getPowerSaveSleepSeconds(30) == 2); // Interrupt idle is permitted.
|
||||
#else
|
||||
assert(node.getPowerSaveSleepSeconds(30) == 1); // Earliest timed owner wins.
|
||||
#endif
|
||||
node.dirty_contacts_expiry = uint32_t(now + 999);
|
||||
#if EXPECT_INTERRUPT_IDLE
|
||||
assert(node.getPowerSaveSleepSeconds(30) == 1);
|
||||
#else
|
||||
assert(node.getPowerSaveSleepSeconds(30) == 0);
|
||||
#endif
|
||||
node.dirty_contacts_expiry = 0;
|
||||
node.queue = node.retry = false;
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
class PowerSavingDeadlineTests(unittest.TestCase):
|
||||
def test_real_role_deadlines_preserve_timed_and_interrupt_idle(self):
|
||||
with tempfile.TemporaryDirectory(prefix='meshcore-sleep-deadlines-') as directory:
|
||||
work = Path(directory)
|
||||
for role, filename, name in (
|
||||
('repeater', 'examples/simple_repeater/MyMesh.cpp', 'MyMesh'),
|
||||
('room', 'examples/simple_room_server/MyMesh.cpp', 'MyMesh'),
|
||||
('sensor', 'examples/simple_sensor/SensorMesh.cpp', 'SensorMesh')):
|
||||
for platform in ('', 'NRF52_PLATFORM', 'RP2040_PLATFORM', 'STM32_PLATFORM'):
|
||||
with self.subTest(role=role, platform=platform or 'timed'):
|
||||
source = (ROOT / filename).read_text()
|
||||
methods = '\n'.join(method(source, signature) for signature in (
|
||||
f'uint32_t {name}::limitSleepToMillisTimer(',
|
||||
f'uint32_t {name}::getPowerSaveSleepSeconds('))
|
||||
methods = methods.replace(name + '::', 'Node::')
|
||||
cpp, exe = work / (role + '.cpp'), work / role
|
||||
cpp.write_text(HARNESS.replace('@METHODS@', methods))
|
||||
sanitizers = [] if os.name == 'nt' else [
|
||||
'-fsanitize=address,undefined', '-fno-sanitize-recover=all',
|
||||
'-fno-pie', '-no-pie']
|
||||
built = subprocess.run([os.environ.get('CXX', 'g++'), '-std=c++17',
|
||||
'-Wall', '-Wextra', '-Werror', *sanitizers,
|
||||
'-DEXPECT_INTERRUPT_IDLE=' + ('1' if platform else '0'),
|
||||
*(['-D' + platform + '=1'] if platform else []), '-I', str(ROOT / 'src'),
|
||||
str(cpp), '-o', str(exe)], capture_output=True, text=True)
|
||||
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
|
||||
ran = subprocess.run([str(exe)], capture_output=True, text=True)
|
||||
self.assertEqual(ran.returncode, 0, ran.stdout + ran.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -18,6 +18,7 @@ HARNESS = r'''
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <limits>
|
||||
#include <helpers/PowerManagementUtils.h>
|
||||
#define WITH_BRIDGE 1
|
||||
#define WITH_RS232_BRIDGE Serial2
|
||||
#define WITH_RS232_BRIDGE_UART 2
|
||||
@@ -310,7 +311,7 @@ class RS232HeartbeatOwnerTests(unittest.TestCase):
|
||||
flags.append("-DWITH_MQTT_BRIDGE=1")
|
||||
built = subprocess.run([compiler, "-std=c++17", "-Wall", "-Wextra", "-Werror",
|
||||
"-fsanitize=address,undefined", "-fno-sanitize-recover=all", "-fno-pie", "-no-pie",
|
||||
*flags, str(cpp), "-o", str(binary)], capture_output=True, text=True, timeout=60)
|
||||
*flags, "-I", str(ROOT / "src"), str(cpp), "-o", str(binary)], capture_output=True, text=True, timeout=60)
|
||||
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
|
||||
checked = subprocess.run([str(binary)], capture_output=True, text=True, timeout=10)
|
||||
self.assertEqual(checked.returncode, 0, checked.stdout + checked.stderr)
|
||||
|
||||
@@ -15,6 +15,7 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
DEADLINES = r'''
|
||||
#include <Arduino.h>
|
||||
#include <cassert>
|
||||
#include <helpers/PowerManagementUtils.h>
|
||||
#define SENSOR_READ_INTERVAL_SECS 60
|
||||
bool logging=false;
|
||||
namespace mesh {bool isUsbLoggingWatchdogArmed(){return logging;}}
|
||||
@@ -98,13 +99,18 @@ int main(){
|
||||
&radio_driver.calibration,&s.queue_due,&s.retry_due}){
|
||||
*due=true;assert(s.hasPendingWork()&&s.getPowerSaveSleepSeconds(30)==0);*due=false;
|
||||
}
|
||||
s.queue_delay_set=true;s.queue_delay=2500;assert(s.getPowerSaveSleepSeconds(30)==3);
|
||||
#if defined(NRF52_PLATFORM) || defined(RP2040_PLATFORM) || defined(STM32_PLATFORM)
|
||||
constexpr unsigned queue_idle=3,retry_idle=2;
|
||||
#else
|
||||
constexpr unsigned queue_idle=2,retry_idle=1;
|
||||
#endif
|
||||
s.queue_delay_set=true;s.queue_delay=2500;assert(s.getPowerSaveSleepSeconds(30)==queue_idle);
|
||||
#if MESH_ENABLE_SENSOR_TRACKER
|
||||
assert(s.trackerNeedsRadio());
|
||||
#endif
|
||||
s.queue_delay_set=false;s.retry_delay_set=true;s.retry_delay=1500;
|
||||
assert(s.getPowerSaveSleepSeconds(30)==2);s.retry_delay_set=false;
|
||||
s.dirty_contacts_expiry=now_ms+1001;assert(s.getPowerSaveSleepSeconds(30)==2);
|
||||
assert(s.getPowerSaveSleepSeconds(30)==retry_idle);s.retry_delay_set=false;
|
||||
s.dirty_contacts_expiry=now_ms+1001;assert(s.getPowerSaveSleepSeconds(30)==retry_idle);
|
||||
s.dirty_contacts_expiry=0;s.last_read_time=45;
|
||||
assert(s.getPowerSaveSleepSeconds(30)==5);s.last_read_time=40;
|
||||
assert(s.hasPendingWork()&&s.getPowerSaveSleepSeconds(30)==0);
|
||||
@@ -245,7 +251,7 @@ class SensorTrackerSleepTest(unittest.TestCase):
|
||||
"-Wno-unused-parameter", "-Wno-unused-function", "-Wno-unused-variable",
|
||||
*(["-fsanitize=address,undefined", "-fno-sanitize-recover=all",
|
||||
"-fno-pie", "-no-pie"] if sys.platform.startswith("linux") else []),
|
||||
*[f"-D{define}" for define in defines], "-I", str(path),
|
||||
*[f"-D{define}" for define in defines], "-I", str(path), "-I", str(ROOT / "src"),
|
||||
str(cpp), "-o", str(binary),
|
||||
], capture_output=True, text=True, timeout=60)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
@@ -261,9 +267,10 @@ class SensorTrackerSleepTest(unittest.TestCase):
|
||||
methods += "\n#if MESH_ENABLE_SENSOR_TRACKER\n" + extract_braced(
|
||||
tracker, "bool SensorMesh::trackerNeedsRadio(") + "\n#endif\n"
|
||||
for enabled in (0, 1):
|
||||
with self.subTest(enabled=enabled):
|
||||
self.compile_run(DEADLINES.replace("@METHODS@", methods),
|
||||
(f"MESH_ENABLE_SENSOR_TRACKER={enabled}",))
|
||||
for platform in ((), ("NRF52_PLATFORM=1",)):
|
||||
with self.subTest(enabled=enabled, platform=platform):
|
||||
self.compile_run(DEADLINES.replace("@METHODS@", methods),
|
||||
(f"MESH_ENABLE_SENSOR_TRACKER={enabled}", *platform))
|
||||
|
||||
def test_actual_main_timer_allocation_queue_failure_and_platform_paths(self):
|
||||
main = (ROOT / "examples/simple_sensor/main.cpp").read_text()
|
||||
|
||||
Reference in new issue
Block a user