#include
namespace obb::tools::gimbal {
namespace {
ControllerState s_state;
controller_callback_t s_callback = nullptr;
unsigned long s_last_poll_ms = 0;
constexpr uint8_t kI2CAddr = 0x28; // RP2040 helper (tiny USB HID bridge)
bool read_stick_from_i2c(uint8_t channel, StickState &stick) {
Wire.beginTransmission(kI2CAddr);
Wire.write(channel);
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(kI2CAddr, static_cast(5)) != 5) {
return false;
}
stick.x = (Wire.read() << 8) | Wire.read();
stick.y = (Wire.read() << 8) | Wire.read();
stick.press = Wire.read();
return true;
}
bool read_keypad_mask(uint32_t &mask) {
Wire.beginTransmission(kI2CAddr);
Wire.write(0x80); // keypad register
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(kI2CAddr, static_cast(3)) != 3) {
return false;
}
mask = Wire.re">
#include
namespace obb::tools::gimbal {
namespace {
ControllerState s_state;
controller_callback_t s_callback = nullptr;
unsigned long s_last_poll_ms = 0;
constexpr uint8_t kI2CAddr = 0x28; // RP2040 helper (tiny USB HID bridge)
bool read_stick_from_i2c(uint8_t channel, StickState &stick) {
Wire.beginTransmission(kI2CAddr);
Wire.write(channel);
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(kI2CAddr, static_cast(5)) != 5) {
return false;
}
stick.x = (Wire.read() << 8) | Wire.read();
stick.y = (Wire.read() << 8) | Wire.read();
stick.press = Wire.read();
return true;
}
bool read_keypad_mask(uint32_t &mask) {
Wire.beginTransmission(kI2CAddr);
Wire.write(0x80); // keypad register
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(kI2CAddr, static_cast(3)) != 3) {
return false;
}
mask = Wire.re">
#include
namespace obb::tools::gimbal {
namespace {
ControllerState s_state;
controller_callback_t s_callback = nullptr;
unsigned long s_last_poll_ms = 0;
constexpr uint8_t kI2CAddr = 0x28; // RP2040 helper (tiny USB HID bridge)
bool read_stick_from_i2c(uint8_t channel, StickState &stick) {
Wire.beginTransmission(kI2CAddr);
Wire.write(channel);
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(kI2CAddr, static_cast(5)) != 5) {
return false;
}
stick.x = (Wire.read() << 8) | Wire.read();
stick.y = (Wire.read() << 8) | Wire.read();
stick.press = Wire.read();
return true;
}
bool read_keypad_mask(uint32_t &mask) {
Wire.beginTransmission(kI2CAddr);
Wire.write(0x80); // keypad register
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(kI2CAddr, static_cast(3)) != 3) {
return false;
}
mask = Wire.re">
#include "app/tools/obb_dual_gimbal.h"
#include <Arduino.h>
#include <Wire.h>
namespace obb::tools::gimbal {
namespace {
ControllerState s_state;
controller_callback_t s_callback = nullptr;
unsigned long s_last_poll_ms = 0;
constexpr uint8_t kI2CAddr = 0x28; // RP2040 helper (tiny USB HID bridge)
bool read_stick_from_i2c(uint8_t channel, StickState &stick) {
Wire.beginTransmission(kI2CAddr);
Wire.write(channel);
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(kI2CAddr, static_cast<uint8_t>(5)) != 5) {
return false;
}
stick.x = ([Wire.read](<http://Wire.read>)() << 8) | [Wire.read](<http://Wire.read>)();
stick.y = ([Wire.read](<http://Wire.read>)() << 8) | [Wire.read](<http://Wire.read>)();
[stick.press](<http://stick.press>) = [Wire.read](<http://Wire.read>)();
return true;
}
bool read_keypad_mask(uint32_t &mask) {
Wire.beginTransmission(kI2CAddr);
Wire.write(0x80); // keypad register
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(kI2CAddr, static_cast<uint8_t>(3)) != 3) {
return false;
}
mask = [Wire.read](<http://Wire.read>)();
mask |= [Wire.read](<http://Wire.read>)() << 8;
mask |= [Wire.read](<http://Wire.read>)() << 16;
return true;
}
void notify() {
if (s_callback) {
s_callback(s_state);
}
}
} // namespace
void init() {
Wire.begin();
s_state = ControllerState{};
s_last_poll_ms = 0;
}
void poll() {
const auto now = millis();
if (now - s_last_poll_ms < 15) {
return; // ~60 Hz max
}
s_last_poll_ms = now;
StickState left;
StickState right;
uint32_t keypad = 0;
const bool left_ok = read_stick_from_i2c(0x00, left);
const bool right_ok = read_stick_from_i2c(0x10, right);
const bool keypad_ok = read_keypad_mask(keypad);
const bool online = left_ok || right_ok || keypad_ok;
s_state.rp2040_online = online;
if (left_ok) {
s_state.left = left;
}
if (right_ok) {
s_state.right = right;
}
if (keypad_ok) {
s_state.keypad_mask = keypad;
}
notify();
}
void set_callback(controller_callback_t cb) {
s_callback = std::move(cb);
}
const ControllerState &last_state() {
return s_state;
}
} // namespace obb::tools::gimbal