feat: TX/RX working — first successful reception via rtl_433 #39

Merged
daru merged 67 commits from fix/first-reception into main 2026-08-03 04:05:06 +00:00
Contributor

First successful TX/RX loop: the firmware transmits Manchester-encoded OOK packets that rtl_433 decodes reliably.

Key changes:

  • Fixed pin_write/read macros (undefined LATA2 → indirect CONCAT via LATAbits.LATAp)
  • Fixed XC8 bitfield accessor usage across all peripheral registers
  • Fixed Timer0 clock source (external T0CKI → internal Fosc/4)
  • Moved ISR body into main loop: ISR now only reloads timer and sets tick flag; transmit_packet() does bit extraction and pin writes
  • Converted tx_buf from uint16_t[15] to uint8_t[30] — eliminates all 16-bit operations on 8-bit MCU
  • Fixed tx_rw bitbanging timing to comply with TX module SPI-like protocol
  • Added FVR timeout handling with error flag in tx_status
  • Added CLI build script, headless flowgraph runner
  • Fixed Manchester encoding to IEEE 802.3 convention

Flex decoder command:

rtl_433 -f 915000000 -X n=test,m=OOK_MC_ZEROBIT,t=50,s=260,y=0,r=600,preamble=0xcebc,invert

Fixes #5 #28 #29 #30 #31 #32 #34 #35

Remaining: MCU not using WDT or sleep mode — power draw is high. Not addressed in this PR.

First successful TX/RX loop: the firmware transmits Manchester-encoded OOK packets that rtl_433 decodes reliably. Key changes: - Fixed pin_write/read macros (undefined LATA2 → indirect CONCAT via LATAbits.LATAp) - Fixed XC8 bitfield accessor usage across all peripheral registers - Fixed Timer0 clock source (external T0CKI → internal Fosc/4) - Moved ISR body into main loop: ISR now only reloads timer and sets tick flag; transmit_packet() does bit extraction and pin writes - Converted tx_buf from uint16_t[15] to uint8_t[30] — eliminates all 16-bit operations on 8-bit MCU - Fixed tx_rw bitbanging timing to comply with TX module SPI-like protocol - Added FVR timeout handling with error flag in tx_status - Added CLI build script, headless flowgraph runner - Fixed Manchester encoding to IEEE 802.3 convention Flex decoder command: ``` rtl_433 -f 915000000 -X n=test,m=OOK_MC_ZEROBIT,t=50,s=260,y=0,r=600,preamble=0xcebc,invert ``` Fixes #5 #28 #29 #30 #31 #32 #34 #35 Remaining: MCU not using WDT or sleep mode — power draw is high. Not addressed in this PR.
time added 67 commits 2026-08-03 03:59:45 +00:00
The PIC[k]LE v0.1 hardware design is now in its as-built state.
All KiCad files, 3D models, and references are finalized for this revision.
Firmware development continues on the software side.

Hardware:
- KiCad schematic and PCB files complete
- 3D step/wrl models imported
- Reference datasheets included
- Firmware source in firmware/PIC[k]LE/ (work in progress)

Firmware:
- RTL-433 integration via deps/rtl_433/
- TX loop, ISR, EEPROM packet counting implemented
- Temperature indicator calibration reference included
- Further firmware development ongoing
- Update manchester table in manchester_encode.h (0x00->0xAAAA, 0xFF->0x5555)
- Update PREAMBLE from 0x1143 to 0xEEBC to produce 0x5656655a after encoding
- Add manchester_gen.py generator script with --verify flag
- Update generate_burst.py to import from manchester_gen and use correct preamble
- Fix comment: rising edge = 1, falling edge = 0
Reviewed-on: daru/picsense#13
build_packet pre-loads dst_buffer[0]=0x0056 and dst_buffer[1]=0x655a, but
manchester_encode then writes the full raw array (which already contains the
preamble) starting at dst_buffer[0], overwriting both values. The pre-load
is dead code.
Reviewed-on: daru/picsense#14
Add explicit config words: BOREN=NSLEEP (saves 300nA in sleep), MCLRE=OFF
(frees RA3), FCMEN=OFF, IESO=OFF. Disable comparator before sleep (Issue #15)
as it was never shut down. Also add stop_fvr_tmp_adc() before sleep as a
defensive measure to ensure FVR/ADC are off.

Power-down budget at 3.0V typical:
- Base IPD: 30nA
- WDT: 500nA
- Total: ~530nA (matches product brief RF Sleep spec)
Reviewed-on: daru/picsense#16
Reviewed-on: daru/picsense#17
Reviewed-by: daru <daru@noreply.localhost>
XC8 was silently dropping pin_write calls because the ternary macro produced a
discarded expression whose volatile LATA side-effects were incorrectly treated as
side-effect-free. Replaced the ternary with direct writes to XC8's volatile
__bit variables (LATAx), which the compiler cannot optimize away. Confirmed by
zero warnings at lines 171/185/340/350/359 and 18-byte code size reduction
(1722→1704 bytes), indicating the operations are now actually emitted.
Reviewed-on: daru/picsense#19
Previously the ADC used FVR (2.048V) as reference and measured battery via
an external divider on RA1. The temperature formula in the decoder used this
battery reading, which was fragile and incorrect.

Now the ADC uses VDD as reference, freeing RA1 for other use. The FVR is read
as an ADC channel to determine VDD: VDD = 2.048 * 1023 / fvr_raw. The
temperature formula becomes VDD-independent when FVR is used:

  Vt  = 2.048 * (1023 - temp_raw) / (4 * fvr_raw)
  Temp = (0.659 - Vt) / 0.00132 - 40

This also fixes the temperature calculation per AN1333 and frees an analog input.
Reviewed-on: daru/picsense#20
start_fvr_tmp_adc returns 1 if FVR fails to stabilize within the timeout
period. Previously the return value was discarded, meaning ADC readings
would be garbage if FVR failed. Now the return value is checked and the
transmission cycle is skipped if FVR fails, preventing garbage data from
being transmitted.
Reviewed-on: daru/picsense#21
Reviewed-on: daru/picsense#22
run_headless.sh was calling gnuradio-companion (the GRC editor), which just
opened a window instead of running the flowgraph. Now it starts Xvfb and runs
the GRC-generated pickle_tx.py against it. Added .gitignore for generated
files, updated README, and cleaned up stale branch state.
Reviewed-on: daru/picsense#25
The firmware Timer0 formula assumed Fosc as the clock source, but PIC12LF1840
uses Fosc/4 (125kHz at 500kHz Fosc). This caused the actual data rate to be
500 bps instead of the intended 2000 bps.

Also add --tx-baud CLI argument to tx_emulator so its half-bit rate is derived
from the firmware's TX_BAUD rather than hardcoded.

Firmware:
  TMR0_RELOAD: 131 -> 225 (half-bit 1000us -> 248us)
  Data rate: 500 bps -> ~2000 bps

tx_emulator:
  Added --tx-baud parameter (default 2000, matches firmware TX_BAUD)
  half_bit_rate now derived from tx_baud * 2 instead of hardcoded 4000
Reviewed-on: daru/picsense#27
Reviewed-by: daru <daru@noreply.localhost>
The XC8 DFP header defines register bits as struct members (e.g.
FVRCONbits.FVREN), not as #define constants. The code was using bare
bitfield names as if they were C constants, which only compiled due to
XC8 quirks but set wrong bits. This caused:

- FVR never ready (ADFVR gain was 0, not 2x)
- WDT prescaler set to reserved value (~1ms) instead of intended 256s
- ADC channel selection and GO_nDONE broken
- Comparator and CPS enable/disable broken

Also: add .gitignore for .vscode/, _build/, out/, and intermediate
object files. Remove .vscode/ from git tracking.
The old CONCAT-based pin_write expanded LATA##pin to LATA2, which is not
a defined identifier in the XC8 DFP header (the correct name is
LATAbits.LATA2). This meant pin_write was writing to an undefined variable
and the DATA pin was never actually toggling, causing no RF output.

Fixed by using the proper bitfield accessors with an indirection layer to
ensure macro arguments are expanded before ## concatenation.

Also: add .vscode/ to .gitignore, fix pin_read to use PORTAbits.RAx,
fix pin_mode_* to use TRISAbits.TRISAx and ANSELAbits.ANSAx.
daru merged commit ee3162dc0e into main 2026-08-03 04:05:06 +00:00
daru deleted branch fix/first-reception 2026-08-03 04:05:30 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
daru/PICsense!39
No description provided.