VTX FPV video transmitter control with Arduino

Connection Partom VTX to Arduino

Written by Mariete

Aug 15, 2020

Updated: September 14, 2020 @ 22:13

How to control a FPV VTX with Arduino

Some time ago it got into my head control a 1.2 Ghz video transmitter with an Arduino.

Specifically, what I wanted to achieve was to make a antenna analyzer, also based on Arduino (which is made and works very well, I have to describe in the Blog how I did it and publish the code) that it was able to do a frequency sweep, for example from 1.1 Ghz to 1.4 Ghz, with steps every few Mhz, measuring the SWR in each of these steps to be able to capture all the data in Excel and make a antenna resonance graph in that frequency range.

I was looking a lot on the internet and I found very little information, so I decided to start almost from scratch and do all the analysis of the protocol that the VTX microprocessor uses to communicate with its IC PLL (an MB15E07L, manufactured by Fujitsu, which is the integrated circuit that creates the emission frequency), to know how to control it, and write the Arduino code.

Fortunately, the user Changosurf had already described the PLL protocol, although it did not work for me as it was and I had to do more research (the 18-bit latch data did not work with my VTX, nor with any of the ones I tried ).

I also found a code that I use as a base but, sorry, since I did this project several years ago, I cannot find the original author to give it credit.

I did the whole process using a 800mw VTX Partom, one of the most used in model aircraft, but the good thing is that almost all video transmitters that have this physical aspect, such as the VTX FOX (and many others), use this same PLL and protocol. In fact I have tried with other transmitters of different powers and other physical aspects and all have worked correctly.

How useful is control with Arduino?

The profits are many, and only the imagination is the limit.

In addition to the example mentioned above that allows us to vary the emission frequency automatically to be able to analyze antennas, we could use it to:

 

  • Change video channel of our plane or drone remotely by radio control. We have interference on the frequency we are using and our model aircraft is far away? Well, we change the frequency remotely (we can even save our device or avoid safe loss).
  • Use non-standard frequencies: Transmitters usually have between one and eight channels that we can select, this means that everyone is on one of those channels, if we can choose a frequency that does not coincide with that of any of those channels we can use that frequency for ourselves, exclusively, without interference.

Architecture of an FPV video transmitter

The architecture of a VTX of those used in FPV is quite simple.

It basically consists of four basic blocks:

  • Control circuit: the user selects an emission channel by means of switches or buttons and a microcontroller checks which frequency that channel corresponds to and encodes it in such a way that the PLL can understand it.
  • Frequency generator circuit or PLL (Phase-locked Loop): This integrated circuit receives data from the microcontroller that indicates the frequency to be generated.
  • Radio Frequency (RF) Circuit: This block receives the frequency generated by the PLL (which does not have to be the real emission frequency) and using that frequency as a base it will generate the final frequency, the subcarriers, and modulate it with the audio and the video.
  • Power amplifier: All of the above is done with extremely low powers, it is this final circuit that takes that signal and multiplies it by thousands or millions of times and sends that high-power signal to the antenna for broadcast.

What we are going to do here is replace the «Control Circuit» with our own to be able to control the PLL as we want.

PLL protocol analysis

The first thing I did was, before making any modifications to the transmitter hardware, it was analyze data on the PLL communication lines with the help of a RIGOL DS1054Z oscilloscope 

What I did was to change channels, manually, and with the oscilloscope, connected to the VTX with a few cables, capture the data that the PIC microcontroller sent to the VTX.

Below you can see the screenshots I made when switching to the different channels:

Finally I helped myself to capture channel 11 to check if it was in 18-bit or 19-bit mode:

In this last image you can see in detail the analysis of the protocol, since I documented it on the capture with numbered bits to make it easier to visualize:

 

Arduino sketch code

 Here you have the complete code to control the VTX with Arduino.

I recommend, if you are going to use it, that you take it from the eMariete page on Github, where you can find the latest version and other interesting information.

To flash the Arduino you can use the normal Arduino IDE. Keep in mind that this code was developed with a 2017 version of the Arduino IDE, so with the latest versions there could be incompatibilities.

The code is not a marvel of programming, it is something "quick and dirty" to check that it worked and I uploaded it to Github "as it is«. I did the tests on a 3.3V 8Mhz Arduino Pro Mini and it worked without problems.

Complete source code for the Arduino

/ * * This sketch controls a Partom or simmilar VTX transmit frequency * Copyright (c) 2017 Mario Elkati (Mariete) * [email protected] * /

/ * ----- (Declare Constants) ----- * /
static const int FREQ [] = {1010, 1040, 1060, 1080, 1100, 1120, 1140, 1160, 1180, 1200, 1240, 1258, 1280, 1320, 1360}; static unsigned long COUNTER18BIT_OLD [] = {
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001, /* 12 */
  0b010000010010110001,
  0b010000010010110001, /* 14 */
  0b010000010010110001}; static unsigned long COUNTER18BIT [] = {// Original PLL data for 1280Mhz
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001, /* 12 */
  0b010000001100100001,
  0b010000001100100001, /* 14 */
  0b010000001100100001}; static unsigned long COUNTER19BIT [] = {
  0b0110001010100001000,
  0b0110010110001000000,
  0b0110011110000010000,
  0b0110100101101100000,
  0b0110101101100110000,
  0b0110110101100000000,
  0b0110111101001010000,
  0b0111000101000100000,
  0b0111001100101110000,
  0b0111010100101000000,
  0b0111100100001100000,
  0b0111101011001101000, /* 12 */
  0b0111110100000001010,
  0b1000000011100100000, /* 14 */
  0b1000010011001000000};
#define VTXDataPin 7
#define VTXClockPin 8
#define VTXLatchEnablePin 9
#define VTXChannelChangePin 3

/ * ----- (Declare Variables) ----- * /
byte VTXChannelOld = 0; byte VTXChannel = 13; void setup () {Serial.begin (9600); pinMode (LED_BUILTIN, OUTPUT); pinMode (VTXDataPin, OUTPUT); pinMode (VTXClockPin, OUTPUT); pinMode (VTXLatchEnablePin, OUTPUT); pinMode (VTXChannelChangePin, INPUT_PULLUP); digitalWrite (VTXChannelChangePin, HIGH); digitalWrite (VTXDataPin, LOW); digitalWrite (VTXClockPin, LOW); digitalWrite (VTXLatchEnablePin, LOW); } void loop () {
  if (! digitalRead (VTXChannelChangePin)) {
    while (! digitalRead (VTXChannelChangePin)) {//} VTXChannel ++; Serial.println ("Button pressed ...");
  }

  if (VTXChannel! = VTXChannelOld) {Serial.println ("Changing channel ..."); ChangeChannel (); } //  delay(9500); // digitalWrite (LED_BUILTIN, HIGH); //  delay(500); // digitalWrite (LED_BUILTIN, LOW); // VTXChannel = 13; // ChangeChannel (VTXChannel); //  delay(9500); // digitalWrite (LED_BUILTIN, HIGH); //  delay(500); // digitalWrite (LED_BUILTIN, LOW); // VTXChannel = 11; // ChangeChannel (VTXChannel); } void bitBang (unsigned long pattern, byte numBits) // This function is what bitbangs the data {// digitalWriteFast (VTXDataPin, LOW); // digitalWriteFast (VTXClockPin, LOW); //  delay(1);
  for(int i = numBits-1; i> -1; i--) {digitalWriteFast (VTXDataPin, bitRead (pattern, i)); delayMicroseconds (300); digitalWriteFast (VTXClockPin, HIGH); delayMicroseconds (350); digitalWriteFast (VTXClockPin, LOW); Serial.print(bitRead (pattern, i)); delayMicroseconds (50); } digitalWriteFast (VTXLatchEnablePin, HIGH); delayMicroseconds (500); digitalWriteFast (VTXLatchEnablePin, LOW); Serial.println (""); digitalWrite (VTXDataPin, LOW); } void ChangeChannel () {Serial.print("VTXChannel:"); Serial.println (VTXChannel); Serial.print("VTXChannelOld:"); Serial.println (VTXChannelOld);
  if (VTXChannel < 1) {VTXChannel = 15;
  }
  if (VTXChannel> 15) {VTXChannel = 1; } switch (VTXChannel) {case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: Serial.print("Change to channel:"); Serial.println (VTXChannel); bitBang (COUNTER18BIT [VTXChannel-1], 18); bitBang (COUNTER19BIT [VTXChannel-1], 19); VTXChannelOld = VTXChannel;
      break; }} void digitalWriteFast (uint8_t pin, uint8_t x) {
  if (pin / 8) {// pin> = 8
    PORTB ^ = (-x ^ PORTB) & (1 << (pin % 8));
  }
  else {PORTD ^ = (-x ^ PORTD) & (1 << (pin % 8));
  }
}
=======
/ * * This sketch controls a Partom or simmilar VTX transmit frequency * Copyright (c) 2017 Mario Elkati (Mariete) * [email protected] * /

/ * ----- (Declare Constants) ----- * /
static const int FREQ [] = {1010, 1040, 1060, 1080, 1100, 1120, 1140, 1160, 1180, 1200, 1240, 1258, 1280, 1320, 1360}; static unsigned long COUNTER18BIT_OLD [] = {
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001,
  0b010000010010110001, /* 12 */
  0b010000010010110001,
  0b010000010010110001, /* 14 */
  0b010000010010110001}; static unsigned long COUNTER18BIT [] = {// Original PLL data for 1280Mhz
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001,
  0b010000001100100001, /* 12 */
  0b010000001100100001,
  0b010000001100100001, /* 14 */
  0b010000001100100001}; static unsigned long COUNTER19BIT [] = {
  0b0110001010100001000,
  0b0110010110001000000,
  0b0110011110000010000,
  0b0110100101101100000,
  0b0110101101100110000,
  0b0110110101100000000,
  0b0110111101001010000,
  0b0111000101000100000,
  0b0111001100101110000,
  0b0111010100101000000,
  0b0111100100001100000,
  0b0111101011001101000, /* 12 */
  0b0111110100000001010,
  0b1000000011100100000, /* 14 */
  0b1000010011001000000};
#define VTXDataPin 7
#define VTXClockPin 8
#define VTXLatchEnablePin 9
#define VTXChannelChangePin 3

/ * ----- (Declare Variables) ----- * /
byte VTXChannelOld = 0; byte VTXChannel = 13; void setup () {Serial.begin (9600); pinMode (LED_BUILTIN, OUTPUT); pinMode (VTXDataPin, OUTPUT); pinMode (VTXClockPin, OUTPUT); pinMode (VTXLatchEnablePin, OUTPUT); pinMode (VTXChannelChangePin, INPUT_PULLUP); digitalWrite (VTXChannelChangePin, HIGH); digitalWrite (VTXDataPin, LOW); digitalWrite (VTXClockPin, LOW); digitalWrite (VTXLatchEnablePin, LOW); } void loop () {
  if (! digitalRead (VTXChannelChangePin)) {
    while (! digitalRead (VTXChannelChangePin)) {//} VTXChannel ++; Serial.println ("Button pressed ...");
  }

  if (VTXChannel! = VTXChannelOld) {Serial.println ("Changing channel ..."); ChangeChannel (); } //  delay(9500); // digitalWrite (LED_BUILTIN, HIGH); //  delay(500); // digitalWrite (LED_BUILTIN, LOW); // VTXChannel = 13; // ChangeChannel (VTXChannel); //  delay(9500); // digitalWrite (LED_BUILTIN, HIGH); //  delay(500); // digitalWrite (LED_BUILTIN, LOW); // VTXChannel = 11; // ChangeChannel (VTXChannel); } void bitBang (unsigned long pattern, byte numBits) // This function is what bitbangs the data {// digitalWriteFast (VTXDataPin, LOW); // digitalWriteFast (VTXClockPin, LOW); //  delay(1);
  for(int i = numBits-1; i> -1; i--) {digitalWriteFast (VTXDataPin, bitRead (pattern, i)); delayMicroseconds (300); digitalWriteFast (VTXClockPin, HIGH); delayMicroseconds (350); digitalWriteFast (VTXClockPin, LOW); Serial.print(bitRead (pattern, i)); delayMicroseconds (50); } digitalWriteFast (VTXLatchEnablePin, HIGH); delayMicroseconds (500); digitalWriteFast (VTXLatchEnablePin, LOW); Serial.println (""); digitalWrite (VTXDataPin, LOW); } void ChangeChannel () {Serial.print("VTXChannel:"); Serial.println (VTXChannel); Serial.print("VTXChannelOld:"); Serial.println (VTXChannelOld);
  if (VTXChannel < 1) {VTXChannel = 15;
  }
  if (VTXChannel> 15) {VTXChannel = 1; } switch (VTXChannel) {case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: Serial.print("Change to channel:"); Serial.println (VTXChannel); bitBang (COUNTER18BIT [VTXChannel-1], 18); bitBang (COUNTER19BIT [VTXChannel-1], 19); VTXChannelOld = VTXChannel;
      break; }} void digitalWriteFast (uint8_t pin, uint8_t x) {
  if (pin / 8) {// pin> = 8
    PORTB ^ = (-x ^ PORTB) & (1 << (pin % 8));
  }
  else {PORTD ^ = (-x ^ PORTD) & (1 << (pin % 8));
  }
}

Modifying the VTX and connecting to the Arduino

The modification consists of replace the transmitter PIC microcontroller with our Arduino, programmed with the code from the previous point.

To do this we will have to carefully desolder the PIC (integrated 14-pin) from the board and solder three cables (for DATA, CLOCK and LATCH ENABLE lines) that will go to the Arduino pins 7, 8 and 9. You will also have to connect the ground between them (use a common negative cable, for example).

I think that in the next photo you can see quite well. 

 

SWR tester antenna analyzer with Arduino

I have yet to document well and publish this project. If you are interested in knowing it subscribe to the newsletter of eMariete so you don't miss it and find out as soon as possible.

0 Comments

It may also interest you ...

VULNERABILITY IN METEOTEMPLATE

Vulnerability in Meteotemplate and how it affects SEO

  Vulnerability in Meteotemplate What is Meteotemplate? Meteotemplate is a software package, or template, that hundreds of weather stations ...
Read more
Connection Partom VTX to Arduino

VTX FPV video transmitter control with Arduino

Do you want to control a VTX video transmitter with an Arduino? Here I explain how to do it ...
Read more
consejos para instalar domotica

10 Tips to install home automation at home

Do you want to install home automation at home but don't know where to start? Here are 10 tips to install home automation in ...
Read more
Homemade CNC with Arduino Root 3

Homemade CNC with Arduino II - Mechanics

Mechanical assembly of the CNC milling machine with Arduino. An easy and cheap machine to build and with many possibilities. It allows ...
Read more

Pin It on Pinterest

Share This