TinyRobotics
Loading...
Searching...
No Matches
RCGamepadMessageSource.h
1#pragma once
2#include "GamepadServer.h" // install https://github.com/pschatzmann/VirtualGamePadArduino
4#include "TinyRobotics/utils/Common.h"
5
6namespace tinyrobotics {
7
8/**
9 * @class RCGamepadMessageSource
10 * @ingroup communication
11 * @brief Message source for remote control via a virtual gamepad.
12 *
13 * @note This class depends on the VirtualGamePadArduino library:
14 * https://github.com/pschatzmann/VirtualGamePadArduino
15 *
16 * @note this is an optional class, so it must be included in your sketch with:
17 * #include "TinyRobotics/communication/RCGamepadMessageSource.h"
18 *
19 * This class connects to a GamepadServer and translates gamepad input into
20 * TinyRobotics Message objects for remote control scenarios (Car, Boat, etc).
21 * It supports different navigation scenarios and publishes control messages
22 * (steering, throttle, turn, etc) based on the selected scenario.
23 *
24 * Usage:
25 * RCGamepadMessageSource source(server);
26 * source.begin(NaviationScenario::Car);
27 * // In loop: source.update();
28 *
29 * ## Callback Mechanism
30 *
31 * You can register a custom callback using setCallback(). The callback is
32 * invoked whenever a new GamepadState is received. If the callback returns
33 * false, the default message publishing is skipped, allowing you to publish
34 * custom messages instead. If the callback returns true (or is not set), the
35 * default messages for the selected scenario are published.
36 *
37 * Example:
38 * source.setCallback([](const GamepadState& state, MessageSource& src) {
39 * // Custom message publishing logic
40 * return false; // skip default publishing
41 * });
42 *
43 * The class is designed for integration with the TinyRobotics message bus and
44 * remote control framework.
45 */
47 public:
48 RCGamepadMessageSource(NetworkServer& server) : gamepad(server) {}
49
50 /// Start the message source and connect to the gamepad server
51 bool begin(ControlScenario scenario = ControlScenario::Car) {
52 this->scenario = scenario;
53 gamepad.begin();
54 is_active_ = true;
55 return true;
56 }
57
58 /// Stop the message source and disconnect from the gamepad server
59 void end() { is_active_ = false; }
60
61 /// Call this in your main loop to handle incoming gamepad messages and
62 /// publish control messages
63 void update() {
64 if (is_active_) {
65 gamepad.handleClient();
66 // Poll for new state and publish if changed
67 GamepadState state = gamepad.getState();
68 if (callback == nullptr || callback(state, *this)) {
69 publish(state);
70 }
71 }
72 }
73
74 /// Add a callback to receive and publish additional messages
75 void setCallback(bool (*cb)(const GamepadState&, MessageSource& source)) {
76 callback = cb;
77 }
78
79 protected:
80 GamepadServer gamepad;
81 ControlScenario scenario;
82 bool (*callback)(const GamepadState&, MessageSource& source) = nullptr;
83 bool is_active_ = false;
84
85 void publish(const GamepadState& state) {
86 switch (scenario) {
87 case ControlScenario::Car:
88 publishCar(state);
89 break;
90 case ControlScenario::Boat:
91 publishBoat(state);
92 break;
93 case ControlScenario::Drone:
94 publishDrone(state);
95 break;
96 case ControlScenario::Plane:
97 publishPlane(state);
98 break;
99 }
100 }
101
102 // Overload for publishing Message<float>
103 void publish(const Message<float>& msg) {
104 // Use sendMessage to forward to all handlers
105 // Make a non-const copy as sendMessage expects non-const
106 Message<float> m = msg;
107 sendMessage(m);
108 }
109
110 void publishCar(const GamepadState& state) {
111 float steeringAngleDegree = state.left_thumbstick.angleDegROS();
112 float throttlePercent = 100.0f * state.left_thumbstick.magnitude();
113 float turnPercent = steeringAngleDegree / 180.0f * 100.0f;
114 publish(Message(MessageContent::SteeringAngle, steeringAngleDegree,
116 publish(Message(MessageContent::Throttle, throttlePercent, Unit::Percent));
117 publish(Message(MessageContent::Turn, turnPercent, Unit::Percent));
118 }
119
120 void publishDrone(const GamepadState& state) {
121 float rollAngleDegree = state.left_thumbstick.angleDegROS();
122 float pitchAngleDegree = state.right_thumbstick.angleDegROS();
123 float throttlePercent = 100.0f * state.left_thumbstick.magnitude();
124 float yawTurnPercent = 100.0f * state.right_thumbstick.magnitude();
125 publish(Message(MessageContent::Roll, rollAngleDegree, Unit::AngleDegree));
126 publish(
127 Message(MessageContent::Pitch, pitchAngleDegree, Unit::AngleDegree));
128 publish(Message(MessageContent::Throttle, throttlePercent, Unit::Percent));
129 publish(Message(MessageContent::Yaw, yawTurnPercent, Unit::Percent));
130 }
131
132 void publishPlane(const GamepadState& state) {
133 float aileronAngleDegree = state.left_thumbstick.angleDegROS();
134 float elevatorAngleDegree = state.right_thumbstick.angleDegROS();
135 float throttlePercent = 100.0f * state.left_thumbstick.magnitude();
136 float rudderTurnPercent = 100.0f * state.right_thumbstick.magnitude();
137 publish(Message(MessageContent::Roll, aileronAngleDegree,
138 Unit::AngleDegree)); // Aileron
139 publish(Message(MessageContent::Pitch, elevatorAngleDegree,
140 Unit::AngleDegree)); // Elevator
141 publish(Message(MessageContent::Throttle, throttlePercent,
142 Unit::Percent)); // Throttle
143 publish(Message(MessageContent::Yaw, rudderTurnPercent,
144 Unit::Percent)); // Rudder
145 }
146
147 void publishBoat(const GamepadState& state) {
148 float steeringAngleDegree = state.left_thumbstick.angleDegROS();
149 float throttlePercent = 100.0f * state.left_thumbstick.magnitude();
150 float turnPercent = steeringAngleDegree / 180.0f * 100.0f;
151
152 publish(Message(MessageContent::SteeringAngle, steeringAngleDegree,
154 publish(Message(MessageContent::Throttle, throttlePercent, Unit::Percent));
155 }
156
157 // No static callback needed; polling is used in update()
158};
159
160} // namespace tinyrobotics
Base class for message sources in the TinyRobotics communication framework.
Definition: MessageSource.h:35
Message source for remote control via a virtual gamepad.
Definition: RCGamepadMessageSource.h:46
void setCallback(bool(*cb)(const GamepadState &, MessageSource &source))
Add a callback to receive and publish additional messages.
Definition: RCGamepadMessageSource.h:75
bool begin(ControlScenario scenario=ControlScenario::Car)
Start the message source and connect to the gamepad server.
Definition: RCGamepadMessageSource.h:51
void end()
Stop the message source and disconnect from the gamepad server.
Definition: RCGamepadMessageSource.h:59
void update()
Definition: RCGamepadMessageSource.h:63
ControlScenario
Control scenario types for remote control vehicles.
Definition: Common.h:68
Unit
Units for message values.
Definition: Common.h:45
@ AngleDegree
Angle in degrees.
@ Percent
Percentage (0-100)
Generic message structure for communication, parameterized by value type.
Definition: Message.h:72