Control transmisor de video VTX FPV con Arduino

Connection Partom VTX to Arduino

Escrito por Mariete

15 Ago, 2020

Actualizado: 14 septiembre, 2020 @ 22:13

Cómo controlar un VTX de FPV con Arduino

Hace algún tiempo se me metió en la cabeza controlar un video transmisor de 1.2 Ghz con un Arduino.

Concretamente, lo que quería conseguir era hacer un analizador de antenas, también basado en Arduino (que está hecho y funciona muy bien, tengo que pendiente describir en el Blog como lo hice y publicar el código) que fuera capaz de hacer un barrido de frecuencias, por ejemplo desde 1,1 Ghz hasta 1,4 Ghz, con escalones cada pocos Mhz, midiendo la SWR en cada uno de esos escalones para poder capturar todos los datos en Excel y hacer una gráfica de la resonancia de la antena en ese rango de frecuencias.

Estuve buscan mucho por internet y encontré muy poca información, de forma que decidí partir, casi, de cero y hacer todo el análisis del protocolo que utiliza el microprocesador del VTX para comunicarse con su IC PLL (un MB15E07L, fabricado por Fujitsu, que es el circuito integrado que crea la frecuencia de emisión), para saber como controlarlo, y escribir el código de Arduino.

Afortunadamente el usuario Changosurf ya había descrito el protocolo del PLL, aunque a mí no me funcionó tal y como estaba y tuve que hacer más investigación (los datos del registro latch de 18 bits no funcionaban con mi VTX, ni con ninguno de los que probé).

Encontré también un código que utilice como base pero, lo siento, como hace varios años que hice este proyecto, no encuentro el autor original para darle credito.

Todo el proceso lo hice utilizando un VTX Partom de 800mw, de los más utilizados en aeromodelismo, pero lo bueno es que casi todos los emisores de video que tienen ese aspecto físico, como los VTX FOX (y otros muchos), utilizan este mismo PLL y protocolo. De hecho he probado con otros transmisores de potencias distintas y otros aspectos físicos y todos han funcionado correctamente.

¿Qué utilidad tiene el control con Arduino?

Las utilidades son muchísimas, y solamente la imaginación es el límite.

Además del ejemplo comentado anteriormente que nos permite variar la frecuencia de emisión de forma automática para poder analizar antenas podríamos utilizarlo para:

 

  • Cambiar el canal de video de nuestro avión o dron remotamente por radiocontrol. ¿Tenemos interferencias en la frecuencia que estamos usando y nuestro aeromodelo está lejos? Pues cambiamos de frecuencia remotamente (podemos hasta salvar nuestro aparato o evitar una perdida segura).
  • Utilizar frecuencias no estándar: Los transmisores suelen tener entre uno y ocho canales que podemos seleccionar, esto quiere decir que todo el mundo está en uno de esos canales, si podemos elegir una frecuencia que no coincide con la de ninguno de esos canales podremos utilizar esa frecuencia para nosotros solos, en exclusiva, sin interferencias.

Arquitectura de un transmisor de video FPV

La arquitectura de un VTX de los utilizados en FPV es bastante sencilla.

Básicamente consta de cuatro bloque básicos:

  • Circuito de control: el usuario selecciona un canal de emisión mediante unos interruptores o botones y un microcontrolador comprueba a qué frecuencia corresponde ese canal y la codifica de forma que el PLL pueda entenderlo.
  • Circuito generador de frecuencia o PLL (Phase-locked Loop): Este circuito integrado recibe unos datos desde el microcontrolador que le indica la frecuencia que debe generar.
  • Circuito de Radio Frecuencia (RF): Este bloque recibe la frecuencia generada por el PLL (que no tiene por qué ser la frecuencia real de emisión) y utilizando esa frecuencia como base generará la frecuencia final, las subportadoras, y la modulará con el audio y el video.
  • Amplificador de potencia: Todo lo anterior se hace con unas potencias extremadamente bajas, es este circuito final el que coge esa señal y la multiplica por miles o millones de veces y manda esa señal, de alta potencia, a la antena para su emisión.

Lo que vamos a hacer aquí es sustituir el «Circuito de control» por el nuestro propio para poder controlar el PLL como queramos.

Análisis del protocolo del PLL

Lo primero que hice fue, antes de realizar ninguna modificación en el hardware del transmisor, fue analizar los datos en las líneas de comunicación del PLL con ayuda de un osciloscopio RIGOL DS1054Z 

Lo que hice fue ir cambiando de canales, manualmente, y con el osciloscopio, conectado al VTX con unos cablecillos,  capturar los datos que el microcontrolador PIC enviaba al VTX.

A continuación puedes ver las capturas que hice al cambiar a los diferentes canales:

Finalmente me ayudé de la captura del canal 11 para comprobar si estaba en modo de 18 bits o 19 bits:

En esta última imagen se puede ver con detalle el análisis del protocolo, ya que lo documenté sobre la captura con los bits numerados para que fuera más fácil de visualizar:

 

Código sketch de Arduino

 Aquí tienes el código completo para controlar el VTX con Arduino.

Te recomiendo, si lo vas a usar, que lo cojas de la página de eMariete en Github, donde podrás encontrar la última versión y otras informaciones interesantes.

Para flashear el Arduino puedes utilizar el Arduino IDE normal. Ten en cuenta que este código se desarrolló con una versión de Arduino IDE de 2017 por lo que con las últimas versiones podría haber incompatibilidades.

El código no es una maravilla de la programación, es algo «rápido y sucio» para comprobar que funcionaba y lo subí a Github «tal cual«. Las pruebas las realicé en un Arduino Pro Mini de 3.3 V a 8 Mhz y funcionaba sin problemas.

Código fuente completo para el 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[] = { // Data original PLL 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[] = { // Data original PLL 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));
  }
}

Modificación del VTX y conexión al Arduino

La modificación consiste en sustituir el microcontrolador PIC del transmisor por nuestro Arduino, programado con el código del punto anterior.

Para ello tendremos que desoldar, con cuidado el PIC (integrado de 14 patas) de la placa y soldar tres cables (para las líneas DATA, CLOCK y LATCH ENABLE) que irán a los a los pines Arduino 7, 8 y 9. Tendrás que conectar también la tierra entre ellos (usar un cable de negativo común, por ejemplo).

Pienso que en la siguiente fotografía se puede ver bastante bien. 

 

Analizador de antenas comprobador de SWR con Arduino

Tengo pendiente documentar bien y publicar este proyecto. Si te interesa conocerlo suscríbete a la newsletter de eMariete para no perdértelo y enterarte cuanto antes.

0 comentarios

También Podría Interesarte…

VULNERABILIDAD EN METEOTEMPLATE

Vulnerabilidad en Meteotemplate y como afecta al SEO

  Vulnerabilidad en Meteotemplate ¿Qué es Meteotemplate? Meteotemplate es un paquete de software, o plantilla, que cientos de estaciones meteorológicas ...
Leer Más
Connection Partom VTX to Arduino

Control transmisor de video VTX FPV con Arduino

Quieres controlar un transmisor de video VTX con un Arduino? Aquí te explico como hacerlo ...
Leer Más
consejos para instalar domotica

10 Consejos para instalar domótica en casa

¿Quieres instalar domótica en casa pero no sabes por donde empezar? Aquí te dejo 10 consejos para instalar domótica en ...
Leer Más
CNC casera con Arduino Root 3

CNC Casera con Arduino II – Mecánica

Montaje mecánico de la fresadora CNC con Arduino. Una máquina fácil y barata de construir y con muchas posibilidades. Permite ...
Leer Más

Pin It en Pinterest

Compartir este