/* * rtlsdr_experimental.h — EXPERIMENTAL RTL2832U-over-USB-Host module for the * ESP32-P4 sensor node (Workstream J, ESP32-P4 Sensor Node spec). * * ============================================================================ * HARDWARE PASS REQUIRED — UNVERIFIED AGAINST REAL HARDWARE * ============================================================================ * This entire module (this header + rtlsdr_experimental.c) has been written * from documented ESP-IDF USB Host Library API shape and the RTL2832U/R820T * register sequence already researched for this project in * frontend/src/lib/sdr.ts (a WebUSB driver, real and documented against * librtlsdr's public register map). It has NOT been compiled against a real * ESP-IDF toolchain (none is available in this environment) and has NOT run * against real ESP32-P4 + RTL2832U dongle hardware. Treat every register * value, timing constant, buffer size, and API call signature here as * "structurally reasoned, not verified." See the "Workstream J" section of * ../../README.md for the full honesty write-up, including exactly what a * real hardware bring-up session needs to check before this is trusted. * * Scope reminder: this is a self-contained ESP-IDF *component*, deliberately * kept out of `main/` so it can be dropped into Workstream I's firmware * project skeleton (WiFi/HTTP/BME280/presence-sensor core) without touching * any file that workstream owns. Integration is one line in the consuming * project: add this directory to EXTRA_COMPONENT_DIRS (or copy it under that * project's own components/) and call rtlsdr_exp_init()/rtlsdr_exp_start() * from wherever app_main() sets up its other sensor drivers. * ============================================================================ */ #pragma once #include #include #include #include "esp_err.h" #ifdef __cplusplus extern "C" { #endif /* --------------------------------------------------------------------------- * USB identification — ported 1:1 from frontend/src/lib/sdr.ts * (RTL2832U_VENDOR / RTL2832U_PRODUCTS / TERRATEC_VENDOR, lines ~30-34). * * sdr.ts deliberately filters WebUSB's device picker by *vendor* id only * (see its requestDevice() comment: "many dongles report product ids * outside the handful we know, so filtering by productId hides them from * the picker"). We mirror that: RTLSDR_EXP_KNOWN_PRODUCT_IDS below is * informational only (used for a startup log line), never a hard match * filter. Any device presenting vendor id RTL2832U or TERRATEC is treated * as a candidate and bring-up is attempted. * ------------------------------------------------------------------------ */ #define RTLSDR_EXP_VENDOR_RTL2832U 0x0BDAu #define RTLSDR_EXP_VENDOR_TERRATEC 0x0CCDu /* Known RTL2832U product ids (informational/logging only — see above). */ extern const uint16_t RTLSDR_EXP_KNOWN_PRODUCT_IDS[4]; #define RTLSDR_EXP_NUM_KNOWN_PRODUCT_IDS 4 /* --------------------------------------------------------------------------- * Configuration * ------------------------------------------------------------------------ */ typedef struct { /* Tuning defaults, applied once bring-up completes. Matches sdr.ts's * open(sampleRateHz = 2_048_000) default and its FM-band test tune of * 98 MHz — pick whatever the product actually wants to listen to. */ uint32_t center_freq_hz; /* e.g. 98_000_000 */ uint32_t sample_rate_hz; /* e.g. 2_048_000 */ /* Bulk-IN read tuning. bulk_read_chunk_bytes MUST be a multiple of 512 * (USB high-speed bulk max packet size) — this mirrors sdr.ts's own * assumption ("Length must be multiple of 512" on readSamples()). * UNVERIFIED: real chunk size vs. USB Host Library heap/DMA limits and * actual achievable throughput needs a real device. See README. */ size_t bulk_read_chunk_bytes; /* default suggestion: 16384 */ size_t bulk_read_queue_depth; /* in-flight pipelined transfers, e.g. 4 */ /* Upstream IQ forwarding (see rtlsdr_exp_forward_iq_block() and the * "IQ forwarding architecture" section of the README for the reasoning * behind a *separate* raw-binary stream rather than reusing the JSON * sensor telemetry endpoint). NOTE: the backend endpoint referenced * here does not exist yet anywhere in this repo as of this workstream — * this is a client-side stub against a *proposed* shape, not a wired * integration. */ const char *iq_upload_url; /* e.g. "https://host/api/device/iq-stream" */ const char *device_bearer_token; /* same raw pairing token used for * the regular telemetry POST loop */ } rtlsdr_exp_config_t; /* Fills in documented, conservative defaults. Caller still MUST set * iq_upload_url / device_bearer_token before starting. */ void rtlsdr_exp_config_default(rtlsdr_exp_config_t *config); typedef struct rtlsdr_exp_handle_s *rtlsdr_exp_handle_t; /* --------------------------------------------------------------------------- * Lifecycle * ------------------------------------------------------------------------ */ /* Allocates internal state. Does NOT touch USB hardware yet. */ esp_err_t rtlsdr_exp_init(const rtlsdr_exp_config_t *config, rtlsdr_exp_handle_t *out_handle); esp_err_t rtlsdr_exp_deinit(rtlsdr_exp_handle_t handle); /* Installs the USB Host Library, registers a client, and spawns the * background tasks that watch for a matching RTL2832U device, run the * init vendor-command sequence on attach, and (once bring-up succeeds) * start the bulk-IN read/forward loop. Non-blocking — returns once the * tasks are created, not once a device is found. * * UNVERIFIED beyond "this is the documented shape of the ESP-IDF USB Host * Library API" — see README for exactly what needs a real board to check. */ esp_err_t rtlsdr_exp_start(rtlsdr_exp_handle_t handle); /* Stops streaming, releases the USB interface/device if held, deregisters * the USB Host client, and tears down the background tasks. Does NOT call * usb_host_uninstall() if other USB Host clients might still be active in * the wider firmware project (Workstream I owns whether anything else in * the project also uses USB host) — see README integration notes. */ esp_err_t rtlsdr_exp_stop(rtlsdr_exp_handle_t handle); /* Best-effort retune while streaming — same caveats as sdr.ts's * setFrequency()/setSampleRate() (simplified PLL/resampler math, marked * HARDWARE PASS REQUIRED there too). */ esp_err_t rtlsdr_exp_set_frequency(rtlsdr_exp_handle_t handle, uint32_t hz); esp_err_t rtlsdr_exp_set_sample_rate(rtlsdr_exp_handle_t handle, uint32_t hz); /* --------------------------------------------------------------------------- * Observability — deliberately exposed so this experimental module can * report its own health (e.g. folded into the regular sensor_type registry * as a synthetic "sdr_status" reading) rather than fail silently. * ------------------------------------------------------------------------ */ typedef struct { bool device_present; /* a matching-vendor USB device is enumerated */ bool bring_up_ok; /* init vendor-command sequence completed without * a control-transfer error (does NOT mean the * tuner/demod are actually producing valid IQ — * only real hardware + a spectrum check can * confirm that) */ bool streaming; uint32_t bulk_reads_ok; uint32_t bulk_reads_failed; uint32_t iq_blocks_dropped; /* forward queue was full — see README */ uint32_t bytes_forwarded_upstream; uint32_t forward_failures; } rtlsdr_exp_stats_t; void rtlsdr_exp_get_stats(rtlsdr_exp_handle_t handle, rtlsdr_exp_stats_t *out_stats); #ifdef __cplusplus } #endif