Principle of operations
I got a friend's remote controller2 and here is what it gives when connected to a PC
It's an HID Joystick with X, Y, Z, Rx and Ry "analog sticks" and 7 active buttons.
Here is the law table to map the actions on the remote2 to the corresponding joystick events.
- All sticks output values between +-660
- Right stick
- Left/right: Joystick X axis
- Up/Down: Joystick Y axis
- Left stick
- Left/Right: Joystick Rx axis
- Up/Down: Joystick Z axis
- gimbal wheel: Joystick Ry
- buttons
- C1: Joystick button 1
- Start: Joystick button 2
- home: Joystick button 3
- photo: Joystick button 4
- gimbal 3 states:
- Up: joystick button 5 = 0 and button 6 = 0
- center: Joystick button 5 = 1 and button 6 = 0
- Down: Joystick button 5 = 0 and button 6 = 1
- Mode Fly:
- Normal: Joystick button 7 = 0 and button 8 = 1
- Sport: Joystick button 7 = 0 and button 8 = 0
- Manual: Joystick button 7 = 1 and button 8 = 0
This being known it is easy to reproduce a Bluetooth Low Energy Joystick!
Schematics
Once again the heart of the system is an ESP32 MCU. The radio receiver or the trainer port output of my radio being 5V devices, I needed a level shifter to accomodate with the 3.3V IO of the ESP32.
And that's it for the schematics: one single pin used as input for the PPM train and the rest over the air via BLE!
PCB
As you may imagine the PCB is also very simple. My receiver is simply put on the side of the ESP32
The board and the receiver are powered by a 5V powerbank and no other connection is needed!
Firmware
Most of the difficulty of this project was not in the hardware side but rather in the software one.
It was really simple to emulate a BLE gamepad, but much more difficult to find the right combination of channels to be accepted by the DJI Virtual Flight. So after a lot of trial and errors I succeeded (see above) !
The code is mostly using the excellent LemingDev BLEgamepad library.
I have only added an interrupt routine to decommutate the PPM train. Only a few lines of code:
void IRAM_ATTR ppmISR() {
// Remember the current micros() and calculate the time since the last pulseReceived()
unsigned long previousMicros = microsAtLastPulse;
microsAtLastPulse = micros();
unsigned long pulseDuration = microsAtLastPulse - previousMicros;
if (pulseDuration < MIN_TIME)
{
microsAtLastPulse = previousMicros; //cancel the pulse
}
else if (pulseDuration > BLANK_TIME)
{
currentChannel = 0; // Blank detected: restart from channel 1
digitalWrite (LED_PIN, !digitalRead(LED_PIN));
}
else
{
// Store times between pulses as channel values
if (currentChannel < NB_CHANNELS)
{
if ((pulseDuration > 900) && (pulseDuration < 2500))
{
rawValues[currentChannel] = pulseDuration;
}
}
currentChannel++ ;
}
}
The rawValues of the Tx are stored in microsecond reprensenting the duration of each pulse.
then the conversion into gamepad events is also straightforward:
//bleGamepad.press(BUTTON_1); //C1 button //bleGamepad.press(BUTTON_2); //Sart button //bleGamepad.press(BUTTON_3); //Pause/home button //bleGamepad.press(BUTTON_4); //photo button //bleGamepad.press(BUTTON_5); //gimbal Up //bleGamepad.press(BUTTON_6); //gimbal Down. And buttons 5 and 6 released = gimbal center //bleGamepad.pressHome(); //exit App releaseHome(); if ((rawValues[5] - 1500) > 250) //trainer button toggle { bleGamepad.press(BUTTON_7); //toggle Manual to S } else bleGamepad.release(BUTTON_7); //map(value, fromLow, fromHigh, toLow, toHigh) bleGamepad.setX(map(rawValues[3],1000, 2000, -660, 660)); //Right Stick horizontal #ifdef MODE_1 bleGamepad.setY(-map(rawValues[2],1000, 2000, -660, 660)); //Right Stick vertical bleGamepad.setZ(map(rawValues[1],1000, 2000, -660, 660)); //Left Stick vertical #else bleGamepad.setY(map(rawValues[2],1000, 2000, -660, 660)); bleGamepad.setZ(-map(rawValues[1],1000, 2000, -660, 660)); #endif bleGamepad.setRX(map(rawValues[0],1000, 2000, -660, 660)); //Left Stick horizontal bleGamepad.setRY(-map(rawValues[4],1000, 2000, -660, 660)); //gimbal on 3 states switch bleGamepad.sendReport();
A you can see I can use:
- the sticks to control the drone
- channel 6 as a momentary switch to toggle Manual to Sport mode
- and the gimbal motion on a three state switch of the radio on channel 5
Source code is on my Github page here : https://github.com/f2knpw/ESP32_DJI_VirtualFlight_RC_radio_interface
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.