-
Notifications
You must be signed in to change notification settings - Fork 0
hardware
All pin assignments and I2C addresses live in a single file:
lib/CONSTANTS/rocat_constants.h.
Always check that file first when touching any peripheral.
flowchart TB
ESP[ESP32]
subgraph I2C["I2C bus (SDA=GPIO4, SCL=GPIO27, 100 kHz)"]
SX[SX1509 IO expander 0x3E]
LIS[LIS3DH accelerometer 0x18]
PCF[PCF8523 RTC]
TMP[TMP102 temp sensor 0x48]
end
ESP --- I2C
ESP -->|8-bit parallel + A0/EN| LCD[SED1530 LCD 100x48]
ESP -->|GPIO15| WS[6x WS2812 LEDs]
ESP -->|GPIO25 DAC1 / I2S0| AMP[Audio amp + speaker]
SX -->|dir/spd/stby/en| DRV[STSPIN240 motor driver]
DRV --> M1[Motor 1]
DRV --> M2[Motor 2]
SX --> LCDPWR[LCD power + backlight + RW]
SX --> RGBPWR[WS2812 power rail]
SX --> AUDEN[Audio amp enable]
SX --> KEEP[Power keep-awake latch]
CHG[Charger IC] -->|CHG / STBY indicators| SX
LIS -->|ADC1 via divider| BATT[Battery voltage]
LIS -->|INT1 → GPIO2| ESP
PCF -->|INT1 → GPIO12| ESP
| GPIO | Constant | Function |
|---|---|---|
| 2 | pin_acc_int1 |
LIS3DH INT1 (motion/click wake; RTC-capable) |
| 4 | pin_gio_sda |
I2C SDA |
| 5, 9, 10, 18, 19, 21, 22, 23 | pin_display[] |
LCD 8-bit data bus (order: 9, 10, 5, 18, 23, 19, 22, 21) |
| 12 | pin_rtc_int1 |
PCF8523 INT1 (alarm wake). Polled, not interrupt-driven — GPIO interrupts false-triggered from I2C coupling on the same wire |
| 13 | pin_dis_en |
LCD EN strobe |
| 14 | pin_dis_ao |
LCD A0 (command/data) |
| 15 | pin_gio_ws2812 |
WS2812 data |
| 25 | pin_aud_sig |
Audio DAC1 output |
| 27 | pin_gio_scl |
I2C SCL |
| 32, 33 | pin_touch_pet_sens_1/2 |
Capacitive touch pads (interleaved strip A,B,A,B) |
| 34 | pin_gpio_js_a_c1 |
ADC keys ch. 0 → DOWN / UP |
| 35 | pin_gpio_js_b_d1 |
ADC keys ch. 1 → LEFT / RIGHT |
| 36 | pin_gpio_js_c1_2 |
ADC keys ch. 2 → OK / CANCEL (also RTC ch. 0, wake button) |
| 37 | pin_gpio_js_a_c2 |
ADC keys ch. 3 → FUNC_1_DOWN / FUNC_2_UP |
| 38 | pin_gpio_js_b_d2 |
ADC keys ch. 4 → FUNC_3_LEFT / FUNC_4_RIGHT |
| 39 | pin_gpio_ldr |
LDR (light sensor) analog input |
| Pin | Constant | Function |
|---|---|---|
| 1 | pin_m_fault |
Motor driver nFAULT (active-low input) |
| 2 | pin_m_stby |
Motor driver STBY/RESET |
| 3 | pin_m_drv_en |
Motor driver enable |
| 4 | pin_m1_dir |
Motor 1 direction |
| 5 | pin_m2_dir |
Motor 2 direction |
| 6 | pin_m1_spd |
Motor 1 speed (PWM) |
| 7 | pin_m2_spd |
Motor 2 speed (PWM) |
| 8 | pin_chg_ind |
Charger "charging" indicator (open-drain, active-low) |
| 9 | pin_stby_ind |
Charger "standby / charge complete" indicator (open-drain, active-low) |
| 10 | pin_dis_pwr |
LCD power |
| 11 | pin_dis_bl |
LCD backlight |
| 12 | pin_dis_rw |
LCD R/W |
| 13 | pin_aud_en |
Audio amplifier enable |
| 14 | pin_mcu_keep_awake |
Power latch — HIGH keeps the whole device powered; driving LOW powers off |
| 15 | pin_rgb_pwr |
WS2812 power rail switch |
Init failure policy: if
io.begin()fails at boot,setup()loops forever on purpose. The SX1509's reset is tied to the ESP32's reset, so only the physical reset button recovers both chips into a consistent state.
| Address | Device | Used by |
|---|---|---|
| 0x3E | SX1509 IO expander | motors, LCD power/backlight/RW, RGB power, audio enable, charger indicators, power latch |
| 0x18 | LIS3DH accelerometer | motion/orientation, temperature, battery voltage via its ADC1 input, motion/click wake interrupts |
| PCF8523 (0x68 fixed) | External RTC | timekeeping, daily alarm, alarm wake |
| 0x48 | TMP102 | temperature sensor (driver present, not wired into the main loop) |
lib/LCD/SED1530_LCD.h — SED1530/SED1531 controller, 100×48 px monochrome,
8-bit parallel bus. The class extends GFXcanvas1 (Adafruit_GFX), so all
standard GFX primitives draw into a RAM framebuffer; updateWholeScreen() /
updatePages() push it to the panel.
-
Icon markers: the panel has a separate icon RAM row addressed via
setMarker(id, on)— independent of the framebuffer, used for the status bar (battery / star / arrows-cross). See GUI & Carousel. -
Backlight via
setBacklight(bool)(SX1509 pin 11). -
Font restriction: only
setTextSize(1)renders correctly on this panel; size 2+ is broken. Default 6×8 px font → 16×6 characters.
Six WS2812 pixels (count_ws2812 = 6), driven by Adafruit_NeoPixel on GPIO 15,
powered through SX1509 pin 15:
| Index | Position |
|---|---|
| 0 | Left eye |
| 1 | Right eye |
| 2 | Left ear, right half |
| 3 | Left ear, left half |
| 4 | Right ear, left half |
| 5 | Right ear, right half |
The eyes (0/1) face the user; LedDriver::show() forces them dark whenever
set_eyes_suppressed(true) (active during games and the menu).
Two DC motors through an STSPIN240 dual H-bridge, all control lines on the
SX1509. MotorDriver::set_motorN(speed) takes −100…100 (%); the sign is
direction and the magnitude maps onto a raw PWM duty window of 66…255.
The mapping is inverted: a low raw duty value is max power and a
high raw duty value is the weakest setting (confirmed by testing, twice —
see the comment in motor_driver.cpp, don't "fix" this on sight).
set_enabled() sequences STBY→EN in the order the chip requires; fault()
reads latched overcurrent/thermal/undervoltage faults from nFAULT.
There is no fuel gauge. The battery voltage reaches the LIS3DH's auxiliary
ADC1 through a voltage divider (ratio ≈ 0.3226). BatteryDriver converts the
10-bit reading to millivolts at the battery
(raw*0.78125 + 800 mV at the ADC pin, then /0.3226 + 316 mV offset
calibration to get the battery-side value) and maintains min/max/avg windows:
a 2 s base window of 100 samples at 50 Hz, cascaded into a 1 min tier (30×2s)
and a 5 min tier (5×1min) — that's the full cascade, there's no 15 min/1 h/2 h
tier. Charger status comes from the two SX1509 indicator pins
(active-low: chg = charging, stby = charge complete).
The battery-low unit mismatch that used to affect CarouselManager's LCD
marker and MoodManager's threshold (comparing a millivolt value against a
bare 3.4f) has been fixed — both now use a millivolt constant
(BATTERY_LOW_MV = 3400.0f) compared directly against the millivolt reading.
Single-channel analog audio on DAC1 (GPIO 25) via the arduino-audio-tools
AnalogAudioStream (I2S0 in internal-DAC mode), through an amplifier gated by
SX1509 pin 13.
-
Two 4-way joysticks + center buttons are read as five analog channels
(resistor-ladder encoding, two buttons per ADC pin — thresholds 403 mV /
1040 mV distinguish none/low/high). Debounced by a 3-sample majority filter
in
AdcKeys, with hold-repeat. See GUI & Carousel for the logical key map. -
Touch strip: two ESP32 touch channels wired to interleaved pads
(A,B,A,B). At least 3 A/B transitions (
MIN_TRANSITIONS), each within 800 ms of the last (STROKE_GAP_MS), and spanning ≥3 s total (GESTURE_MIN_MS) yieldsPetGesture::UPorDOWN(used as "petting" by the mood engine). -
LDR on GPIO 39, converted to ohms, EMA-smoothed in
loop().