High Resolution Gyro Question

Hello everyone!

I came across a Gyroscope that operates at 1 megabaud with 8-N-1 byte format. The bytes are sent with LSB first and consist of 6 bytes total.

Bytes 3 and 4 are the theta angles and what I am prioritizing to decode.

Byte 1: Status of the gyro
Byte 2: Tag ID
Byte 3: LSB Theta (16 bit cumulative - wraps)
Byte 4: MSB Theta (16 bit cumulative - wraps)
Byte 5: Tag Data
Byte 6: Checksum

Theta is a 16 bit value is cumulative and unsigned and it will wrap if an overflow or underflow is detected.

I currently have a Nano with a LCD 16x2 screen that will display the angular velocity.
The gyro is sending serial TTL and hooked up the the RX of the Nano.
The Nano is sending a pulse/clock for the gyro to return the current angular position.

Looking for help on which direction to try to receive and select bytes 3 & 4 to read the current Theta.

The code I have setup will then calculate the current Delta Theta and convert it to ArcSec per Sec (which I intend to display on the screen).

I would really appreciate anyone that would be willing to help me decode and turn bytes 3 & 4 into a usable number so I can calculate the angular velocity.

Thank you!!

#include <LiquidCrystal.h>
LiquidCrystal lcd(10, 9, 8, 7, 6, 5);       // put your pin numbers here


// the setup routine runs once when you press reset:
void setup() {
  pinMode(11, OUTPUT);    // LCD CONTRAST PIN
  pinMode(2, OUTPUT);    // Clear To Send PIN
  pinMode(3, OUTPUT);    // Clock PIN
  digitalWrite(2, HIGH);  // CTS CONSTANT HIGH
  tone(3,32); //CLOCK FREQ in HZ on PIN 3
  analogWrite(11, 79); //LCD CONTRAST AMOUNT

  Serial.begin(1000000);
  
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
}


void loop() {

    //float angularVelocity;
    //currentValue = analogRead(sensorPin); //CHECK
    //angularVelocity =((previousValue - currentValue)*1.113065); // count * 1.113065   ARC SEC / SEC
    //Serial.print(angularVelocity, 6); //6 decimal place values
    //Serial.println(" ArcSec/s");  
    //Serial.println(" "); // new line
    //previousValue = currentValue;

  
  // when characters arrive over the serial port...
  if (Serial.available()) {
    // wait a bit for the entire message to arrive
    delay(100);
    // clear the screen
    lcd.clear();
    // read all the available characters
    while (Serial.available() > 0) {
      // display each character to the LCD
      lcd.write(Serial.read());
    }
  }
}

Remembering the slowest part of the process will be the LCD.

the payload seems to be 6 bytes (weird there is no start or end marker) to send over serial.
What's the frame rate ? (how often does the Gyroscope send the data ?)

if the data comes in at 1 megabaud, you should think at what happens during this

// wait a bit for the entire message to arrive
    delay(100);

At 1Mbaud you receive 1 million bits per second (Gyroscope constantly outputting data?). So during the 100ms delay you had, you possibly received 100,000 bits, roughly 10,000 bytes and you serial buffer is 64 bytes deep...

➜ don't second guess timing on asynchronous protocols, just read the data correctly when it arrives (I would suggest to study Serial Input Basics to understand how to approach this)

The payload is 6 bytes in total. its 8N1 so its has start and stop bits.


image

The Gyro is capable of sending angle positions at 5KHZ.

I am interested at first in requesting packets within the range of 1-100hz

My goal is to just read the angles at a very low rate as I will not be moving the gyroscope.

There's no start/end marker on the message because the Nano initiates the communication. So you pulse an output, then the gyro responds with 6 bytes. You can then choose how fast you want to receive data

Any link to the gyro spec?

here is the manual that I am working with.
GG1320-usermanual.pdf (357.4 KB)

So did you get this to work?

Serial Output Transmission
Upon receipt of a User Sample Request (pin 8), the gyro counts and gyro dither position angle are measured with a maximum time delay of 0.5 microsecond. The gyro counts are corrected for dither compensation gain, phase error, non-linearity, and scale factor. In addition, the gyro output is compensated for temperature and temperature rate-of-change.
The software does not allow any interrupt processing during the output. The software outputs the next gated serial data message when a User Sample Request occurs and the Clear-to-Send pin is high. No data is sent if the Clear-to-Send pin is low. The output processing automatically loops through a predefined set of output signals and repeats the sequence when the last defined output signal has been transmitted. The predefined set of output signals are made up of measured gyro signals, gyro status words, and internally computed variables. The serial output transmits a 6-byte (8-bit) serial stream per output frame. A single frame is sent for each interrupt. Each byte is transmitted with the least significant bit first. The 6-byte stream is defined as in Table 8 and Figure 5. For gyro responses requiring more than one response byte, the software sends the remaining bytes in the successive frames. After all bytes have been sent for the current gyro response, the software automatically proceeds to the next defined output signal.

A small piece if code where you poke the gyro and print in HEX what comes back would be a good first step.

I believe I did that with this code:
It was printing and scrolling through what looks like HEX.

// Example 6 - Receiving binary data - from Arudino Forum
#include <LiquidCrystal.h>
LiquidCrystal lcd(10, 9, 8, 7, 6, 5);       // put your pin numbers here

const byte numBytes = 6;
byte receivedBytes[numBytes];
byte numReceived = 0;

boolean newData = false;

void setup() {
    pinMode(11, OUTPUT);    // LCD CONTRAST PIN
    pinMode(2, OUTPUT);    // Clear To Send PIN
    pinMode(3, OUTPUT);    // Clock PIN
    digitalWrite(2, HIGH);  // CTS CONSTANT HIGH
    tone(3,32); //CLOCK FREQ in HZ
    analogWrite(11, 79); //LCD CONTRAST AMOUNT
    Serial.begin(1000000);
    
    // set up the LCD's number of columns and rows:
    lcd.begin(16, 2);
}

void loop() {
    recvBytesWithStartEndMarkers();
    showNewData();
}

void recvBytesWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    byte startMarker = 0x3C;
    byte endMarker = 0x3E;
    byte rb;
   

    while (Serial.available() > 0 && newData == false) {
        rb = Serial.read();

        if (recvInProgress == true) {
            if (rb != endMarker) {
                receivedBytes[ndx] = rb;
                ndx++;
                if (ndx >= numBytes) {
                    ndx = numBytes - 1;
                }
            }
            else {
                receivedBytes[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                numReceived = ndx;  // save the number for use when printing
                ndx = 0;
                newData = true;
            }
        }

        else if (rb == startMarker) {
            recvInProgress = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        lcd.clear();
        for (byte n = 0; n < numReceived; n++) {
            lcd.print(receivedBytes[n], HEX);
        }
        newData = false;
    }
}

Where does this come from?

This is where I have started to learn from:

I just dropped in the code I found on that post and am starting to play around with it and figured I create a post to help me along the way if anyone. ( really appreciate you all replying so far!)

I am quite fresh to attempting to understand serial communication so I must confess I am quite in the dark about all this and a bit overwhelmed but I am excited to have a project that will get me to start learning all this.

The point as discussed before is that your protocol does not have any start nor stop.

Seems you use it in continuous mode and not in a mode where you ping it to get data. If you were to miss one byte only for whatever reason then you’ll need some robust code to re-sync…

I would keep it easy at the beginning at try with the pulse approach (send a pulse, read the 6 bytes and do something with that, then start again) to have the upper hand on the communication

Hi, @kilgore
To add code please click this link;

This will put your code in a scrolling window, making it easier to read.
Like this;

// Example 6 - Receiving binary data - from Arudino Forum
#include <LiquidCrystal.h>
LiquidCrystal lcd(10, 9, 8, 7, 6, 5);       // put your pin numbers here

const byte numBytes = 6;
byte receivedBytes[numBytes];
byte numReceived = 0;

boolean newData = false;

void setup() {
    pinMode(11, OUTPUT);    // LCD CONTRAST PIN
    pinMode(2, OUTPUT);    // Clear To Send PIN
    pinMode(3, OUTPUT);    // Clock PIN
    digitalWrite(2, HIGH);  // CTS CONSTANT HIGH
    tone(3,32); //CLOCK FREQ in HZ
    analogWrite(11, 79); //LCD CONTRAST AMOUNT
    Serial.begin(1000000);
    
    // set up the LCD's number of columns and rows:
    lcd.begin(16, 2);
}

void loop() {
    recvBytesWithStartEndMarkers();
    showNewData();
}

void recvBytesWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    byte startMarker = 0x3C;
    byte endMarker = 0x3E;
    byte rb;
   

    while (Serial.available() > 0 && newData == false) {
        rb = Serial.read();

        if (recvInProgress == true) {
            if (rb != endMarker) {
                receivedBytes[ndx] = rb;
                ndx++;
                if (ndx >= numBytes) {
                    ndx = numBytes - 1;
                }
            }
            else {
                receivedBytes[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                numReceived = ndx;  // save the number for use when printing
                ndx = 0;
                newData = true;
            }
        }

        else if (rb == startMarker) {
            recvInProgress = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        lcd.clear();
        for (byte n = 0; n < numReceived; n++) {
            lcd.print(receivedBytes[n], HEX);
        }
        newData = false;
    }
}


Tom... :smiley: :+1: :coffee: :australia:

Hi,
To help read your code, can I suggest you give variable names to your pin.

// Example 6 - Receiving binary data - from Arudino Forum
#include <LiquidCrystal.h>
LiquidCrystal lcd(10, 9, 8, 7, 6, 5);       // put your pin numbers here

const byte numBytes = 6;
byte receivedBytes[numBytes];
byte numReceived = 0;

byte LCDConPin = 11;
byte CTSPin = 2;
byte ClockPin = 3;

boolean newData = false;

void setup() {
  pinMode(LCDConPin, OUTPUT);    // LCD CONTRAST PIN
  pinMode(CTSPin, OUTPUT);    // Clear To Send PIN
  pinMode(ClockPin, OUTPUT);    // Clock PIN
  digitalWrite(CTSPin, HIGH);  // CTS CONSTANT HIGH
  tone(ClockPin, 32); //CLOCK FREQ in HZ
  analogWrite(LCDConPin, 79); //LCD CONTRAST AMOUNT
  Serial.begin(1000000);

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
}

Tom... :smiley: :+1: :coffee: :australia:

#include <LiquidCrystal.h>
LiquidCrystal lcd(10, 9, 8, 7, 6, 5);       // put your pin numbers here

byte index = 0;
int DMM_ARRAY[6];   // Where to store the Bytes read
byte c;
char SETUP = 0x65;
int n = 0;
float previousValue = 0;
float currentValue = 0;
uint32_t t = 0;   // Time record was received '2', '3', '4', '5', '6', '7', '8', '9', ' ', 'L'};
void printRecord(Print* pr, char sep = ',') {
  pr->print(t);
  //pr->print(sep);
  //pr->print(DMM_ARRAY[0]); // Print btye 0
  //pr->print(sep);
  //pr->print(DMM_ARRAY[1]); // Print btye 1
  pr->print(sep);
  pr->print(DMM_ARRAY[2]); // Print btye 2
  pr->print(sep);
  pr->print(DMM_ARRAY[3]); // Print btye 3
  //pr->print(sep);
  //pr->print(DMM_ARRAY[4]); // Print btye 4
  //pr->print(sep);
  //pr->print(DMM_ARRAY[5]); // Print btye 5

}
void setup() {
  pinMode(11, OUTPUT);    // LCD CONTRAST PIN
  pinMode(2, OUTPUT);    // Clear To Send PIN
  pinMode(3, OUTPUT);    // Clock PIN
  digitalWrite(2, HIGH);  // CTS CONSTANT HIGH
  //tone(3,1); //CLOCK FREQ in HZ // minimum 31hz?

  analogWrite(11, 50); //LCD CONTRAST AMOUNT
  Serial.begin(1000000);
  lcd.begin(16, 2);
  lcd.print("GG1320AN21 TEST");
  lcd.clear();
}

void loop() {
  digitalWrite(3, HIGH);
  delayMicroseconds(200);
  digitalWrite(3, LOW);
  Get_data(); //Read the Serial bytes coming in

  //lcd.setCursor(0, 0);
  //lcd.print( DMM_ARRAY[2]);// Print btye 3 (LSB)
  //lcd.print(" ");
  
  //lcd.setCursor(0, 1);
  //lcd.print( DMM_ARRAY[3]);// Print btye 4 (MSB)
  //lcd.print(" ");
  //delay(1000);
  //lcd.clear();


  float angularVelocity;
  currentValue = DMM_ARRAY[2];

  angularVelocity =((currentValue - previousValue)*1.113065); // count * 1.113065   ARC SEC / SEC
  
  lcd.setCursor(0, 0);
  lcd.print(angularVelocity, 3); //3 decimal place values
  //lcd.print(" ");
  //lcd.setCursor(0, 1);
  //lcd.println(" ArcSec/s    ");
  //lcd.print(" ");
  previousValue = currentValue;
  delay(1000);
  lcd.clear();

}


void  Get_data() {

if (Serial.available() >= 6) {
    for (int i = 0; i < 6; i++) {
      DMM_ARRAY[i] = Serial.read();
    }
  }

}

I have not adjusted the variables to be more readable yet, so sorry it will still be a bit tough to read.

I used another forum post to get something that I think it doing the trick for the moment.
I am currently reading what I believe is the 3rd Byte.

I need to figure out how to get a accurate 1 hz pulse to be sent to the gyro so I can accurately calculate the angular velocity while it is sitting still.

It currently is bouncing around the expected value so I think I am close. :slight_smile:

don't rely on delay(1000); to assume you have 1s in between two cycles of the loop.
use a dynamic time based approach: remember the last time you made a measure and when you calculate your velocity you can take into account the actual clock time ∆t = current time - previous time

  static unsigned long previousMicros=0;
  unsigned long currentMicros = micros();    // you could do this with millis() too, depends on the precision you want

  if (currentMicros - previousMicros >= 1000000ul) { // 1s in µs
    unsigned long deltaT = currentMicros  - previousMicros; // that will be give or take 1s
    // time to get a new read
   // ... your code here and use deltaT for elapsed time since last measure (of course convert into the right unit)
    previousMicros = currentMicros;
  }
...

Thank you for the tips and the code!
I have collaged it into the sketch I am working with.

The 8 bit LSM is counting up (or down depending on how it is sitting on the bench) and I need to starting counting it as it wraps around. Would this be best done with Timer1 counting it?

#include <LiquidCrystal.h>
LiquidCrystal lcd(10, 9, 8, 7, 6, 5);       // put your pin numbers here

byte index = 0;
int DMM_ARRAY[6];   // Where to store the Bytes read
byte c;
//char SETUP = 0x65;
int n = 0;
float previousValue = 0;
float currentValue = 0;
uint32_t t = 0;   // Time record was received '2', '3', '4', '5', '6', '7', '8', '9', ' ', 'L'};


void setup() {
  pinMode(11, OUTPUT);    // LCD CONTRAST PIN
  pinMode(2, OUTPUT);     // Clear To Send PIN
  pinMode(3, OUTPUT);     // Request Sample PIN
  analogWrite(11, 50);    //LCD CONTRAST AMOUNT
  Serial.begin(1000000);
  lcd.begin(16, 2);
  lcd.print("GG1320AN21 TEST");
  lcd.clear();
}

void loop() {

float deltaTheta;
static unsigned long previousMicros=0;
unsigned long currentMicros = micros();    // you could do this with millis() too, depends on the precision you want

  if (currentMicros - previousMicros >= 1000000ul) { // 1s in µs
    unsigned long deltaT = currentMicros - previousMicros; // that will be give or take 1s
    digitalWrite(2, HIGH);  // CTS Open
    digitalWrite(3, HIGH);  // RS
    delayMicroseconds( 50 );
    digitalWrite(3, LOW);  //RS
    delayMicroseconds( 200 );
    digitalWrite(2, LOW);  // CTS Close
    
  // time to get a new read
  
  Get_data(); //Read the Serial bytes coming in
  currentValue = DMM_ARRAY[2];

   // ... your code here and use deltaT for elapsed time since last measure (of course convert into the right unit)

  deltaTheta =((previousValue - currentValue)* 1.113065); // count * 1.113065   ARC SEC / SEC
lcd.clear();
lcd.setCursor(0, 0);

lcd.print(deltaTheta / (deltaT / 1000000)); //3 decimal place values
lcd.print(" ");
  
lcd.setCursor(0, 1);
lcd.print( DMM_ARRAY[2]);// Print btye 3 (LSB)
lcd.print(" ");

previousMicros = currentMicros;
previousValue = currentValue;
  
  }

}


void  Get_data() {

if (Serial.available() >= 6) {
    for (int i = 0; i < 6; i++) {
      DMM_ARRAY[i] = Serial.read();
    }
  }
}

your Get_data() function should return a boolean, true upon success for example. In the current situation, if you call Get_data() and there are not yet 6 bytes in the incoming Serial buffer, you don't read anything, yet you proceed with the maths in the loop.

something like this would do

bool Get_data() {
  bool success = (Serial.available() >= 6);
  if (success) Serial.readBytes(DMM_ARRAY, 6);
  return success;
}

and then in the main code you do:

  if (Get_data()) { //Read the Serial bytes coming in
    currentValue = DMM_ARRAY[2];
     // ... your code here and use deltaT for elapsed time since last measure (of course convert into the right unit)
    ...
  }

Thank you for the continued help J-M-L !! Really appreciate you taking the time to work with me on this.
here is my attempt at integrating what you have suggested.
Not sure if this was what was intended..

I also got a warning that the Serial.readBytes was expecting a Char or Byte for the buffer type so I changed it to a Byte rather then an Int.

#include <LiquidCrystal.h>
LiquidCrystal lcd(10, 9, 8, 7, 6, 5);       // put your pin numbers here

byte index = 0;
byte DMM_ARRAY[6];   // Where to store the Bytes read
byte c;
//char SETUP = 0x65;
int n = 0;
float previousValue = 0;
float currentValue = 0;
uint32_t t = 0;   // Time record was received '2', '3', '4', '5', '6', '7', '8', '9', ' ', 'L'};


void setup() {
  pinMode(11, OUTPUT);    // LCD CONTRAST PIN
  pinMode(2, OUTPUT);     // Clear To Send PIN
  pinMode(3, OUTPUT);     // Request Sample PIN
  analogWrite(11, 50);    //LCD CONTRAST AMOUNT
  Serial.begin(1000000);
  lcd.begin(16, 2);
  lcd.print("GG1320AN21 TEST");
  lcd.clear();
}


bool Get_data() {
  bool success = (Serial.available() >= 6);
  if (success) Serial.readBytes(DMM_ARRAY, 6);
  return success;
}

void loop() {

float deltaTheta;
static unsigned long previousMicros=0;
unsigned long currentMicros = micros();    // you could do this with millis() too, depends on the precision you want

  if (currentMicros - previousMicros >= 1000000ul) { // 1s in µs
    unsigned long deltaT = currentMicros - previousMicros; // that will be give or take 1s
    digitalWrite(2, HIGH);  // CTS Open
    digitalWrite(3, HIGH);  // RS
    delayMicroseconds( 50 );
    digitalWrite(3, LOW);  //RS

      if (Get_data()) { //Read the Serial bytes coming in
      currentValue = DMM_ARRAY[2];
      delayMicroseconds( 200 );
      digitalWrite(2, LOW);  // CTS Close
     
      deltaTheta =((previousValue - currentValue)* 1.113065); // count * 1.113065   ARC SEC / SEC
      
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(deltaTheta / (deltaT / 1000000)); //3 decimal place values
      lcd.print(" ");
      
      lcd.setCursor(0, 1);
      lcd.print( DMM_ARRAY[2]);// Print btye 3 (LSB)
      lcd.print(" ");
      
      }

  previousMicros = currentMicros;
  previousValue = currentValue;
  
  }

}

/*
void  Get_data() {

if (Serial.available() >= 6) {
    for (int i = 0; i < 6; i++) {
      DMM_ARRAY[i] = Serial.read();
    }
  }
}
*/