Install
$ agentstack add skill-wang200935-security-agent-skills-nrf24-bitbang-spi ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
nRF24L01+ Bit-Bang SPI Driver with Verified Writes & Auto-Repair
🎯 Trigger Conditions
- nRF24 modules on breadboard with Dupont wires showing SPI corruption (STATUS 0x00/0xFF, register reads corrupted)
- Hardware SPI works on healthy connections but fails on high-resistance contacts
- Need continuous ShockBurst (max-length packet flood) for WiFi/Bluetooth interference
- Long-running firmware needs periodic health checks and self-repair
📋 Problem Context
Hardware SPI on ESP32 uses ~10-50ns edge rates. On breadboard with Dupont wires, contact resistance creates RC filters that round edges, causing bit errors. Bit-banging with configurable µs delays provides clean edges that pass through high-R connections.
Proven thresholds (Radio C = high-R branch):
- HW SPI 50kHz: dead (MISO stuck 0x00)
- Bit-bang 2kHz (500µs): alive, 0/20 errors
- Bit-bang 20kHz (50µs): alive, 0/20 errors but marginal
- Bit-bang 40kHz (25µs): occasional errors
- Bit-bang 100kHz (10µs): frequent errors
- Stable: 150µs delay (~6.6 kHz) for high-R; 10µs for healthy branches
🏗️ Architecture
Class Interface
class SoftNRF {
public:
SoftNRF(uint8_t cePin, uint8_t csnPin, uint16_t delayUs);
static void busInit(); // Call ONCE at boot: SCK/MOSI outputs LOW, MISO input
void beginPins(); // CE output LOW, CSN output HIGH
bool isPresent(); // STATUS sanity: not 0x00 / not 0xFF = alive
void configureTx(); // Power-up, TX, no CRC/ACK, 2Mbps, PA max
void setChannel(uint8_t ch); // Fast path (no verification)
void setChannelChecked(uint8_t); // Verified write (for init)
void carrierOn(); // CONT_WAVE + PLL_LOCK, CE HIGH
void carrierOff(); // CE LOW, clear carrier bits
void powerDown(); // Full power down
// constCarrier mode — CONT_WAVE + REUSE_TX_PL = continuous carrier+payload flood
// This is the ONLY working TX method for interference. See rf-jammer-multi-protocol-jammer/references/nrf24-tx-mode-truth.md
void startConstCarrier(); // RF_SETUP=0x9F, load payload, REUSE_TX_PL
void stopConstCarrier(); // powerDown + clear CONT_WAVE + FLUSH_TX
void sendReuseTXPL(); // CE LOW → clear MAX_RT → transfer(0xE3) → CE HIGH
void reloadP(); // FLUSH_TX → W_TX_PAYLOAD_NOACK → sendReuseTXPL
// Legacy ShockBurst mode — DO NOT USE for interference (fires 1 packet then stops)
void startShockBurst(); // [DEPRECATED] Single-packet TX, not continuous
void repairShockBurstConfig(); // [DEPRECATED] Use reloadP() with constCarrier instead
uint8_t readReg(uint8_t reg); // Verification helper
private:
uint8_t transfer(uint8_t out);
void writeReg(uint8_t reg, uint8_t val);
bool writeRegChecked(uint8_t reg, uint8_t val); // Write + read-back verify (4 retries)
uint8_t _ce, _csn;
uint16_t _d; // per-bit delay in microseconds
};
⚡ Critical Implementation Patterns
1. Bus Initialization (Single Call)
void SoftNRF::busInit() {
pinMode(18, OUTPUT); digitalWrite(18, LOW); // SCK
pinMode(23, OUTPUT); digitalWrite(23, LOW); // MOSI
pinMode(19, INPUT); // MISO
}
2. Bit-Bang Transfer (Mode 0, MSB First)
uint8_t SoftNRF::transfer(uint8_t out) {
uint8_t in = 0;
for (int i = 0; i = 1.5ms
// FLUSH_TX → W_TX_PAYLOAD_NOACK (0xB0) with 32 bytes 0xFF
digitalWrite(_ce, HIGH);
delay(1);
// sendReuseTXPL(): CE LOW → STATUS clear → transfer(0xE3) → CE HIGH
}
// Expected RF_SETUP after start = 0x9F. FIFO shows TX_FULL=1, TX_EMPTY=0.
// If TX_EMPTY=1 → radio stopped. Call reloadP() to re-arm.
5. stopConstCarrier — Cannot Stop with CE LOW
Datasheet: CONTWAVE + REUSETX_PL together → CE LOW doesn't stop TX. Must powerDown:
void SoftNRF::stopConstCarrier() {
powerDown(); // PWR_UP=0 — only way to release
writeRegChecked(RF_SETUP, 0x0F); // clear CONT_WAVE + PLL_LOCK
// FLUSH_TX
}
6. Periodic Health Check: reloadP()
void SoftNRF::reloadP() {
// FLUSH_TX → W_TX_PAYLOAD_NOACK 32×0xFF → sendReuseTXPL()
}
// Runtime: every 5s, if RF_SETUP != 0x9F → call reloadP()
7. [DEPRECATED] startShockBurst — Single-Packet TX (DO NOT USE for interference)
void SoftNRF::startShockBurst() {
writeRegChecked(EN_AA, 0x00); // Auto-ack off
writeRegChecked(SETUP_RETR, 0x00); // No retries
writeRegChecked(RF_SETUP, 0x0F); // 2Mbps, PA max, LNA
writeReg(STATUS, 0x70); // Clear IRQ (not verifiable)
writeRegChecked(CONFIG, 0x02); // PWR_UP=1, PRIM_RX=0, CRC off
delay(2); // Tpd2stby ≥ 1.5ms
// TX address + payload width
writeRegChecked(TX_ADDR, 0xE7);
writeRegChecked(RX_ADDR_P0, 0xE7);
writeRegChecked(RX_PW_P0, 32);
// Fill TX FIFO with 32 bytes of 0xAA
digitalWrite(_csn, LOW);
delayMicroseconds(_d * 2);
transfer(0xA0); // W_TX_PAYLOAD
for (int i = 0; i = 5000) {
for (auto* r : {&RadioA, &RadioB, &RadioC}) {
uint8_t rf = r->readReg(0x06);
if (rf != 0x9F) { // Expected: CONT_WAVE + PLL_LOCK + 2Mbps + PA max
r->reloadP(); // FLUSH_TX → reload payload → re-arm REUSE_TX_PL
}
}
}
📡 TX Mode Comparison for 2.4GHz Interference
| Aspect | Pure CW (CONTWAVE only) | ShockBurst TX | constCarrier (CONTWAVE + REUSETXPL) | |--------|--------------------------|---------------|---------------------------------------------| | Signal | Single tone, no modulation | 32-byte packet, fire ONCE then stop | Continuous carrier modulated with payload data | | Duration | Continuous (until CE/PWR down) | Single packet (~10µs at 2Mbps) | Continuous (until powerDown) | | WiFi CCA Trigger | Energy detect only (often insufficient) | Brief, then gone | Energy detect + preamble correlation (persistent) | | BT/BLE Impact | Minimal — no preamble | Minimal — one packet | Effective — continuous modulated flood | | FIFO State | Empty (no payload) | Empty after 1 TX | TXFULL=1 (always has data via REUSETX_PL) | | How to Stop | CE LOW | CE LOW (auto-stops after packet) | powerDown() only (CE won't respond) |
Result: constCarrier is the ONLY working method. Verified empirically — see rf-jammer-multi-protocol-jammer/references/nrf24-tx-mode-truth.md for STATUS/FIFO observation data.
🔑 Key Register Map
| Register | Addr | ShockBurst Value | Purpose | |----------|------|------------------|---------| | CONFIG | 0x00 | 0x02 | PWRUP=1, PRIMRX=0, CRC off | | ENAA | 0x01 | 0x00 | Auto-ack disabled | | ENRXADDR | 0x02 | 0x01 | Pipe 0 only | | SETUPRETR | 0x04 | 0x00 | No retries | | RFCH | 0x05 | varies | Channel (0-125) | | RFSETUP | 0x06 | 0x0F | 2Mbps, PA max, LNA on | | STATUS | 0x07 | read-only | FIFO status, IRQ flags | | TXADDR | 0x10 | 0xE7... | TX address | | RXADDRP0 | 0x0A | 0xE7... | RX pipe 0 address | | RXPWP0 | 0x11 | 32 | Max payload | | DYNPD | 0x1C | 0x01 | Dynamic payload pipe 0 | | FEATURE | 0x1D | 0x01 | ENDYNACK |
📐 nRF24 Channel → Frequency Mapping
nRF24 channel N = 2400 + N MHz
| Protocol | Frequency | nRF24 Channel | |----------|-----------|---------------| | WiFi ch 1 | 2412 MHz | 12 | | WiFi ch 6 | 2437 MHz | 37 | | WiFi ch 11 | 2462 MHz | 62 | | BLE adv 37 | 2402 MHz | 2 | | BLE adv 38 | 2426 MHz | 26 | | BLE adv 39 | 2480 MHz | 80 | | BT Classic | 2402-2480 MHz | 2-80 |
🐛 Debugging Cheatsheet
| Symptom | Cause | Fix | |---------|-------|-----| | STATUS = 0x00 or 0xFF | SPI not communicating | Check wiring, power, CSN pin | | RFSETUP reads 0x62 not 0x0F | Write corruption during FIFO load | Use repair pass | | FEATURE reads 0x00 | ENDYN_ACK not set | Verify writeRegChecked in startShockBurst | | Works on boot, drifts later | No periodic health check | Add 5s verify + repair loop | | Radio C fails, A/B OK | High-R breadboard contact | Increase delayUs to 150µs |
🚀 Performance
- Bit-bang rate: ~6.6 kHz (150µs delay) on high-R; ~100 kHz (10µs) on healthy
- Channel hop: Single
setChannel()= 1 register write = ~130µs PLL re-lock - ShockBurst throughput: Continuous 32-byte packets at 2Mbps
- Memory: ~2KB flash, ~200 bytes RAM per radio instance
⚖️ Legal Notice
Taiwan Telecommunications Act Art 66/67: Manufacturing, possessing, or using 2.4GHz jammers is illegal (NCC confiscation + fines 100-700万; possession/use 3-30万). This driver is for authorized research in Faraday cage environments only.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Wang200935
- Source: Wang200935/security-agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.