Skip to content

Commit 7fbd72d

Browse files
Joeri Van Doorenclaude
andcommitted
Stay off the air while a computer has the config drive mounted
The amplifier draws a few hundred milliamps for the several seconds an SF12 frame is on the air, and some PC USB ports cannot hold 5 V through that. The undervoltage resets the tracker - in the worst case while the host still has the config drive mounted and half-written. Enumeration is the signal, not the mass-storage mount, because there is no dependable mount signal to use: FatFSUSB's onPlug only fires on a SCSI START STOP UNIT load, which no operating system sends when it mounts a volume, and PREVENT/ALLOW MEDIUM REMOVAL is optional per host. tud_mounted() reports SET_CONFIGURATION, which is mandatory USB and behaves the same on macOS, Windows and Linux. A charger and the 13.8 V Powerpole never enumerate anything, so a deployed tracker is unaffected. Ejecting has to keep meaning something. The eject already reboots to apply the new config, and a reboot re-enumerates, so the host would simply mount the volume again and the tracker would go quiet for good. The eject is therefore latched into watchdog scratch[2] - which survives a soft reset but not a power cycle - and the boot that follows reports no medium, so nothing can mount it and the inhibit has nothing left to hold. The latch is dropped before reset_usb_boot() so a freshly flashed board still comes up with its drive. The gate sits above sb.shouldBeacon() rather than at the radio. Taking the beacon decision and then suppressing the RF would burn a sequence number into flash for a frame nobody hears, stamp lastVoltageWarning on an alert that never went out, and clear metadataForced. Suppressing between shouldBeacon() and updateAfterBeacon() would be worse still: the SmartBeacon anchor stays unset, which makes shouldBeacon() true on every 50 ms pass and erases a flash sector each time. loraSendText() keeps a backstop so no future path can key the PA behind the gate's back. usbTxInhibit=false is the escape hatch. A car head unit is a USB host and would otherwise hold an in-car tracker silent for the whole journey. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 49a5dad commit 7fbd72d

4 files changed

Lines changed: 269 additions & 7 deletions

File tree

README.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ Detection can be overridden from `config.txt` with `radioChip` and
4646
4. A drive named `RPI-RP2` will appear - copy the `.uf2` file to it
4747
5. The device reboots automatically and a new drive named `APRSTRKR` appears
4848
6. Edit `config.txt` on the drive (see Configuration below)
49-
7. Eject the drive - the device reboots with your settings
49+
7. Eject the drive - the device reboots with your settings and starts
50+
transmitting. The drive stays offline until the tracker is re-powered
5051

5152
## Configuration
5253

@@ -64,6 +65,30 @@ After flashing, the device presents as a USB drive with a `config.txt` file. Edi
6465
TCXO supply voltage such as `1.8` / `3.3`
6566
- **paDrive** - chip dBm into a module PA, -9..22 (V2 only)
6667
- **gpsBaud** - `0` to detect, or pin it (`9600` / `115200`)
68+
- **usbTxInhibit** - stay off the air while a computer has the tracker
69+
enumerated (`true`/`false`, default `true`)
70+
71+
### Transmitting while connected to a computer
72+
73+
The amplifier draws a few hundred milliamps for the several seconds an SF12
74+
frame is on the air. Some PC USB ports cannot hold 5 V through that, and the
75+
undervoltage resets the tracker - in the worst case while the computer still
76+
has the config drive mounted and half-written. So the tracker stays off the
77+
air for as long as a computer has it enumerated, and says so on the console
78+
and by double-blinking the LoRa LED every 5 seconds.
79+
80+
Only an actual USB host counts. A charger and the 13.8 V Powerpole never
81+
enumerate anything, so a deployed tracker beacons exactly as before.
82+
83+
Ejecting the drive enables transmission for the rest of that power session -
84+
that is how to bench-test the PA with the tracker on USB. Ejecting also takes
85+
the config drive offline: unplug the tracker and power it again to get the
86+
drive back. Note that ejecting only says the computer has let go of the
87+
filesystem, so a brownout can no longer corrupt it; it does not make the port
88+
any stronger. If that port still cannot supply the transmitter the tracker
89+
will keep resetting, and unplugging it remains the way out.
90+
91+
`usbTxInhibit=false` in `config.txt` turns the whole thing off.
6792

6893
### What `power` means depends on the board
6994

@@ -100,6 +125,10 @@ To update to a new firmware version, either:
100125
- Repeat the Quick Install steps above with the new `.uf2` file, **or**
101126
- Edit `config.txt` on the USB drive, replace all content with just `firmwareupdate`, and eject - the device will reboot into UF2 bootloader mode ready for the new firmware
102127

128+
Flashing through `reset.sh` reboots without a power cycle, so the config
129+
drive may not reappear until the tracker is unplugged and plugged back in.
130+
The `firmwareupdate` route above clears that by itself.
131+
103132
## Factory Reset
104133

105134
Copy `flash_nuke.uf2` to the `RPI-RP2` drive to erase all flash, then reflash the firmware.
@@ -118,7 +147,9 @@ The firmware will be at `.pio/build/pico/firmware.uf2`.
118147

119148
`tools/chipprobe/` holds several standalone firmwares for bringing up a
120149
new board. Only the `-tx` and `cwtest` builds transmit; the rest are
121-
passive and never enable the PA.
150+
passive and never enable the PA. They are separate binaries and are not
151+
subject to the transmit inhibit described above, which makes them the way
152+
to measure PA output with the tracker on a PC.
122153

123154
```console
124155
cd tools/chipprobe
@@ -151,6 +182,8 @@ tio --auto-connect new
151182
```
152183

153184
The console shows colored output with GPS status, LoRa TX frames, and voltage monitoring.
185+
On a computer the tracker is silent by design and prints why - see
186+
[Transmitting while connected to a computer](#transmitting-while-connected-to-a-computer).
154187

155188
<img width="938" alt="TrackerTOP" src="https://github.com/Guru-RF/LoraAPRStracker/assets/1251767/c3a32cc5-92fe-420b-a335-53400f411a51">
156189
<img width="1076" alt="TrackerBottom" src="https://github.com/Guru-RF/LoraAPRStracker/assets/1251767/2ef5376d-9d41-4aac-892e-fea3d2fedd85">
@@ -163,6 +196,13 @@ there will be no amplified output.
163196

164197
**V2 boards:** the amplifier is inside the radio module and is fed from
165198
the on-board buck regulator, so full output is available on USB-C as
166-
well as on the Powerpole. Do not connect both at once.
199+
well as on the Powerpole. Do not connect both at once. Note that the
200+
firmware will not transmit while a computer has the tracker enumerated -
201+
eject the drive, use a plain charger, or use the diagnostic firmwares.
202+
203+
**Car head units are USB hosts.** A head unit or media player will
204+
enumerate the tracker and hold it silent for the whole journey. Power an
205+
in-car tracker from the Powerpole or a plain charger rather than a head
206+
unit's data port, or set `usbTxInhibit=false` in `config.txt`.
167207

168208
Never transmit without an antenna or a 50 ohm dummy load on the SMA.

data/config.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,10 @@ i2cEnabled=true
5757
i2cDevice=BME680
5858
bme680TempOffset=0
5959

60+
# --- USB ---
61+
# Stay off the air while a computer has the tracker enumerated;
62+
# eject the drive to transmit. false for a car head unit.
63+
usbTxInhibit=true
64+
6065
# --- Debug ---
6166
fullDebug=false

include/config_file.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ extern FS FatFS;
2626
#define DEFAULT_I2C_DEVICE "BME680"
2727
#define DEFAULT_BME680_OFFSET 0
2828
#define DEFAULT_FULL_DEBUG false
29+
// Hold off transmitting while a computer has the tracker
30+
// enumerated over USB. Set false only where the tracker is
31+
// powered from a USB host that is expected to stay attached,
32+
// such as a car head unit - see src/main.cpp for why it exists.
33+
#define DEFAULT_USB_TX_INHIBIT true
2934
#define DEFAULT_LORA_FREQ 433.775f
3035

3136
// Radio chip: "auto" identifies SX1276/RFM95 vs SX1262 over SPI at boot.
@@ -98,6 +103,7 @@ struct TrackerConfig {
98103
char i2cDevice[16];
99104
int bme680TempOffset;
100105
bool fullDebug;
106+
bool usbTxInhibit;
101107
float loraFrequency;
102108
char radioChip[12];
103109
char loraTcxo[8];
@@ -143,6 +149,7 @@ static void configSetDefaults(TrackerConfig &cfg) {
143149
strncpy(cfg.i2cDevice, DEFAULT_I2C_DEVICE, sizeof(cfg.i2cDevice));
144150
cfg.bme680TempOffset = DEFAULT_BME680_OFFSET;
145151
cfg.fullDebug = DEFAULT_FULL_DEBUG;
152+
cfg.usbTxInhibit = DEFAULT_USB_TX_INHIBIT;
146153
cfg.loraFrequency = DEFAULT_LORA_FREQ;
147154
strncpy(cfg.radioChip, DEFAULT_RADIO_CHIP, sizeof(cfg.radioChip));
148155
strncpy(cfg.loraTcxo, DEFAULT_LORA_TCXO, sizeof(cfg.loraTcxo));
@@ -226,6 +233,8 @@ static void configSetValue(TrackerConfig &cfg, const char *key, const char *val)
226233
cfg.bme680TempOffset = constrain(atoi(val), 0, 99);
227234
} else if (strcasecmp(key, "fullDebug") == 0) {
228235
cfg.fullDebug = (strcasecmp(val, "true") == 0 || strcmp(val, "1") == 0);
236+
} else if (strcasecmp(key, "usbTxInhibit") == 0) {
237+
cfg.usbTxInhibit = (strcasecmp(val, "true") == 0 || strcmp(val, "1") == 0);
229238
} else if (strcasecmp(key, "loraFrequency") == 0) {
230239
cfg.loraFrequency = atof(val);
231240
} else if (strcasecmp(key, "radioChip") == 0) {
@@ -314,6 +323,11 @@ static const char *CONFIG_TEMPLATE =
314323
"i2cDevice=BME680\n"
315324
"bme680TempOffset=0\n"
316325
"\n"
326+
"# --- USB ---\n"
327+
"# Stay off the air while a computer has the tracker enumerated;\n"
328+
"# eject the drive to transmit. false for a car head unit.\n"
329+
"usbTxInhibit=true\n"
330+
"\n"
317331
"# --- Debug ---\n"
318332
"fullDebug=false\n";
319333

@@ -360,6 +374,10 @@ static bool configCreateDefault() {
360374
f.println("i2cDevice=BME680");
361375
f.println("bme680TempOffset=0");
362376
f.println("");
377+
f.println("# Stay off the air while a computer has the tracker enumerated;");
378+
f.println("# eject the drive to transmit. false for a car head unit.");
379+
f.println("usbTxInhibit=true");
380+
f.println("");
363381
f.println("fullDebug=false");
364382
f.close();
365383
Serial.println("[CONFIG] Default config.txt created");

0 commit comments

Comments
 (0)