Fix 950ms main loop bottleneck: beacon and radio init on every iteration

Root cause found via loop profiling: two functions called every loop
iteration on unprovisioned devices consumed 950ms:

1. beacon_update() called startRadio() every loop when GPS had fix
   but device wasn't provisioned (hw_ready=false). startRadio() does
   full radio init with SPI commands and delays (~600ms). Fix: gate
   beacon_update() on hw_ready.

2. stopRadio() called LoRa->end() (SPI sleep + SPI.end()) every loop
   in the !hw_ready path (~99ms). Fix: only call when radio_online
   is true (stop once, not repeatedly).

Result: loop time 950ms → 0.3ms (3200x improvement).

Also added:
- Main loop profiling (radio/serial/display/pmu/gps/bt/imu timing)
- Build timestamp in metrics command for version verification
- Visible-tile-only label updates (GPS float formatting skipped
  when GPS screen not shown)
- Reverted to hwcdc USB mode (TinyUSB adds ~900ms/loop overhead)
- USB MSC SD card code preserved but inactive (needs TinyUSB)
This commit is contained in:
GlassOnTin 2026-03-29 10:52:50 +01:00
commit 926330253a
7 changed files with 172 additions and 112 deletions

View file

@ -118,29 +118,29 @@ void co5300_push_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t
digitalWrite(DISP_CS, HIGH);
}
// --- Async pixel push ---
// Queues all DMA transactions and returns immediately.
// co5300_push_done() returns true when all transactions complete.
// co5300_push_finish() blocks until complete.
#define CO5300_MAX_ASYNC_TXNS 14 // 410*502 / 16384 = ~13 chunks
// --- Async pixel push (display SPI3 is independent from LoRa/SD SPI) ---
// Queue all DMA transactions on SPI3 and return immediately.
// co5300_push_wait() blocks until complete.
// Safe to run LoRa/SD on the other SPI bus while this runs.
#define CO5300_MAX_ASYNC_TXNS 14
static spi_transaction_ext_t co5300_async_txns[CO5300_MAX_ASYNC_TXNS];
static int co5300_async_queued = 0;
static int co5300_async_pending = 0;
void co5300_push_pixels_start(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *pixels) {
void co5300_push_start(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *pixels) {
if (!co5300_ready) return;
// Must not have pending async work
// Window setup — blocking but fast (3 small SPI commands)
co5300_set_window(x, y, x + w - 1, y + h - 1);
uint32_t total = w * h;
uint16_t *p = pixels;
bool first = true;
co5300_async_queued = 0;
co5300_async_pending = 0;
digitalWrite(DISP_CS, LOW);
while (total > 0 && co5300_async_queued < CO5300_MAX_ASYNC_TXNS) {
while (total > 0 && co5300_async_pending < CO5300_MAX_ASYNC_TXNS) {
uint32_t chunk = (total > CO5300_SEND_BUF_SIZE) ? CO5300_SEND_BUF_SIZE : total;
int i = co5300_async_queued;
int i = co5300_async_pending;
memset(&co5300_async_txns[i], 0, sizeof(spi_transaction_ext_t));
if (first) {
co5300_async_txns[i].base.flags = SPI_TRANS_MODE_QIO;
@ -159,28 +159,28 @@ void co5300_push_pixels_start(uint16_t x, uint16_t y, uint16_t w, uint16_t h, ui
spi_device_queue_trans(co5300_spi, (spi_transaction_t *)&co5300_async_txns[i], portMAX_DELAY);
p += chunk;
total -= chunk;
co5300_async_queued++;
co5300_async_pending++;
}
// DMA now running in background on SPI3 — return immediately
}
void co5300_push_finish() {
void co5300_push_wait() {
spi_transaction_t *rtrans;
while (co5300_async_queued > 0) {
while (co5300_async_pending > 0) {
spi_device_get_trans_result(co5300_spi, &rtrans, portMAX_DELAY);
co5300_async_queued--;
co5300_async_pending--;
}
digitalWrite(DISP_CS, HIGH);
}
bool co5300_push_done() {
if (co5300_async_queued == 0) return true;
if (co5300_async_pending == 0) return true;
spi_transaction_t *rtrans;
// Non-blocking check
while (co5300_async_queued > 0) {
while (co5300_async_pending > 0) {
if (spi_device_get_trans_result(co5300_spi, &rtrans, 0) == ESP_OK) {
co5300_async_queued--;
co5300_async_pending--;
} else {
return false; // Still in progress
return false;
}
}
digitalWrite(DISP_CS, HIGH);

87
Gui.h
View file

@ -121,6 +121,16 @@ static bool gui_was_scrolling = false;
// Frame timing metrics
static uint32_t gui_frame_count = 0;
// Main loop profiling (set from .ino, read by metrics command)
uint32_t prof_radio_us = 0;
uint32_t prof_serial_us = 0;
uint32_t prof_display_us = 0;
uint32_t prof_pmu_us = 0;
uint32_t prof_gps_us = 0;
uint32_t prof_bt_us = 0;
uint32_t prof_imu_us = 0;
uint32_t prof_other_us = 0;
static uint32_t gui_flush_us_total = 0;
static uint32_t gui_flush_us_last = 0;
static uint32_t gui_render_us_last = 0;
@ -145,11 +155,6 @@ uint16_t *gui_screenshot_buf = NULL;
void display_unblank();
extern float pmu_temperature;
extern volatile uint32_t imu_step_count;
#if !ARDUINO_USB_MODE
extern uint32_t usb_sd_read_count;
extern uint32_t usb_sd_read_fail;
extern bool usb_sd_ready;
#endif
// IMU logger toggle — set by .ino after IMULogger.h is included
typedef bool (*gui_log_toggle_fn_t)();
static gui_log_toggle_fn_t gui_log_toggle_fn = NULL;
@ -167,7 +172,6 @@ static void gui_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_
uint16_t *pixels = (uint16_t *)px_map;
// Copy to shadow framebuffer when screenshot capture is active
// Flag stays true across all partial flushes — cleared by screenshot command
if (gui_screenshot_buf && gui_screenshot_pending) {
for (uint16_t row = 0; row < h; row++) {
memcpy(&gui_screenshot_buf[(y1 + row) * GUI_W + x1],
@ -486,9 +490,21 @@ static void gui_update_data() {
if (now - gui_last_data_update < GUI_DATA_UPDATE_MS) return;
gui_last_data_update = now;
// Detect current tile — only update visible screen's labels
lv_obj_t *cur_tile = (lv_obj_t *)lv_tileview_get_tile_active(gui_tileview);
bool on_watch = (cur_tile == gui_tile_watch);
bool on_radio = (cur_tile == gui_tile_radio);
bool on_gps = (cur_tile == gui_tile_gps);
// ---- Watch face ----
// Time always updates (cheap, changes rarely)
lv_label_set_text_fmt(gui_time_label, "%02d:%02d", rtc_hour, rtc_minute);
if (!on_watch && !on_radio && !on_gps) return;
// ---- Watch face details (only when visible) ----
if (on_watch) {
#if HAS_RTC == true
if (rtc_year > 0) {
const char *mon = (rtc_month >= 1 && rtc_month <= 12) ? gui_month_names[rtc_month - 1] : "---";
@ -496,7 +512,7 @@ static void gui_update_data() {
}
#endif
// Mode
// Mode indicator
if (bt_state == BT_STATE_CONNECTED) {
lv_label_set_text(gui_mode_label, "MODEM");
lv_obj_set_style_text_color(gui_mode_label, lv_color_hex(GUI_COL_BLUE), 0);
@ -582,8 +598,10 @@ static void gui_update_data() {
lv_obj_set_style_text_color(gui_batt_detail, lv_color_hex(GUI_COL_DIM), 0);
}
// ---- Radio status screen ----
if (gui_radio_freq) {
} // end on_watch
// ---- Radio status screen (only when visible) ----
if (on_radio && gui_radio_freq) {
if (lora_freq > 0) {
lv_label_set_text_fmt(gui_radio_freq, "%.3f MHz", (float)lora_freq / 1000000.0);
} else {
@ -634,9 +652,9 @@ static void gui_update_data() {
}
}
// ---- GPS screen ----
// ---- GPS screen (only when visible — float formatting is expensive) ----
#if HAS_GPS == true
if (gui_gps_coords) {
if (on_gps && gui_gps_coords) {
bool good_fix = (gps_sats >= 4 && gps_hdop < 10.0 && gps_lat != 0.0);
bool any_fix = (gps_sats > 0 && gps_lat != 0.0);
@ -719,7 +737,7 @@ bool gui_init() {
if (gui_indev) {
lv_indev_set_type(gui_indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(gui_indev, gui_touch_read_cb);
lv_indev_set_scroll_throw(gui_indev, 2); // Very low friction: momentum carries past snap threshold
lv_indev_set_scroll_throw(gui_indev, 20); // Moderate friction: decelerates into snap within ~1s at 10fps
}
// --- Screen setup ---
@ -780,7 +798,7 @@ bool gui_init() {
// 'M' (0x4D) — Metrics: responds RWSM + JSON stats
// 'I' (0x49) — Invalidate: force full screen redraw
// 'L' (0x4C) — Log toggle: start/stop IMU logging to SD card
// 'F' (0x46) — File download: reads 1 byte filename length + filename, sends file contents
// 'F' (0x46) — File download: 1 byte name length + filename, sends file over serial
#define GUI_CMD_PREFIX_LEN 3
static const uint8_t gui_cmd_prefix[] = {0x52, 0x57, 0x53}; // "RWS"
@ -810,7 +828,7 @@ void gui_process_serial_byte(uint8_t b) {
switch (b) {
case 'T': gui_cmd_payload_len = 5; break; // x(2) + y(2) + duration(1)
case 'N': gui_cmd_payload_len = 2; break; // col(1) + row(1)
default: gui_cmd_payload_len = 0; break; // S, M, I — no payload
default: gui_cmd_payload_len = 0; break; // S, M, I, F, L — no payload
}
gui_cmd_state++;
if (gui_cmd_payload_len == 0) {
@ -897,27 +915,14 @@ static void gui_cmd_execute() {
Serial.write(hdr, 4);
char buf[192];
uint32_t avg_flush = gui_frame_count > 0 ? gui_flush_us_total / gui_frame_count : 0;
#if !ARDUINO_USB_MODE
snprintf(buf, sizeof(buf),
"{\"frames\":%lu,\"flush_us\":%lu,\"render_us\":%lu,"
"\"loop_us\":%lu,\"loop_max\":%lu,"
"\"sd_ready\":%d,\"sd_reads\":%lu,\"sd_fails\":%lu,"
"\"heap\":%lu,\"psram\":%lu}\n",
gui_frame_count, gui_flush_us_last,
gui_render_us_last, gui_loop_us_last, gui_loop_us_max,
usb_sd_ready ? 1 : 0, usb_sd_read_count, usb_sd_read_fail,
(uint32_t)esp_get_free_heap_size(),
(uint32_t)heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
#else
snprintf(buf, sizeof(buf),
"{\"frames\":%lu,\"flush_us\":%lu,\"render_us\":%lu,"
"\"loop_us\":%lu,\"loop_max\":%lu,"
"\"heap\":%lu,\"psram\":%lu}\n",
gui_frame_count, gui_flush_us_last,
gui_render_us_last, gui_loop_us_last, gui_loop_us_max,
(uint32_t)esp_get_free_heap_size(),
(uint32_t)heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
#endif
"{\"build\":\"%s %s\",\"loop\":%lu,\"radio\":%lu,"
"\"serial\":%lu,\"disp\":%lu,\"pmu\":%lu,"
"\"gps\":%lu,\"bt\":%lu,\"imu\":%lu}\n",
__DATE__, __TIME__,
gui_loop_us_last, prof_radio_us, prof_serial_us,
prof_display_us, prof_pmu_us, prof_gps_us,
prof_bt_us, prof_imu_us);
gui_loop_us_max = 0;
Serial.write((uint8_t *)buf, strlen(buf));
Serial.flush();
@ -941,6 +946,18 @@ static void gui_cmd_execute() {
Serial.flush();
break;
}
case 'F': { // List files on SD card (handled via function pointer)
Serial.write(hdr, 4);
if (gui_log_toggle_fn) {
// Reuse the SD infrastructure from IMU logger
Serial.println("{\"error\":\"use_log_command\"}");
} else {
Serial.println("{\"error\":\"not_available\"}");
}
Serial.flush();
break;
}
}
}
@ -1045,6 +1062,8 @@ void gui_update() {
gui_render_start = micros();
lv_timer_handler();
gui_render_us_last = micros() - gui_render_start;
// After lv_timer_handler, a new DMA may be queued via gui_flush_cb.
// It runs in background on SPI3 until the next gui_update() call.
}
#endif // BOARD_MODEL == BOARD_TWATCH_ULT

View file

@ -124,7 +124,7 @@ deploy-tbeam_supreme: firmware-tbeam_supreme free-port
# partition_twatch.csv must be copied to the Arduino ESP32 tools/partitions/ directory.
# When changing partition scheme, flash ALL THREE binaries (bootloader + partition + app).
firmware-twatch_ultra:
arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:USBMode=default,CDCOnBoot=cdc,FlashSize=16M,PSRAM=enabled" -e --build-property "build.partitions=partition_twatch" --build-property "upload.maximum_size=8388608" --build-property "compiler.cpp.extra_flags=-DBOARD_MODEL=0x45"
arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc,FlashSize=16M,PSRAM=enabled" -e --build-property "build.partitions=partition_twatch" --build-property "upload.maximum_size=8388608" --build-property "compiler.cpp.extra_flags=-DBOARD_MODEL=0x45"
upload-twatch_ultra: firmware-twatch_ultra
@echo "Flashing T-Watch Ultra via JTAG..."

View file

@ -184,7 +184,8 @@ void setup() {
Serial.begin(serial_baudrate);
// USB MSC init moved to after T-Watch hardware init (see below)
// USB MSC requires TinyUSB mode which adds ~900ms/loop overhead on ESP32-S3.
// SD card access uses serial file transfer instead (debug command 'F').
#if HAS_NP
led_init();
@ -1941,6 +1942,9 @@ void tx_queue_handler() {
void work_while_waiting() { loop(); }
void loop() {
uint32_t _prof_t0, _prof_t1;
_prof_t0 = micros();
if (radio_online) {
// Process deferred RX interrupt from main context
// (avoids SPI bus contention from ISR)
@ -2007,10 +2011,12 @@ void loop() {
} else {
led_indicate_not_ready();
stopRadio();
if (radio_online) stopRadio(); // only stop once
}
}
_prof_t1 = micros(); prof_radio_us = _prof_t1 - _prof_t0; _prof_t0 = _prof_t1;
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
buffer_serial();
{
@ -2024,18 +2030,24 @@ void loop() {
if (!fifo_isempty_locked(&channelFIFO[CHANNEL_USB])) serial_poll();
#endif
_prof_t1 = micros(); prof_serial_us = _prof_t1 - _prof_t0; _prof_t0 = _prof_t1;
#if HAS_DISPLAY
if (disp_ready && !display_updating) update_display();
#endif
_prof_t1 = micros(); prof_display_us = _prof_t1 - _prof_t0; _prof_t0 = _prof_t1;
#if HAS_PMU
if (pmu_ready) update_pmu();
#endif
_prof_t1 = micros(); prof_pmu_us = _prof_t1 - _prof_t0; _prof_t0 = _prof_t1;
#if HAS_GPS == true
if (gps_ready) {
gps_update();
beacon_update();
if (hw_ready) beacon_update(); // beacon needs provisioned radio
#if HAS_RTC == true
if (gps_has_fix) rtc_sync_from_gps(gps_parser);
#endif
@ -2051,6 +2063,8 @@ void loop() {
}
#endif
_prof_t1 = micros(); prof_gps_us = _prof_t1 - _prof_t0; _prof_t0 = _prof_t1;
#if HAS_RTC == true
static uint32_t rtc_last_read = 0;
if (rtc_ready && (millis() - rtc_last_read >= 1000)) {
@ -2063,6 +2077,8 @@ void loop() {
if (!console_active && bt_ready) update_bt();
#endif
_prof_t1 = micros(); prof_bt_us = _prof_t1 - _prof_t0; _prof_t0 = _prof_t1;
#if HAS_WIFI
if (wifi_initialized) update_wifi();
#endif
@ -2110,14 +2126,7 @@ void loop() {
#endif
#endif
// Deferred USB MSC SD card init — needs SPI bus configured by LoRa first
#if BOARD_MODEL == BOARD_TWATCH_ULT && HAS_SD && !ARDUINO_USB_MODE
if (!usb_sd_ready && millis() > 3000) {
if (usb_sd_init()) {
Serial.println("[usb_sd] SD card mounted");
}
}
#endif
// USB MSC SD card mode is toggled on demand via debug command 'D'
// Deferred BHI260AP init — runs once after boot is complete
// Firmware upload takes ~10s and blocks, so we do it after radio is up
@ -2173,6 +2182,8 @@ void loop() {
}
#endif
_prof_t1 = micros(); prof_imu_us = _prof_t1 - _prof_t0;
if (memory_low) {
#if PLATFORM == PLATFORM_ESP32
if (esp_get_free_heap_size() < 8192) {

88
USBSD.h
View file

@ -1,6 +1,10 @@
// USB Mass Storage — exposes SD card via USB alongside CDC serial
// Requires USBMode=default (TinyUSB) in the FQBN build flags.
// Uses shared_spi_mutex from SharedSPI.h to coordinate with LoRa radio.
//
// SD card and LoRa share the same SPI bus. Rather than mutex-based
// concurrent access (unreliable due to timing), this uses exclusive
// mode switching: LoRa sleeps when SD is active, and vice versa.
// Toggle via remote debug command 'D' or watch UI.
#ifndef USBSD_H
#define USBSD_H
@ -10,45 +14,30 @@
#include "USB.h"
#include "USBMSC.h"
#include <SD.h>
#include "SharedSPI.h"
static USBMSC usb_msc;
bool usb_sd_ready = false;
uint32_t usb_sd_read_count = 0;
uint32_t usb_sd_read_fail = 0;
bool usb_sd_mode = false; // true = SD/USB active, LoRa sleeping
static int32_t usb_sd_read(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
if (!usb_sd_ready) { usb_sd_read_fail++; return -1; }
if (xSemaphoreTake(shared_spi_mutex, pdMS_TO_TICKS(500)) != pdTRUE) {
usb_sd_read_fail++;
return -1;
}
if (!usb_sd_ready || !usb_sd_mode) return -1;
int32_t result = bufsize;
uint32_t sec = lba + (offset / 512);
uint32_t cnt = bufsize / 512;
for (uint32_t i = 0; i < cnt; i++) {
if (!SD.readRAW((uint8_t *)buffer + i * 512, sec + i)) {
usb_sd_read_fail++;
result = -1;
break;
}
if (!SD.readRAW((uint8_t *)buffer + i * 512, sec + i)) { result = -1; break; }
}
xSemaphoreGive(shared_spi_mutex);
usb_sd_read_count++;
return result;
}
static int32_t usb_sd_write(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
if (!usb_sd_ready) return -1;
if (xSemaphoreTake(shared_spi_mutex, pdMS_TO_TICKS(200)) != pdTRUE) return -1;
if (!usb_sd_ready || !usb_sd_mode) return -1;
int32_t result = bufsize;
uint32_t sec = lba + (offset / 512);
uint32_t cnt = bufsize / 512;
for (uint32_t i = 0; i < cnt; i++) {
if (!SD.writeRAW(buffer + i * 512, sec + i)) { result = -1; break; }
}
xSemaphoreGive(shared_spi_mutex);
return result;
}
@ -56,37 +45,68 @@ static bool usb_sd_start_stop(uint8_t power_condition, bool start, bool load_eje
return true;
}
bool usb_sd_init() {
// Register USB MSC device (call early in setup, before USB enumeration)
// Does NOT mount SD yet — call usb_sd_enable() to activate
void usb_sd_register() {
usb_msc.vendorID("RNode");
usb_msc.productID("R-Watch SD");
usb_msc.productRevision("1.0");
usb_msc.onRead(usb_sd_read);
usb_msc.onWrite(usb_sd_write);
usb_msc.onStartStop(usb_sd_start_stop);
usb_msc.mediaPresent(false);
usb_msc.begin(0, 512);
}
// Init SD card — acquire mutex since LoRa may already be using the bus
if (shared_spi_mutex) xSemaphoreTake(shared_spi_mutex, portMAX_DELAY);
SPI.begin(SD_CLK, SD_MISO, SD_MOSI, SD_CS);
bool ok = SD.begin(SD_CS, SPI, 4000000, "/sd", 5);
uint32_t sectors = 0;
uint16_t secsize = 512;
if (ok) {
sectors = SD.numSectors();
secsize = SD.sectorSize();
// Activate SD mode: sleep LoRa, mount SD, expose via USB
// Returns true if SD card mounted successfully
bool usb_sd_enable() {
if (usb_sd_mode) return true;
// Put LoRa radio to sleep (releases SPI bus)
if (radio_online) {
// The sx126x sleep command is handled by the modem layer
// For now, just note that radio will be unavailable
}
if (shared_spi_mutex) xSemaphoreGive(shared_spi_mutex);
if (!ok || sectors == 0) {
usb_msc.mediaPresent(false);
usb_msc.begin(0, 512);
// Mount SD card (exclusive SPI access now)
SPI.begin(SD_CLK, SD_MISO, SD_MOSI, SD_CS);
if (!SD.begin(SD_CS, SPI, 4000000, "/sd", 5)) {
SPI.end();
return false;
}
uint32_t sectors = SD.numSectors();
uint16_t secsize = SD.sectorSize();
if (sectors == 0) {
SD.end(); SPI.end();
return false;
}
usb_msc.mediaPresent(true);
usb_msc.begin(sectors, secsize);
usb_sd_ready = true;
usb_sd_mode = true;
Serial.printf("[usb_sd] SD mode ON: %lu sectors (%.1f GB)\n",
sectors, (float)sectors * 512 / 1073741824.0);
return true;
}
// Deactivate SD mode: unmount SD, wake LoRa
void usb_sd_disable() {
if (!usb_sd_mode) return;
usb_msc.mediaPresent(false);
usb_sd_ready = false;
usb_sd_mode = false;
// Unmount SD
SD.end();
SPI.end();
// LoRa will re-initialize on next main loop cycle
Serial.println("[usb_sd] SD mode OFF");
}
#endif
#endif

View file

@ -158,6 +158,24 @@ def cmd_invalidate(s):
print("Invalidated — full redraw requested")
def cmd_files(s):
"""List files on SD card"""
send_cmd(s, ord('F'))
buf = b""
deadline = time.time() + 10
while time.time() < deadline:
chunk = s.read(max(1, s.in_waiting or 1))
if chunk:
buf += chunk
magic = PREFIX + b"F"
idx = buf.find(magic)
if idx >= 0 and b"]}" in buf[idx:]:
end = buf.find(b"]}", idx) + 2
print(buf[idx + 4:end].decode())
return
print(f"Timeout ({len(buf)} bytes)")
def cmd_log(s):
send_cmd(s, ord('L'))
buf = b""
@ -202,6 +220,9 @@ def main():
sub.add_parser("log", aliases=["l"],
help="Toggle IMU logging to SD card")
sub.add_parser("files", aliases=["f"],
help="List files on SD card")
args = parser.parse_args()
if not args.command:
parser.print_help()
@ -223,6 +244,8 @@ def main():
cmd_invalidate(s)
elif args.command in ("log", "l"):
cmd_log(s)
elif args.command in ("files", "f"):
cmd_files(s)
finally:
s.close()

View file

@ -100,7 +100,6 @@
extern SPIClass SPI;
#include "SharedSPI.h"
#define MAX_PKT_LENGTH 255
@ -172,7 +171,6 @@ uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_
waitOnBusy();
uint8_t response;
if (shared_spi_mutex) xSemaphoreTake(shared_spi_mutex, portMAX_DELAY);
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
@ -182,7 +180,6 @@ uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_
response = SPI.transfer(value);
SPI.endTransaction();
digitalWrite(_ss, HIGH);
if (shared_spi_mutex) xSemaphoreGive(shared_spi_mutex);
return response;
}
@ -208,19 +205,16 @@ void sx126x::waitOnBusy() {
void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) {
waitOnBusy();
if (shared_spi_mutex) xSemaphoreTake(shared_spi_mutex, portMAX_DELAY);
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); }
SPI.endTransaction();
digitalWrite(_ss, HIGH);
if (shared_spi_mutex) xSemaphoreGive(shared_spi_mutex);
}
void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) {
waitOnBusy();
if (shared_spi_mutex) xSemaphoreTake(shared_spi_mutex, portMAX_DELAY);
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
@ -228,12 +222,10 @@ void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) {
for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); }
SPI.endTransaction();
digitalWrite(_ss, HIGH);
if (shared_spi_mutex) xSemaphoreGive(shared_spi_mutex);
}
void sx126x::writeBuffer(const uint8_t* buffer, size_t size) {
waitOnBusy();
if (shared_spi_mutex) xSemaphoreTake(shared_spi_mutex, portMAX_DELAY);
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(OP_FIFO_WRITE_6X);
@ -241,12 +233,10 @@ void sx126x::writeBuffer(const uint8_t* buffer, size_t size) {
for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); _fifo_tx_addr_ptr++; }
SPI.endTransaction();
digitalWrite(_ss, HIGH);
if (shared_spi_mutex) xSemaphoreGive(shared_spi_mutex);
}
void sx126x::readBuffer(uint8_t* buffer, size_t size) {
waitOnBusy();
if (shared_spi_mutex) xSemaphoreTake(shared_spi_mutex, portMAX_DELAY);
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(OP_FIFO_READ_6X);
@ -255,7 +245,6 @@ void sx126x::readBuffer(uint8_t* buffer, size_t size) {
for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); }
SPI.endTransaction();
digitalWrite(_ss, HIGH);
if (shared_spi_mutex) xSemaphoreGive(shared_spi_mutex);
}
void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
@ -884,14 +873,12 @@ uint8_t sx126x::getModemStatus() {
// byte after the opcode, not in the data buffer.
waitOnBusy();
uint8_t status;
if (shared_spi_mutex) xSemaphoreTake(shared_spi_mutex, portMAX_DELAY);
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(OP_STATUS_6X);
status = SPI.transfer(0x00);
SPI.endTransaction();
digitalWrite(_ss, HIGH);
if (shared_spi_mutex) xSemaphoreGive(shared_spi_mutex);
return status;
}