Head Tracking w FlySky GT3B and Telemetry Dashboard for Gmade GOM 1/10 Rock Crawler

The project is divided into 3 parts:

  1. Add head tracking (Channel 4-6) using Teensy 3.2 and toggle switching (Channel 7 & 8) functions in Flysky GT3B pistol transmitter 
  2. Add front and back FPV camera into GOM
  3. Add telemetry dashboard function using ST7735 and Arduino Nano






PART 1: Add head tracking function(Channel 4-6) using Teensy 3.2 and toggle switching (Channel 7 & 8) in Flysky GT3B pistol transmitter.

(Thank you for sharing by Dylan Fairbanks for instruction of adding head tracking function and additional channels using Teensy 3.2 and Flysky GT3B)

Pre-requisites: 
  • Basic programming skill
  • Basic soldering skill
  • Understanding of PPM and PWM 

Components
  • FlySky GT3B Transmitter and FS-R9B 8ch Receiver

  • Teensy 3.2

 

  • SkyZone 03O FPV Goggles (with Head Tracking Function)

  • JR (Head Tracking) Cable(come with 03O FPV Goggles)

  • 3.5mm Stereo Socket

  • 2-ways Toggle Switch (3 Pins)
  • 3-ways Toggle Switch (3 Pins)

  • Axis Pan&Tilt Camera Mount

  • 2 x Micro Servo 

Connection Diagram


Steps
  1. First use of the Teensy 3.2 https://www.pjrc.com/teensy/first_use.html
  2. Please read through “Basic Teensyduino Usage” if this is your first time to use Teensy https://www.pjrc.com/teensy/td_usage.html
  3. Please cut USB Power at back of Teensy 3.2. https://www.pjrc.com/teensy/external_power.html
  4. Unscrew and open the top cover of GT3B
  5. Solder the wire (I am not a professional at soldering) 


  6. Need cut original PPM Trace at the back of GT3 board 
  7. Make the holes and install the 2 x toggle switch and 3.5MM audio socket



Programming

#include "PulsePosition.h"
#include <Servo.h> 
 
Servo myservo;
PulsePositionInput RadioIn(FALLING);
PulsePositionInput TrackerIn(FALLING);
PulsePositionOutput MixOut(FALLING);

const uint8_t  RADIO_INPUT_PIN   = 22; //input the transmitter signal
const uint8_t  TRACKER_INPUT_PIN = 21; //input the head tracker signal
const uint8_t  MIX_OUT_PIN       = 20; //output the mixed signal

void setup() {
  Serial.begin(9600);
  RadioIn.begin(RADIO_INPUT_PIN);
  TrackerIn.begin(TRACKER_INPUT_PIN);
  MixOut.begin(MIX_OUT_PIN);
  myservo.attach(7);  
  pinMode(5, INPUT_PULLUP); //for channel 8 two ways toggle switch - light switch 
  pinMode(6, INPUT_PULLUP); //for channel 7 three was toggle swich - high-lo-dig gear
  pinMode(7, INPUT_PULLUP); //for channel 7 three was toggle swich - high-lo-dig gear
}

void loop() {
  int i, num;
  int j = 0;
  
//read channel 1-3 signal from transmitter FlySky GT3B
num = RadioIn.available(); 
if (num > 0) {
  for (i=1; i<=3; i++) { 
   float val = RadioIn.read(i);
    MixOut.write(i, val);
  }
}
  
//read channel 5-7 signal from headtracker and map to 4,5,6. For setup of headtracking signal channel, please read the headtracker manual. 
//For example, Skyzone 03O google: https://files.banggood.com/2016/12/SKY03%20Manual-V1.0(1).pdf
//Skyzone 03O google manual : The PPM channels can be set in the menu: CH5-CH6, CH5-CH7, CH5-CH8, CH6-CH7, CH6-CH8 and CH7-CH8. 
num = TrackerIn.available();    
if (num > 0) {
   for (i=1; i <= num; i++) {
    float val = TrackerIn.read(i);
    switch (i) {
       case 5:
          j = 4;
         break;
       case 6:
          j = 5;
          break;
        case 7:
         j = 6;
          break;
        case 8:
       j = 0;
        break;
      default:
    break;
   }
   if (j != 0) {
        MixOut.write(j, val);
   }
  }
 }


if (digitalRead(7)==LOW) {
   MixOut.write(7, 900);              // tell Ch7 servo to go to Dig position (servo turns to left)
  } else 
   if (digitalRead(6)==LOW) {
   MixOut.write(7, 1950);             // tell Ch7 servo to go to High position (servo turns to right)
  } else 
   MixOut.write(7, 1500);             // tell Ch7 servo to go to Low position (servo no turns)

 if (digitalRead(5)==LOW) {
   MixOut.write(8, 1950);             // tell Ch8 servo(LED light control) to be off 
  } else 
   MixOut.write(8, 1500);     	      // tell Ch8 servo(LED light control) to be on
}

Ch4-6 - Head Tracking testing




Ch7 - 3-ways Hi-Lo-Dig Gear(Servo) testing







Ch8 - 2-ways Toggle Switch Testing



Ch 8 - Light Switch Testing (please refer to "Convert to a old servo to a light switch")





PART 2: Add front and back FPV camera into GOM


Components

  • Rush Tank FPV Transmitter
  • 2 x FPV camera
  • FPV camera channel switch

Connection Diagram



Remark:
Make sure that the input voltage of CAM and FPV transmitter can be supported by battery voltage



PART 3: Add telemetry dashboard function using ST7735 and Arduino Nano

The Telemetry Dashboard

Please refer to "Display a customized image in ST7735" for display customized image" in this blog

Pre-requisites: 

Components

  • ST7735 1.8" TFT LCD
  • Arduino Nano


  • Connection Diagram



    Programming
    #include <SPI.h>
    #include <Adafruit_GFX.h>
    #include <Adafruit_ST7735.h> // include Hardware library

    #define TFT_CS   10
    #define TFT_RST  9  
    #define TFT_DC   8
    #define TFT_SCLK 13   
    #define TFT_MOSI 11   

    Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);


    #define BACKCOLOR 0x18E3
    #define BARCOLOR 0x0620
    #define SCALECOLOR 0xFFFF
    #define BLACK   0x0000
    #define BLUE    0xF800
    #define PURPLE  0xF81F
    #define CYAN    0xFFE0  
    #define WHITE   0xFFFF
    #define GRAY    0x8410
    #define RED     0xF800
    #define ORANGE  0xFA60
    #define YELLOW  0xFFE0
    #define LIME    0x07FF
    #define GREEN   0x07E0
    #define AQUA    0x04FF
    #define MAGENTA 0xF81F
    #define PINK    0xF8FF

    const int analogIn = A0;
    int RawValue = 0;
    int LastPercent = 0;
    double throttle;
    int gear;
    int val=0;
    int preval=0;
    int FNB=0;
    int preFNB=0;
    int meter=0;
    int premeter=0;
    int gearval=0;
    int pregearval=0;

    // Bear Logo
    const unsigned char bearlogo [] PROGMEM = {
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
      0x00, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x00, 
      0x1f, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x7c, 0x07, 0xc0, 0x00, 0x00, 0x03, 0xf0, 0x01, 0xf0, 0x00, 
      0x00, 0x0f, 0xe0, 0x31, 0xf8, 0x00, 0x00, 0x1f, 0xe1, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0x0f, 
      0xfe, 0x00, 0x00, 0xfe, 0x00, 0x01, 0xff, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x3f, 0x80, 0x03, 0xf6, 
      0x00, 0x00, 0x1f, 0xc0, 0x07, 0xef, 0x00, 0x00, 0x1f, 0xe0, 0x0f, 0x9b, 0x00, 0x00, 0x1f, 0xe0, 
      0x0f, 0xb2, 0x00, 0x00, 0x3f, 0xe0, 0x0f, 0xe4, 0x00, 0x00, 0x3d, 0xf0, 0x1f, 0x64, 0x00, 0x00, 
      0x3d, 0xf0, 0x1f, 0x40, 0x00, 0x00, 0x1c, 0xf0, 0x1f, 0xc0, 0x00, 0xc0, 0x3e, 0xf0, 0x1f, 0x40, 
      0x00, 0xe0, 0x30, 0xf0, 0x1f, 0x60, 0x01, 0xe0, 0x61, 0xf8, 0x1f, 0x60, 0x01, 0xf0, 0xc1, 0xf8, 
      0x1f, 0x60, 0x00, 0x18, 0x01, 0xf0, 0x1d, 0xc0, 0x00, 0x18, 0x01, 0xf0, 0x1d, 0x80, 0x00, 0x10, 
      0x01, 0xf0, 0x1d, 0x80, 0x00, 0x00, 0x01, 0xf0, 0x0c, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x0f, 0x10, 
      0x00, 0x00, 0x01, 0xf0, 0x1f, 0x20, 0x00, 0x00, 0x03, 0xe0, 0x1f, 0x60, 0x00, 0x00, 0x03, 0xe0, 
      0x1f, 0xe0, 0x00, 0x00, 0x03, 0xc0, 0x1f, 0xc0, 0x00, 0x00, 0x03, 0x80, 0x3f, 0xc0, 0x00, 0x00, 
      0x03, 0x80, 0x3f, 0xe0, 0x00, 0x00, 0x07, 0x00, 0x3f, 0xe0, 0x1f, 0x80, 0x06, 0x00, 0x1f, 0xf8, 
      0x1f, 0x00, 0x06, 0x00, 0x0f, 0xfc, 0x1e, 0x00, 0x0c, 0x00, 0x07, 0xfc, 0x06, 0x00, 0x0c, 0x00, 
      0x07, 0xf8, 0x02, 0x00, 0x08, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x18, 0x00, 0x0f, 0xf0, 0x00, 0x00, 
      0x10, 0x00, 0x0f, 0xf8, 0x02, 0x00, 0x30, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x30, 0x00, 0x07, 0xfc, 
      0x06, 0x00, 0x70, 0x00, 0x00, 0x7e, 0x0c, 0x00, 0x70, 0x00, 0x00, 0x7e, 0xf0, 0x01, 0xf0, 0x00, 
      0x00, 0x01, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
      0x00, 0x00, 0x00, 0x00
    };

    // gomlogo, 47x18px
    const unsigned char gomlogo [] PROGMEM = {
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf0, 0x3f, 0xe1, 0xe0, 0x78, 0x3f, 0xf8, 0x7f, 0xf1, 
      0xf0, 0xf8, 0x7f, 0xfc, 0xff, 0xf9, 0xf0, 0xf8, 0x7c, 0x7c, 0xf8, 0xf9, 0xf9, 0xf8, 0x78, 0x00, 
      0xf0, 0x79, 0xff, 0xf8, 0x78, 0xfc, 0xf0, 0x79, 0xff, 0xf8, 0x78, 0x7c, 0xf0, 0x79, 0xff, 0xf8, 
      0x78, 0x7c, 0xf0, 0x79, 0xef, 0x78, 0x7f, 0xfc, 0xff, 0xf9, 0xe6, 0x78, 0x3f, 0xfc, 0x7f, 0xf1, 
      0xe0, 0x78, 0x1f, 0xec, 0x3f, 0xe1, 0xc0, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xce, 
      0xa1, 0xd2, 0xe7, 0x48, 0x2f, 0x5a, 0xe3, 0xfa, 0xc4, 0x38, 0x3f, 0x58, 0xe3, 0xda, 0xf7, 0xb0, 
      0x2f, 0xde, 0xb3, 0xfe, 0xf7, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };


    void setup() {
     Serial.begin(9600);
     pinMode(6, INPUT); //get throttle signal from ch2
     pinMode(4, INPUT); //get hi-lo-dig signal from ch7

     tft.initR(INITR_BLACKTAB);
     tft.fillScreen(BACKCOLOR); // back colour in background of ST7735
     tft.setRotation(1); //set as landscape orientation

     //display header in dashboard
     tft.setTextColor(WHITE);
     tft.setCursor(10,11);
     tft.setTextSize(1);
     tft.println("GOM TELEMETRY DASHBOARD");

     //draw marker of acceleration bar
     tft.drawLine(5,40,150,40,GRAY);
     tft.setTextColor(WHITE);
     tft.setCursor(5,45);
     tft.println("1");
     tft.setCursor(25,45);
     tft.println("2");
     tft.setCursor(45,45);
     tft.println("3");
     tft.setCursor(65,45);
     tft.println("4");
     tft.setCursor(85,45);
     tft.println("5");
     tft.setCursor(105,45);
     tft.println("6");
     tft.setCursor(125,45);
     tft.println("7");
     tft.setCursor(145,45);
     tft.println("8");

     //Print "0" as acceleration meter as default
     tft.setTextColor(WHITE);
     tft.setCursor(45,62);
     tft.setTextSize(4);
     tft.println("0");

     //Print "N" as default
     tft.setTextColor(WHITE);
     tft.setCursor(15,66);
     tft.setTextSize(3);
     tft.println("N");

     //display hi-lo-dig gear 
     tft.fillCircle(20,116,5,GRAY);
     tft.fillCircle(50,116,5,GREEN);
     tft.fillCircle(80,116,5,GRAY);
     tft.drawLine(25,116,45,116,WHITE);
     tft.drawLine(55,116,75,116,WHITE);
     tft.setTextColor(WHITE);
     tft.setCursor(16,103);
     tft.setTextSize(1);
     tft.println("HI");
     tft.setTextColor(WHITE);
     tft.setCursor(46,103);
     tft.setTextSize(1);
     tft.println("LO");
     tft.setTextColor(WHITE);
     tft.setCursor(73,103);
     tft.setTextSize(1);
     tft.println("DIG");

     tft.drawBitmap(110, 55, bearlogo, 47, 54, WHITE); //draw bear logo
     tft.drawBitmap(110, 107, gomlogo, 47, 18, WHITE); //draw gom logo
    }


    void loop(){  
     int newPercent;
     int i;
     int j=1;
     int k=1;
     int colour;
     int diff;
     int FNBdiff;

     //get signal from pin6 (Ch2)
     throttle = pulseIn(6, HIGH);
     if (throttle>=1514) //car moving backward
      FNB = -1;
      else if (throttle<1479) //car moving forward
      FNB = 1;
      else //car in neutral
      FNB = 0;
     if (throttle<1479){ //if moving forward, val is for acceleration bar 
      val = map(throttle, 1479, 989,0,7);
      meter = map(throttle, 1479, 989, 0, 99); //map the throttle signal to 0-99, meter is for acceleration meter
     }
     else { 
      val=0;
      meter=0;
     }

     diff= val - preval; //calculate the different value for acceleration bar 
     if (diff>0) //acceleration, draw the colour bar
     {
      j=preval*20;
      for (i=preval; i<=val ; i++){
       if (i>=5) 
        else colour = RED;
       if (i>=3)
        else colour = YELLOW;
       colour = GREEN;
       Tft.fillRect(j+4, 27, 20, 10, colour);
       j=j+20;
      }
     }
     else if (diff <0) //deceleration, draw "black" bar
     {
      j=preval*20;
      for (i=preval; i>=val ; i--){
      tft.fillRect(j+4, 27, 20, 10, BLACK);
      j=j-20;
      }
     }
     preval=val;

     //display "D" - Forward, "N" - Neutral, "R" - Backward
     tft.setTextColor(PURPLE);
     tft.setCursor(15,66);
     tft.setTextSize(3);
     if ((FNB-preFNB)!=0) {
       if(FNB>0){
          tft.setTextColor(GREEN);
          tft.fillRect(15, 66, 20, 23, BLACK);
          tft.println("D");
          }
          else
          if (FNB<0){
            tft.setTextColor(RED);
            tft.fillRect(15, 66, 20, 23, BLACK);
            tft.println("R");
          }
          else {
            tft.setTextColor(WHITE);
            tft.fillRect(15, 66, 20, 23, BLACK);
            tft.println("N");
          }
     }
        

     //display acceleration meter
     if ((FNB-preFNB)!=0){ //display "0" if deceleration
       tft.fillRect(45, 62, 60, 30, BLACK);
       tft.setTextColor(WHITE);
       tft.setCursor(45,62);
       tft.setTextSize(4);
       tft.println("0");
     } 
     else
     if (FNB>0){ //display "meter" if acceleration
       if((meter-premeter)>1){
         tft.fillRect(45, 62, 60, 30, BLACK);
         tft.setTextColor(WHITE);
         tft.setCursor(45,62);
         tft.setTextSize(4);
         tft.println(meter);
       }
     }
     premeter=meter;
     preFNB=FNB;  

     //display Hi-lo-Dig Gear
     gear = pulseIn(4, HIGH);
     if (gear<950){ //display Hi Gear
       gearval=1;
       if ((gearval-pregearval)!=0){
         tft.fillCircle(20,116,5,GREEN);
         tft.fillCircle(50,116,5,GRAY);
         tft.fillCircle(80,116,5,GRAY);
       }
     }
     else
     if (gear>1900) { //display Dig Gear
       gearval=-1;
       if ((gearval-pregearval)!=0){
         tft.fillCircle(20,116,5,GRAY);
         tft.fillCircle(50,116,5,GRAY);
         tft.fillCircle(80,116,5,GREEN);
       }
     }
     else{
      gearval=0;
      if ((gearval-pregearval)!=0){ //display Lo Gear
        tft.fillCircle(20,116,5,GRAY);
        tft.fillCircle(50,116,5,GREEN);
        tft.fillCircle(80,116,5,GRAY);
      }
     }
     pregearval=gearval;
    }

    Dashboard - Throttle testing


    Dashboard - Hi-Lo-Dig Gear testing





    Comments

    Popular posts from this blog

    Display a customized image in ST7735

    Convert an old servo to a light switch