diff --git a/README.md b/README.md index 5eb3018..aa9d1be 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,42 @@ # arduino_pps_gprmc_time_sync -Use Arduino Uno to simulate a GPS module, sending PPS pulse and GPRMC to Lidar + +Use Arduino Uno to simulate a GPS module, sending PPS pulse and GPRMC message to Lidar. + +## Verified Platform +- Arduino Uno +- Velodyne Lidar VLP-16 +- ROS 2 Foxy with VLP-16 driver https://github.com/ros-drivers + +## Wiring Connection + +| VLP-16 | Arduino | +| ---- | ---- | +| Ground | GND | +| GPS PULSE | Pin#8 | +| GPS RECEIVE | Pin#11 | + +## VLP-16 Configuraition + +Make sure the parameter `gps_time` in yaml file is set to **true** to enable time sync. + +![](resource/vlp16_gps_time.png) + +## Test Result + +- VLP-16 web interface + +It is successfully synchronized if you see the coordinates of **GPS Position** and locked **PPS** status. + +![](resource/vlp16_pps_locked.jpg) + +- VLP-16 ROS 2 timestamp + +If the `gps_time` parameter is set, then you will see the time jump after time synchronized. + +![](resource/vlp16_time_sync_stamp.jpg) + +- GPRMC debug message + +You can enable the debug messages in Arduino code to monitor the GPRMC. + +![](resource/arduino_serial_monitor.png) diff --git a/resource/arduino_serial_monitor.png b/resource/arduino_serial_monitor.png new file mode 100644 index 0000000..3599929 Binary files /dev/null and b/resource/arduino_serial_monitor.png differ diff --git a/resource/vlp16_gps_time.png b/resource/vlp16_gps_time.png new file mode 100644 index 0000000..c12b8fb Binary files /dev/null and b/resource/vlp16_gps_time.png differ diff --git a/resource/vlp16_pps_locked.jpg b/resource/vlp16_pps_locked.jpg new file mode 100644 index 0000000..c763632 Binary files /dev/null and b/resource/vlp16_pps_locked.jpg differ diff --git a/resource/vlp16_time_sync_stamp.jpg b/resource/vlp16_time_sync_stamp.jpg new file mode 100644 index 0000000..d196cc9 Binary files /dev/null and b/resource/vlp16_time_sync_stamp.jpg differ diff --git a/src/pps_gprmc_time_sync/pps_gprmc_time_sync.ino b/src/pps_gprmc_time_sync/pps_gprmc_time_sync.ino new file mode 100644 index 0000000..b926398 --- /dev/null +++ b/src/pps_gprmc_time_sync/pps_gprmc_time_sync.ino @@ -0,0 +1,112 @@ +#include + +const byte pps_pin = 8; +const byte msg_pin = 9; +const byte rx_pin = 10; +const byte tx_pin = 11; +const bool inverse_logic = true; +SoftwareSerial gprmc_conn(rx_pin, tx_pin, inverse_logic); + +const unsigned int trigger_freq = 1; +const unsigned long dt = 1000000 / trigger_freq; // => dt = 1 sec +const unsigned long dt_pps_pull_low = dt + 100000; // => dt + 100ms +const unsigned long dt_sent_gprmc = dt_pps_pull_low + 250000; // => dt_pps_pull_low + 100ms +unsigned long timestamp; +unsigned long trigger_start_time; +unsigned long ts_pps_high; +unsigned long ts_pps_low; +unsigned long ts_msg_high; +unsigned long ts_msg_low; +unsigned long i; + +// Start timestamp: HH:MM:SS +int hh = 00; +int mm = 01; +int ss = 02; +// GPS coordinates from Taipei +char pos_latitude[] = "25.04776"; +char pos_longitude[] = "121.53185"; + +void setup() { + pinMode(rx_pin, INPUT); + pinMode(tx_pin, OUTPUT); + pinMode(pps_pin, OUTPUT); // PPS + pinMode(msg_pin, OUTPUT); // Indicator of msg_sent + Serial.begin(9600); + gprmc_conn.begin(9600); + // gprmc_conn.println("Hello World!"); + trigger_start_time = micros(); +} + +void loop() { + char buffer[128]; + byte CRC = 0; + + timestamp = micros() - trigger_start_time; + if (timestamp >= dt*i + dt_sent_gprmc) + { + digitalWrite(msg_pin, HIGH); + i += 1; + if (ss == 59) + { + ss = 0; + if (mm == 59) + { + mm = 0; + hh += 1; + } + else mm += 1; + } + else ss += 1; + + ts_msg_high = micros(); + + // Prepare GPRMC msg + sprintf(buffer, "GPRMC,%02d%02d%02d,A,%s,N,%s,E,022.4,084.4,070423,,A", hh, mm, ss, pos_latitude, pos_longitude); + for (byte x = 0; x < strlen(buffer); x++) { + // XOR every character in between '$' and '*' + CRC = CRC ^ buffer[x]; + } + + // Send GPRMC msg + gprmc_conn.print("$"); + gprmc_conn.print(buffer); + gprmc_conn.print("*"); + gprmc_conn.print(CRC, HEX); + gprmc_conn.println(); + + digitalWrite(msg_pin, LOW); + ts_msg_low = micros(); + +#if 0 + // Print Debug Messages + Serial.print("PPS at: "); + Serial.print(ts_pps_high); + Serial.print(" PPS low after: "); + Serial.print(ts_pps_low-ts_pps_high); + Serial.print(" MSG high after: "); + Serial.print(ts_msg_high-ts_pps_high); + Serial.print(" MSG low after: "); + Serial.print(ts_msg_low-ts_pps_high); + Serial.print(" $"); + Serial.print(buffer); + Serial.print("*"); + Serial.print(CRC, HEX); + Serial.println(); +#endif + + } + else if (timestamp >= dt*i + dt_pps_pull_low) + { + // Pull pps to low + digitalWrite(pps_pin, LOW); + ts_pps_low = micros(); + } + else if (timestamp >= dt*i) + { + // Pull pps to high + digitalWrite(pps_pin, HIGH); + ts_pps_high = micros(); + } + +}