Skip to content

feat(sim): mock the RFM69 radio and drive remote loads over it - #171

Merged
FredM67 merged 1 commit into
devfrom
sim/rfm69-model
Sep 30, 2026
Merged

FredM67 merged 1 commit into
devfrom
sim/rfm69-model

Conversation

@FredM67

@FredM67 FredM67 commented Sep 30, 2026

Copy link
Copy Markdown
Owner

Remote loads can now be tested without hardware or sun: grid_sim models the RFM69 radio on the SPI bus, and remote loads follow the frames the router sends them.

What's new

  • sim/rfm69.c/.h: a model of the RFM69, covering just what the LowPowerLab library uses to send in packet mode:

    • the register file, including the sync read-back done by initialize();
    • the operating modes;
    • a FIFO that goes on air when the chip enters TX mode.

    PacketSent rises after the frame's real air time at the programmed bit rate (1.73 ms here), so send() blocks the main loop as long as it does on the board. The channel always looks free.

  • Remote loads: remote <node> <bit> <phase> <W> declares a load driven by an ideal receiver:

    • it takes its bit from each frame sent to its node, once the frame has been sent;
    • it switches off after rf_timeout without frames (500 ms by default, like RemoteLoadReceiver).

    Its power is drawn from the simulated grid, so the router regulates through the RF link.

  • Expectations: expect <t> remote <node> <bit> on|off and expect rf_max_gap <node> <s>. Frames are logged in the events CSV (RF15,01), and the summary has one line per node.

  • sim/configs/remote_loads.patch: a two-line config.h change that moves the third load to remote unit 15. It's applied with git apply --unidiff-zero, which fails loudly if config.h drifts.

  • Scenarios: scenarios/rf/remote_step.scn and remote_override.scn, run with make check-rf.

  • CI: the Grid simulation job applies the patch, builds rf and runs make -C sim check-rf.

Results

On dev, with the patched config:

  • the grid holds at -20 W, the configured export, while the remote load bursts on and off;
  • 741 frames go to node 15, at most 120–164 ms apart;
  • 0 ISR overruns and 0 misfiled samples.

The refresh is every 5 mains cycles (100 ms). It is delayed while the main loop prints a datalog line, which is real firmware behaviour and well within the receiver's 500 ms timeout.

#160, configured with remote units 1 and 2, passes the same kind of scenario:

  • each unit gets its own frames (740 and 543), and a unit only gets an immediate frame when its own bits change;
  • the grid holds at -19 W, with 0 overruns.

Once this is merged, #160 needs its own version of the patch for the Load::remote() config format.

Notes

  • On the ATmega328P, simavr registers the SPI port as 0, not '0'.
  • Stage 2, in a later PR: run the real RemoteLoadReceiver firmware in extra simavr instances linked by the radio model, and inject frame loss.

🤖 Generated with Claude Code

grid_sim gets a model of the RFM69 on the SPI bus (chip select D10): the
register file, operating modes and a FIFO that goes on air in TX mode, with
PacketSent raised after the frame's real air time. Remote loads, declared
with 'remote <node> <bit> <phase> <W>', follow the frames sent to their node
through an ideal receiver, and drop out after the 500 ms link timeout like
RemoteLoadReceiver.

New expectations: 'expect <t> remote <node> <bit> on|off' and
'expect rf_max_gap <node> <s>'. Frames go to the events CSV and the summary.

scenarios/rf run against the rf environment with configs/remote_loads.patch
applied (third load on remote unit 15); CI builds it and runs make check-rf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@FredM67
FredM67 merged commit b28166e into dev Sep 30, 2026
9 checks passed
@FredM67
FredM67 deleted the sim/rfm69-model branch September 30, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant