Blackbox device type 'file' (SITL) considered working when file handler is available
[inav.git] / src / main / io / rcdevice_cam.c
blob0edbc8bf979b7415b67d74e1cae2cc552e480772
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stddef.h>
22 #include "cms/cms.h"
24 #include "common/printf.h"
25 #include "common/utils.h"
27 #include "fc/rc_controls.h"
28 #include "fc/runtime_config.h"
30 #include "io/beeper.h"
31 #include "io/rcdevice_cam.h"
32 #include "io/osd_joystick.h"
34 #include "rx/rx.h"
36 #ifdef USE_RCDEVICE
38 #define IS_HI(X) (rxGetChannelValue(X) > FIVE_KEY_CABLE_JOYSTICK_MAX)
39 #define IS_LO(X) (rxGetChannelValue(X) < FIVE_KEY_CABLE_JOYSTICK_MIN)
40 #define IS_MID(X) (rxGetChannelValue(X) > FIVE_KEY_CABLE_JOYSTICK_MID_START && rxGetChannelValue(X) < FIVE_KEY_CABLE_JOYSTICK_MID_END)
41 static runcamDevice_t runcamDevice;
42 runcamDevice_t *camDevice = &runcamDevice;
43 rcdeviceSwitchState_t switchStates[BOXCAMERA3 - BOXCAMERA1 + 1];
44 bool rcdeviceInMenu = false;
45 bool isButtonPressed = false;
46 bool waitingDeviceResponse = false;
49 static bool isFeatureSupported(uint8_t feature)
51 #ifndef UNIT_TEST
52 #ifdef USE_LED_STRIP
53 if (!rcdeviceIsEnabled() && osdJoystickEnabled() ) {
54 return true;
56 #endif
57 #endif
59 if (camDevice->info.features & feature) {
60 return true;
63 return false;
66 bool rcdeviceIsEnabled(void)
68 return camDevice->serialPort != NULL;
71 static void rcdeviceCameraControlProcess(void)
73 for (boxId_e i = BOXCAMERA1; i <= BOXCAMERA3; i++) {
74 uint8_t switchIndex = i - BOXCAMERA1;
76 if (IS_RC_MODE_ACTIVE(i)) {
77 // check last state of this mode, if it's true, then ignore it.
78 // Here is a logic to make a toggle control for this mode
79 if (switchStates[switchIndex].isActivated) {
80 continue;
83 uint8_t behavior = RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION;
84 uint8_t behavior1 = RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION;
85 switch (i) {
86 case BOXCAMERA1:
87 if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_SIMULATE_WIFI_BUTTON)) {
88 // avoid display wifi page when arming, in the next firmware(>2.0) of rcsplit we have change the wifi page logic:
89 // when the wifi was turn on it won't turn off the analog video output,
90 // and just put a wifi indicator on the right top of the video output. here is for the old split firmware
91 if (!ARMING_FLAG(ARMED)) {
92 behavior = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_WIFI_BTN;
94 behavior1 = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_WIFI_BTN;
96 break;
97 case BOXCAMERA2:
98 if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_SIMULATE_POWER_BUTTON)) {
99 behavior = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_POWER_BTN;
100 behavior1 = RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_POWER_BTN;
102 break;
103 case BOXCAMERA3:
104 if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_CHANGE_MODE)) {
105 // avoid change camera mode when arming
106 if (!ARMING_FLAG(ARMED)) {
107 behavior = RCDEVICE_PROTOCOL_CAM_CTRL_CHANGE_MODE;
109 behavior1 = RCDEVICE_PROTOCOL_CAM_CTRL_CHANGE_MODE;
111 break;
112 default:
113 break;
115 if ((behavior != RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION) && rcdeviceIsEnabled()) {
116 runcamDeviceSimulateCameraButton(camDevice, behavior);
117 switchStates[switchIndex].isActivated = true;
119 #ifndef UNIT_TEST
120 #ifdef USE_LED_STRIP
121 else if ((behavior1 != RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION) && osdJoystickEnabled()) {
122 switch (behavior1) {
123 case RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_WIFI_BTN:
124 osdJoystickSimulate5KeyButtonPress(RCDEVICE_CAM_KEY_ENTER);
125 break;
126 case RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_POWER_BTN:
127 osdJoystickSimulate5KeyButtonPress(RCDEVICE_CAM_KEY_UP);
128 break;
129 case RCDEVICE_PROTOCOL_CAM_CTRL_CHANGE_MODE:
130 osdJoystickSimulate5KeyButtonPress(RCDEVICE_CAM_KEY_DOWN);
131 break;
133 switchStates[switchIndex].isActivated = true;
135 #endif
136 #endif
137 UNUSED(behavior1);
138 } else {
139 #ifndef UNIT_TEST
140 #ifdef USE_LED_STRIP
141 if (osdJoystickEnabled() && switchStates[switchIndex].isActivated) {
142 osdJoystickSimulate5KeyButtonRelease();
144 #endif
145 #endif
146 switchStates[switchIndex].isActivated = false;
151 static void rcdeviceSimulationOSDCableFailed(rcdeviceResponseParsingContext_t *ctx)
153 waitingDeviceResponse = false;
154 if (ctx->command == RCDEVICE_PROTOCOL_COMMAND_5KEY_CONNECTION) {
155 uint8_t operationID = ctx->paramData[0];
156 if (operationID == RCDEVICE_PROTOCOL_5KEY_CONNECTION_CLOSE) {
157 return;
162 static void rcdeviceSimulationRespHandle(rcdeviceResponseParsingContext_t *ctx)
164 if (ctx->result != RCDEVICE_RESP_SUCCESS) {
165 rcdeviceSimulationOSDCableFailed(ctx);
166 waitingDeviceResponse = false;
167 return;
170 switch (ctx->command) {
171 case RCDEVICE_PROTOCOL_COMMAND_5KEY_SIMULATION_RELEASE:
172 isButtonPressed = false;
173 break;
174 case RCDEVICE_PROTOCOL_COMMAND_5KEY_CONNECTION:
176 // the high 4 bits is the operationID that we sent
177 // the low 4 bits is the result code
178 isButtonPressed = true;
179 uint8_t operationID = ctx->paramData[0];
180 bool errorCode = (ctx->recvBuf[1] & 0x0F);
181 if (operationID == RCDEVICE_PROTOCOL_5KEY_CONNECTION_OPEN) {
182 if (errorCode == 1) {
183 rcdeviceInMenu = true;
184 beeper(BEEPER_CAM_CONNECTION_OPEN);
185 } else {
186 beeper(BEEPER_CAM_CONNECTION_CLOSE);
188 } else if (operationID == RCDEVICE_PROTOCOL_5KEY_CONNECTION_CLOSE) {
189 if (errorCode == 1) {
190 rcdeviceInMenu = false;
191 beeper(BEEPER_CAM_CONNECTION_CLOSE);
195 break;
196 case RCDEVICE_PROTOCOL_COMMAND_5KEY_SIMULATION_PRESS:
197 isButtonPressed = true;
198 break;
201 waitingDeviceResponse = false;
204 static void rcdeviceCamSimulate5KeyCablePress(rcdeviceCamSimulationKeyEvent_e key)
206 uint8_t operation = RCDEVICE_PROTOCOL_5KEY_SIMULATION_NONE;
207 switch (key) {
208 case RCDEVICE_CAM_KEY_LEFT:
209 operation = RCDEVICE_PROTOCOL_5KEY_SIMULATION_LEFT;
210 break;
211 case RCDEVICE_CAM_KEY_UP:
212 operation = RCDEVICE_PROTOCOL_5KEY_SIMULATION_UP;
213 break;
214 case RCDEVICE_CAM_KEY_RIGHT:
215 operation = RCDEVICE_PROTOCOL_5KEY_SIMULATION_RIGHT;
216 break;
217 case RCDEVICE_CAM_KEY_DOWN:
218 operation = RCDEVICE_PROTOCOL_5KEY_SIMULATION_DOWN;
219 break;
220 case RCDEVICE_CAM_KEY_ENTER:
221 operation = RCDEVICE_PROTOCOL_5KEY_SIMULATION_SET;
222 break;
223 case RCDEVICE_CAM_KEY_NONE:
224 default:
225 operation = RCDEVICE_PROTOCOL_5KEY_SIMULATION_NONE;
226 break;
229 runcamDeviceSimulate5KeyOSDCableButtonPress(camDevice, operation, rcdeviceSimulationRespHandle);
232 void rcdeviceSend5KeyOSDCableSimualtionEvent(rcdeviceCamSimulationKeyEvent_e key)
234 switch (key) {
235 case RCDEVICE_CAM_KEY_CONNECTION_OPEN:
236 runcamDeviceOpen5KeyOSDCableConnection(camDevice, rcdeviceSimulationRespHandle);
237 break;
238 case RCDEVICE_CAM_KEY_CONNECTION_CLOSE:
239 runcamDeviceClose5KeyOSDCableConnection(camDevice, rcdeviceSimulationRespHandle);
240 break;
241 case RCDEVICE_CAM_KEY_ENTER:
242 case RCDEVICE_CAM_KEY_LEFT:
243 case RCDEVICE_CAM_KEY_UP:
244 case RCDEVICE_CAM_KEY_RIGHT:
245 case RCDEVICE_CAM_KEY_DOWN:
246 rcdeviceCamSimulate5KeyCablePress(key);
247 break;
248 case RCDEVICE_CAM_KEY_RELEASE:
249 runcamDeviceSimulate5KeyOSDCableButtonRelease(camDevice, rcdeviceSimulationRespHandle);
250 break;
251 case RCDEVICE_CAM_KEY_NONE:
252 default:
253 break;
257 static void rcdevice5KeySimulationProcess(timeUs_t currentTimeUs)
259 UNUSED(currentTimeUs);
261 #ifdef USE_CMS
262 if (cmsInMenu) {
263 return;
265 #endif
267 if (ARMING_FLAG(ARMED)) {
268 return;
271 if (isButtonPressed) {
272 if (IS_MID(YAW) && IS_MID(PITCH) && IS_MID(ROLL)) {
273 if ( rcdeviceIsEnabled() ) {
274 rcdeviceSend5KeyOSDCableSimualtionEvent(RCDEVICE_CAM_KEY_RELEASE);
275 waitingDeviceResponse = true;
277 #ifndef UNIT_TEST
278 #ifdef USE_LED_STRIP
279 else if (osdJoystickEnabled()) {
280 osdJoystickSimulate5KeyButtonRelease();
281 isButtonPressed = false;
283 #endif
284 #endif
286 } else {
287 if (waitingDeviceResponse) {
288 return;
291 rcdeviceCamSimulationKeyEvent_e key = RCDEVICE_CAM_KEY_NONE;
293 if (IS_MID(THROTTLE) && IS_MID(ROLL) && IS_MID(PITCH) && IS_LO(YAW)) { // Disconnect Lo YAW
294 if (rcdeviceInMenu) {
295 key = RCDEVICE_CAM_KEY_CONNECTION_CLOSE;
297 } else {
298 if (rcdeviceInMenu) {
299 if (IS_LO(ROLL)) { // Left LO ROLL
300 key = RCDEVICE_CAM_KEY_LEFT;
301 } else if (IS_HI(PITCH)) { // Up HI PITCH
302 key = RCDEVICE_CAM_KEY_UP;
303 } else if (IS_HI(ROLL)) { // Right HI ROLL
304 key = RCDEVICE_CAM_KEY_RIGHT;
305 } else if (IS_LO(PITCH)) { // Down LO PITCH
306 key = RCDEVICE_CAM_KEY_DOWN;
307 } else if (IS_MID(THROTTLE) && IS_MID(ROLL) && IS_MID(PITCH) && IS_HI(YAW)) { // Enter HI YAW
308 key = RCDEVICE_CAM_KEY_ENTER;
310 } else {
311 if (IS_MID(THROTTLE) && IS_MID(ROLL) && IS_MID(PITCH) && IS_HI(YAW)) { // Enter HI YAW
312 key = RCDEVICE_CAM_KEY_CONNECTION_OPEN;
317 if (key != RCDEVICE_CAM_KEY_NONE) {
318 if ( rcdeviceIsEnabled() ) {
319 rcdeviceSend5KeyOSDCableSimualtionEvent(key);
320 waitingDeviceResponse = true;
322 #ifndef UNIT_TEST
323 #ifdef USE_LED_STRIP
324 else if (osdJoystickEnabled()) {
325 if ( key == RCDEVICE_CAM_KEY_CONNECTION_OPEN ) {
326 rcdeviceInMenu = true;
327 } else if ( key == RCDEVICE_CAM_KEY_CONNECTION_CLOSE ) {
328 rcdeviceInMenu = false;
329 } else {
330 osdJoystickSimulate5KeyButtonPress(key);
333 #endif
334 #endif
335 isButtonPressed = true;
340 void rcdeviceUpdate(timeUs_t currentTimeUs)
342 if ( rcdeviceIsEnabled() ) {
343 rcdeviceReceive(currentTimeUs);
346 rcdeviceCameraControlProcess();
348 rcdevice5KeySimulationProcess(currentTimeUs);
351 void rcdeviceInit(void)
353 // open serial port
354 runcamDeviceInit(camDevice);
356 for (boxId_e i = BOXCAMERA1; i <= BOXCAMERA3; i++) {
357 uint8_t switchIndex = i - BOXCAMERA1;
358 switchStates[switchIndex].isActivated = true;
362 #endif