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.
#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());
}
}
}
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)
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
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 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
#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.
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.
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();
}
}
}
*/