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/>.
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"
37 #define IS_HI(X) (rxGetChannelValue(X) > FIVE_KEY_CABLE_JOYSTICK_MAX)
38 #define IS_LO(X) (rxGetChannelValue(X) < FIVE_KEY_CABLE_JOYSTICK_MIN)
39 #define IS_MID(X) (rxGetChannelValue(X) > FIVE_KEY_CABLE_JOYSTICK_MID_START && rxGetChannelValue(X) < FIVE_KEY_CABLE_JOYSTICK_MID_END)
40 static runcamDevice_t runcamDevice
;
41 runcamDevice_t
*camDevice
= &runcamDevice
;
42 rcdeviceSwitchState_t switchStates
[BOXCAMERA3
- BOXCAMERA1
+ 1];
43 bool rcdeviceInMenu
= false;
44 bool isButtonPressed
= false;
45 bool waitingDeviceResponse
= false;
48 static bool isFeatureSupported(uint8_t feature
)
50 if (camDevice
->info
.features
& feature
) {
57 bool rcdeviceIsEnabled(void)
59 return camDevice
->serialPort
!= NULL
;
62 static void rcdeviceCameraControlProcess(void)
64 for (boxId_e i
= BOXCAMERA1
; i
<= BOXCAMERA3
; i
++) {
65 uint8_t switchIndex
= i
- BOXCAMERA1
;
67 if (IS_RC_MODE_ACTIVE(i
)) {
68 // check last state of this mode, if it's true, then ignore it.
69 // Here is a logic to make a toggle control for this mode
70 if (switchStates
[switchIndex
].isActivated
) {
74 uint8_t behavior
= RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION
;
77 if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_SIMULATE_WIFI_BUTTON
)) {
78 // avoid display wifi page when arming, in the next firmware(>2.0) of rcsplit we have change the wifi page logic:
79 // when the wifi was turn on it won't turn off the analog video output,
80 // and just put a wifi indicator on the right top of the video output. here is for the old split firmware
81 if (!ARMING_FLAG(ARMED
)) {
82 behavior
= RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_WIFI_BTN
;
87 if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_SIMULATE_POWER_BUTTON
)) {
88 behavior
= RCDEVICE_PROTOCOL_CAM_CTRL_SIMULATE_POWER_BTN
;
92 if (isFeatureSupported(RCDEVICE_PROTOCOL_FEATURE_CHANGE_MODE
)) {
93 // avoid change camera mode when arming
94 if (!ARMING_FLAG(ARMED
)) {
95 behavior
= RCDEVICE_PROTOCOL_CAM_CTRL_CHANGE_MODE
;
102 if (behavior
!= RCDEVICE_PROTOCOL_CAM_CTRL_UNKNOWN_CAMERA_OPERATION
) {
103 runcamDeviceSimulateCameraButton(camDevice
, behavior
);
104 switchStates
[switchIndex
].isActivated
= true;
107 switchStates
[switchIndex
].isActivated
= false;
112 static void rcdeviceSimulationOSDCableFailed(rcdeviceResponseParsingContext_t
*ctx
)
114 waitingDeviceResponse
= false;
115 if (ctx
->command
== RCDEVICE_PROTOCOL_COMMAND_5KEY_CONNECTION
) {
116 uint8_t operationID
= ctx
->paramData
[0];
117 if (operationID
== RCDEVICE_PROTOCOL_5KEY_CONNECTION_CLOSE
) {
123 static void rcdeviceSimulationRespHandle(rcdeviceResponseParsingContext_t
*ctx
)
125 if (ctx
->result
!= RCDEVICE_RESP_SUCCESS
) {
126 rcdeviceSimulationOSDCableFailed(ctx
);
127 waitingDeviceResponse
= false;
131 switch (ctx
->command
) {
132 case RCDEVICE_PROTOCOL_COMMAND_5KEY_SIMULATION_RELEASE
:
133 isButtonPressed
= false;
135 case RCDEVICE_PROTOCOL_COMMAND_5KEY_CONNECTION
:
137 // the high 4 bits is the operationID that we sent
138 // the low 4 bits is the result code
139 isButtonPressed
= true;
140 uint8_t operationID
= ctx
->paramData
[0];
141 bool errorCode
= (ctx
->recvBuf
[1] & 0x0F);
142 if (operationID
== RCDEVICE_PROTOCOL_5KEY_CONNECTION_OPEN
) {
143 if (errorCode
== 1) {
144 rcdeviceInMenu
= true;
145 beeper(BEEPER_CAM_CONNECTION_OPEN
);
147 beeper(BEEPER_CAM_CONNECTION_CLOSE
);
149 } else if (operationID
== RCDEVICE_PROTOCOL_5KEY_CONNECTION_CLOSE
) {
150 if (errorCode
== 1) {
151 rcdeviceInMenu
= false;
152 beeper(BEEPER_CAM_CONNECTION_CLOSE
);
157 case RCDEVICE_PROTOCOL_COMMAND_5KEY_SIMULATION_PRESS
:
158 isButtonPressed
= true;
162 waitingDeviceResponse
= false;
165 static void rcdeviceCamSimulate5KeyCablePress(rcdeviceCamSimulationKeyEvent_e key
)
167 uint8_t operation
= RCDEVICE_PROTOCOL_5KEY_SIMULATION_NONE
;
169 case RCDEVICE_CAM_KEY_LEFT
:
170 operation
= RCDEVICE_PROTOCOL_5KEY_SIMULATION_LEFT
;
172 case RCDEVICE_CAM_KEY_UP
:
173 operation
= RCDEVICE_PROTOCOL_5KEY_SIMULATION_UP
;
175 case RCDEVICE_CAM_KEY_RIGHT
:
176 operation
= RCDEVICE_PROTOCOL_5KEY_SIMULATION_RIGHT
;
178 case RCDEVICE_CAM_KEY_DOWN
:
179 operation
= RCDEVICE_PROTOCOL_5KEY_SIMULATION_DOWN
;
181 case RCDEVICE_CAM_KEY_ENTER
:
182 operation
= RCDEVICE_PROTOCOL_5KEY_SIMULATION_SET
;
184 case RCDEVICE_CAM_KEY_NONE
:
186 operation
= RCDEVICE_PROTOCOL_5KEY_SIMULATION_NONE
;
190 runcamDeviceSimulate5KeyOSDCableButtonPress(camDevice
, operation
, rcdeviceSimulationRespHandle
);
193 void rcdeviceSend5KeyOSDCableSimualtionEvent(rcdeviceCamSimulationKeyEvent_e key
)
196 case RCDEVICE_CAM_KEY_CONNECTION_OPEN
:
197 runcamDeviceOpen5KeyOSDCableConnection(camDevice
, rcdeviceSimulationRespHandle
);
199 case RCDEVICE_CAM_KEY_CONNECTION_CLOSE
:
200 runcamDeviceClose5KeyOSDCableConnection(camDevice
, rcdeviceSimulationRespHandle
);
202 case RCDEVICE_CAM_KEY_ENTER
:
203 case RCDEVICE_CAM_KEY_LEFT
:
204 case RCDEVICE_CAM_KEY_UP
:
205 case RCDEVICE_CAM_KEY_RIGHT
:
206 case RCDEVICE_CAM_KEY_DOWN
:
207 rcdeviceCamSimulate5KeyCablePress(key
);
209 case RCDEVICE_CAM_KEY_RELEASE
:
210 runcamDeviceSimulate5KeyOSDCableButtonRelease(camDevice
, rcdeviceSimulationRespHandle
);
212 case RCDEVICE_CAM_KEY_NONE
:
218 static void rcdevice5KeySimulationProcess(timeUs_t currentTimeUs
)
220 UNUSED(currentTimeUs
);
228 if (camDevice
->serialPort
== 0 || ARMING_FLAG(ARMED
)) {
232 if (isButtonPressed
) {
233 if (IS_MID(YAW
) && IS_MID(PITCH
) && IS_MID(ROLL
)) {
234 rcdeviceSend5KeyOSDCableSimualtionEvent(RCDEVICE_CAM_KEY_RELEASE
);
235 waitingDeviceResponse
= true;
238 if (waitingDeviceResponse
) {
242 rcdeviceCamSimulationKeyEvent_e key
= RCDEVICE_CAM_KEY_NONE
;
244 if (IS_MID(THROTTLE
) && IS_MID(ROLL
) && IS_MID(PITCH
) && IS_LO(YAW
)) { // Disconnect Lo YAW
245 if (rcdeviceInMenu
) {
246 key
= RCDEVICE_CAM_KEY_CONNECTION_CLOSE
;
249 if (rcdeviceInMenu
) {
250 if (IS_LO(ROLL
)) { // Left LO ROLL
251 key
= RCDEVICE_CAM_KEY_LEFT
;
252 } else if (IS_HI(PITCH
)) { // Up HI PITCH
253 key
= RCDEVICE_CAM_KEY_UP
;
254 } else if (IS_HI(ROLL
)) { // Right HI ROLL
255 key
= RCDEVICE_CAM_KEY_RIGHT
;
256 } else if (IS_LO(PITCH
)) { // Down LO PITCH
257 key
= RCDEVICE_CAM_KEY_DOWN
;
258 } else if (IS_MID(THROTTLE
) && IS_MID(ROLL
) && IS_MID(PITCH
) && IS_HI(YAW
)) { // Enter HI YAW
259 key
= RCDEVICE_CAM_KEY_ENTER
;
262 if (IS_MID(THROTTLE
) && IS_MID(ROLL
) && IS_MID(PITCH
) && IS_HI(YAW
)) { // Enter HI YAW
263 key
= RCDEVICE_CAM_KEY_CONNECTION_OPEN
;
268 if (key
!= RCDEVICE_CAM_KEY_NONE
) {
269 rcdeviceSend5KeyOSDCableSimualtionEvent(key
);
270 isButtonPressed
= true;
271 waitingDeviceResponse
= true;
276 void rcdeviceUpdate(timeUs_t currentTimeUs
)
278 rcdeviceReceive(currentTimeUs
);
280 rcdeviceCameraControlProcess();
282 rcdevice5KeySimulationProcess(currentTimeUs
);
285 void rcdeviceInit(void)
288 runcamDeviceInit(camDevice
);
290 for (boxId_e i
= BOXCAMERA1
; i
<= BOXCAMERA3
; i
++) {
291 uint8_t switchIndex
= i
- BOXCAMERA1
;
292 switchStates
[switchIndex
].isActivated
= true;