birdcage/docs/ble-bridge-wiring.md
Ryan Malloy f2c1eb84d2 Fix GPS baud rate: RYS352A defaults to 115200, not 9600
The AG3352 GNSS engine in the RYS352A ships at 115200 8N1 per
the datasheet spec table. 9600 was a generic assumption that
would cause the UART to read garbage and never acquire a fix.
2026-02-11 16:06:31 -07:00

261 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# BLE Bridge Wiring — ESP32-S3 + 2× MAX485
Transparent BLE-to-RS422 bridge for the Winegard Carryout G2 satellite dish,
with optional IMU and barometric sensors for orientation and refraction correction.
## Parts
**Bridge (required):**
- ESP32-S3-DevKitC-1-N16R8
- 2× MAX485 TTL-to-RS485 module
- 1× SparkFun Bidirectional Logic Level Converter (BOB-12009, BSS138-based)
- RJ-12 6P6C straight-wired cable with breakout
- Hookup wire / jumpers
**Sensors (optional):**
- 1× GY-9250 (MPU-9250) — 9-axis IMU (accelerometer + gyroscope + magnetometer)
- 1× BMP388 — barometric pressure + temperature
- 1× RYS352A GPS module — observer location + PPS timing
## Schematic
```
SparkFun Level Converter (BOB-12009)
┌──────────────────────────────────────┐
│ │
ESP32 3V3 ──────────────►│ LV HV │◄── ESP32 5V
ESP32 GND ──────────────►│ GND GND │◄── (shared)
│ │
ESP32 GPIO17 (TX) ──────►│ LV1 HV1 │──────► MAX485₁ DI
ESP32 GPIO18 (RX) ◄──────│ LV2 HV2 │◄────── MAX485₂ RO
│ │
│ LV3 (spare) HV3 (spare) │
│ LV4 (spare) HV4 (spare) │
└──────────────────────────────────────┘
MAX485 Board 1 (TX only) MAX485 Board 2 (RX only)
┌────────────────────────┐ ┌────────────────────────┐
│ VCC ◄── 5V │ │ VCC ◄── 5V │
│ GND ◄── GND │ │ GND ◄── GND │
│ │ │ │
│ DI ◄── HV1 │ │ RO ──► HV2 │
│ RO (unused) │ │ DI (unused) │
│ │ │ │
│ DE ◄── 5V ┐ locked │ │ DE ◄── GND ┐ locked │
│ RE ◄── 5V ┘ TX mode │ │ RE ◄── GND ┘ RX mode │
│ │ │ │
│ A ───────────────────┼──► pin 2 │ A ◄──────────────────┼── pin 4
│ B ───────────────────┼──► pin 3 │ B ◄──────────────────┼── pin 5
└────────────────────────┘ └────────────────────────┘
RJ-12 to Carryout G2
┌───────────────────────────┐
│ Pin 1 (White) ── GND │◄── ESP32 GND
│ Pin 2 (Red) ── TX+/TA │◄── A₁
│ Pin 3 (Black) ── TX-/TB │◄── B₁
│ Pin 4 (Yellow) ── RX+/RA │──► A₂
│ Pin 5 (Green) ── RX-/RB │──► B₂
│ Pin 6 (Blue) ── N/C │
└───────────────────────────┘
```
## Power Rails
```
ESP32 5V ──┬── Level Converter HV
├── MAX485₁ VCC
├── MAX485₁ DE + RE (tied high = TX mode)
└── MAX485₂ VCC
ESP32 3V3 ─── Level Converter LV
ESP32 GND ─┬── Level Converter GND
├── MAX485₁ GND
├── MAX485₂ GND
├── MAX485₂ DE + RE (tied low = RX mode)
└── RJ-12 Pin 1
```
## RJ-12 Cable Notes
Straight-wired 6P6C. Pin 1 is leftmost when looking at the jack with the clip
facing away from you (tab down). Wire colors per the standard flat cable:
| Pin | Color | Function | Connects to |
|-----|--------|------------------|--------------------|
| 1 | White | GND | Common ground |
| 2 | Red | TX+ (TA) | MAX485₁ A |
| 3 | Black | TX- (TB) | MAX485₁ B |
| 4 | Yellow | RX+ (RA) | MAX485₂ A |
| 5 | Green | RX- (RB) | MAX485₂ B |
| 6 | Blue | N/C | — |
If crimping your own cable, verify pin-to-color with a multimeter before
connecting to the dish. RJ-12 crimps are easy to get reversed (pins mirror
if the connector is flipped). A wrong connection won't damage anything
(differential signals are current-limited) but communication won't work.
## How It Works
The Carryout G2 uses RS-422 full-duplex: two separate differential pairs,
one for each direction. The MAX485 is a half-duplex RS-485 transceiver with
a shared A/B pair and direction control pins (DE/RE). By hardwiring DE/RE,
each board is locked into a single direction:
- **Board 1 (TX):** DE=HIGH, RE=HIGH → driver always enabled, receiver disabled.
ESP32 UART1 TX → level shifter → DI → differential A/B → G2 serial RX.
- **Board 2 (RX):** DE=LOW, RE=LOW → driver disabled, receiver always enabled.
G2 serial TX → differential A/B → RO → level shifter → ESP32 UART1 RX.
The SparkFun level converter translates between 3.3V (ESP32) and 5V (MAX485)
on both data lines. The two spare channels (LV3/HV3, LV4/HV4) are available
if DE/RE ever need GPIO control for a half-duplex variant.
## Firmware
See `firmware/ble-bridge/` — transparent BLE Nordic UART Service (NUS) bridge.
The firmware is the same regardless of whether the RS-422 transceiver is a
MAX490 (single full-duplex chip) or two MAX485s (locked half-duplex pair).
It only sees UART TX/RX on GPIO17/18.
## Sensors — I2C Bus
The MPU-9250 and BMP388 share a single I2C bus on GPIO8 (SDA) / GPIO9 (SCL).
Both run at 3.3V directly from the ESP32, no level shifting needed.
```
I2C Bus (3.3V, 400kHz)
─────────────────────
ESP32 3V3 ──┬──────────────────┬─── MPU-9250 VCC
│ └─── BMP388 VCC
├── 4.7KΩ ── SDA bus ──┬── MPU-9250 SDA
│ └── BMP388 SDI
└── 4.7KΩ ── SCL bus ──┬── MPU-9250 SCL
└── BMP388 SCK
ESP32 GPIO8 (SDA) ──── SDA bus
ESP32 GPIO9 (SCL) ──── SCL bus
ESP32 GND ──┬── MPU-9250 GND
└── BMP388 GND (SDO to GND = addr 0x76)
MPU-9250 AD0 ── GND (I2C address = 0x68)
BMP388 SDO ── GND (I2C address = 0x76)
```
The 4.7KΩ pull-ups are shared — one pair for the whole bus. Many breakout
boards include onboard pull-ups already; if both the GY-9250 and BMP388
boards have them, the combined parallel resistance (~2.3KΩ) is still fine
for 400kHz I2C at 3.3V. Only add external pull-ups if neither board has them.
### MPU-9250 (GY-9250) — 9-Axis IMU
| I2C Address | 0x68 (AD0 → GND) |
|-------------|-------------------|
| VCC | 3-5V (onboard LDO) |
| Interface | I2C (up to 400kHz) or SPI |
**What it provides for satellite tracking:**
- **Magnetometer (AK8963):** Compass heading for automatic north alignment.
Eliminates manual alignment of dish base "BACK" marking to true north.
Apply local magnetic declination to convert magnetic north → true north.
- **Accelerometer:** Gravity vector → tilt angle = elevation. Independent
verification of the dish firmware's reported EL position.
- **Gyroscope:** Angular rate during slews. Detect oscillation, overshoot,
and vibration for tuning the leapfrog overshoot compensation algorithm.
**Mounting considerations:** The magnetometer is extremely sensitive to nearby
ferrous metals and electromagnetic interference from motors. Mount on the
fixed base plate, away from motor housings, with a known axis aligned to the
dish's reference direction. Rigid mounting — any flex between sensor and dish
structure introduces measurement error.
### BMP388 — Barometric Pressure + Temperature
| I2C Address | 0x76 (SDO → GND) |
|-------------|-------------------|
| VCC | 3.3V |
| Pressure range | 300-1250 hPa |
| Pressure resolution | ±0.01 hPa (±8 cm altitude) |
| Temperature accuracy | ±0.5°C |
| Interface | I2C (up to 3.4MHz) or SPI |
**What it provides for satellite tracking:**
- **Atmospheric refraction correction.** Radio signals bend as they pass
through the atmosphere, especially at low elevation angles. The amount of
bending depends on air pressure and temperature. At 15° elevation (the
Trav'ler's minimum), refraction shifts apparent position by ~0.2°.
Standard refraction models (Bennett, Saemundsson) take pressure and
temperature as inputs — the BMP388 provides both in real time.
- **Temperature monitoring.** Ambient temperature at the dish for thermal
drift awareness and electronics health monitoring.
**Refraction formula (simplified Bennett):**
```
R = 1/tan(el + 7.31/(el + 4.4)) × (P/1010) × (283/(273 + T))
```
Where R is refraction in arcminutes, el is apparent elevation in degrees,
P is pressure in hPa, T is temperature in °C. At el=15°, P=1013, T=20°C:
R ≈ 3.4 arcmin ≈ 0.057°. Small but meaningful for narrow-beam antennas.
## GPS — RYS352A
The RYS352A is a compact GPS module with PPS output. It connects via UART2 and
provides observer location for satellite pass prediction and a 1Hz PPS pulse
for precise UTC time synchronization.
```
ESP32 GPIO5 (UART2 RX) ◄── RYS352A TX (NMEA sentences out)
ESP32 GPIO6 (UART2 TX) ──► RYS352A RX (config commands in, optional)
ESP32 GPIO7 ◄── RYS352A PPS (1Hz rising edge, ~100ns jitter)
ESP32 3V3 ──► RYS352A VCC
ESP32 GND ──► RYS352A GND
```
| Module Pin | ESP32 Pin | Function |
|------------|-----------|----------|
| VCC | 3V3 | 3.3V power (onboard LDO on most breakouts) |
| GND | GND | Ground |
| TX | GPIO5 (UART2 RX) | NMEA sentence output at 115200 baud |
| RX | GPIO6 (UART2 TX) | UBX/NMEA config input (optional) |
| PPS | GPIO7 | 1Hz pulse synchronized to GPS time |
**PPS (Pulse Per Second):** The RYS352A outputs a precise 1Hz pulse on the
rising edge, synchronized to UTC via GPS constellation. The firmware captures
this edge via interrupt (`micros()` timestamp) for correlating satellite events
with sub-microsecond precision relative to the GPS epoch. The module's RTC
battery backup enables warm starts (~5s) after initial cold start fix (~30-60s).
**UART notes:** The RYS352A defaults to 115200 baud NMEA output. The TX line
(GPIO6) is optional — only needed if you want to send UBX configuration
commands to change update rate, constellation selection, or enable additional
NMEA sentences. The firmware uses TinyGPS++ to parse standard GGA/RMC sentences.
## Full GPIO Map
| GPIO | Function | Interface | Notes |
|------|----------|-----------|-------|
| 5 | GPS RX | UART2 RX | ← RYS352A TX (NMEA out) |
| 6 | GPS TX | UART2 TX | → RYS352A RX (config in) |
| 7 | GPS PPS | GPIO interrupt | 1Hz rising edge |
| 8 | I2C SDA | I2C | MPU-9250 + BMP388 (shared bus) |
| 9 | I2C SCL | I2C | MPU-9250 + BMP388 (shared bus) |
| 17 | RS-422 TX | UART1 TX | → Level shifter → MAX485₁ DI |
| 18 | RS-422 RX | UART1 RX | ← Level shifter ← MAX485₂ RO |
| 38 | RGB LED | WS2812 | Onboard NeoPixel (DevKitC V1.1) |
| 43 | USB Console TX | UART0 | CH343 USB-serial (untouched) |
| 44 | USB Console RX | UART0 | CH343 USB-serial (untouched) |
## Loopback Test (no dish)
Before connecting to the G2, verify the bridge by shorting MAX485₁ A to
MAX485₂ A, and MAX485₁ B to MAX485₂ B (loop TX back into RX). Anything
sent via BLE or USB serial should echo back.