|
| 1 | +/* |
| 2 | + u-blox Example: ESF RAW (100Hz!) |
| 3 | + By: Paul Clark |
| 4 | + SparkFun Electronics |
| 5 | + Date: September 8th, 2022 |
| 6 | + License: MIT. See license file for more information but you can |
| 7 | + basically do whatever you want with this code. |
| 8 | +
|
| 9 | + This example configures the External Sensor Fusion RAW IMU sensor messages on the NEO-M8U / ZED-F9R and |
| 10 | + shows how to access the ESF data in the loop - without using the callback. |
| 11 | +
|
| 12 | + Notes: |
| 13 | + On the ZED-F9R, each ESF RAW message contains _one_ set of IMU sensor data: seven readings in total (3 x Accel, 3 x Gyro, 1 x Temperature). |
| 14 | + However, on the NEO-M8U, each message contains _ten_ sets of IMU sensor data, seventy readings in total. |
| 15 | + The NEO-M8U data is all timestamped and it is possible to reconstruct the full data stream, you just need to do it |
| 16 | + ten samples at a time... |
| 17 | + Also, note that the sensor data is 24-bit signed (two's complement). You need to be careful when converting to int32_t. |
| 18 | + Data will arrive at 100Hz! (10Hz x 10 on the NEO-M8U) |
| 19 | + 400kHz I2C is essential... |
| 20 | + Serial printing needs to be kept short and the baud rate needs to be at least 230400. |
| 21 | +
|
| 22 | + Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary: |
| 23 | + https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources |
| 24 | +
|
| 25 | + Feel like supporting open source hardware? |
| 26 | + Buy a board from SparkFun! |
| 27 | + NEO-M8U: https://www.sparkfun.com/products/16329 |
| 28 | +
|
| 29 | + Hardware Connections: |
| 30 | + Plug a Qwiic cable into the GPS and a Redboard Qwiic |
| 31 | + If you don't have a platform with a Qwiic connection use the |
| 32 | + SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) |
| 33 | + Open the serial monitor at 115200 baud to see the output |
| 34 | +
|
| 35 | +*/ |
| 36 | + |
| 37 | +#include <Wire.h> //Needed for I2C to GPS |
| 38 | + |
| 39 | +#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS |
| 40 | +SFE_UBLOX_GNSS myGNSS; |
| 41 | + |
| 42 | +// Callback: printESFRAWdata will be called when new ESF RAW data arrives |
| 43 | +// See u-blox_structs.h for the full definition of UBX_ESF_RAW_data_t |
| 44 | +// _____ You can use any name you like for the callback. Use the same name when you call setAutoESFRAWcallback |
| 45 | +// / _____ This _must_ be UBX_ESF_RAW_data_t |
| 46 | +// | / _____ You can use any name you like for the struct |
| 47 | +// | | / |
| 48 | +// | | | |
| 49 | +void printESFRAWdata(UBX_ESF_RAW_data_t *ubxDataStruct) |
| 50 | +{ |
| 51 | + Serial.println(F("Hey! The ESF RAW callback has been called!")); |
| 52 | +} |
| 53 | + |
| 54 | +void setup() |
| 55 | +{ |
| 56 | + Serial.begin(230400); // <--- Use >> 100k baud (see notes above) |
| 57 | + |
| 58 | + while (!Serial); //Wait for user to open terminal |
| 59 | + Serial.println(F("SparkFun u-blox Example")); |
| 60 | + |
| 61 | + Wire.begin(); |
| 62 | + Wire.setClock(400000); // <-- Use 400kHz I2C (ESSENTIAL) |
| 63 | + |
| 64 | + //myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial |
| 65 | + |
| 66 | + if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port |
| 67 | + { |
| 68 | + Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing.")); |
| 69 | + while (1); |
| 70 | + } |
| 71 | + |
| 72 | + myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) |
| 73 | + myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR |
| 74 | + |
| 75 | + myGNSS.setI2CpollingWait(5); //Allow checkUblox to poll I2C data every 5ms to keep up with the ESF RAW messages |
| 76 | + |
| 77 | + if (myGNSS.setAutoESFRAWcallbackPtr(&printESFRAWdata) == true) // Enable automatic ESF RAW messages with callback to printESFRAWdata |
| 78 | + Serial.println(F("setAutoESFRAWcallback successful")); |
| 79 | +} |
| 80 | + |
| 81 | +void loop() |
| 82 | +{ |
| 83 | + myGNSS.checkUblox(); // Check for the arrival of new data and process it. |
| 84 | + |
| 85 | + // Check if new ESF RAW data has arrived: |
| 86 | + // If myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid is true, it indicates new ESF RAW data has been received and has been copied. |
| 87 | + // automaticFlags.flags.bits.callbackCopyValid will be cleared automatically when the callback is called. |
| 88 | + |
| 89 | + if (myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid == true) |
| 90 | + { |
| 91 | + // But, we can manually clear the callback flag too. This will prevent the callback from being called! |
| 92 | + myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid = false; // Comment this line if you still want the callback to be called |
| 93 | + |
| 94 | + // myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks indicates how many sensor readings the UBX_ESF_RAW_data_t contains. |
| 95 | + // On the ZED-F9R, numEsfRawBlocks will be 7: 3 x Accel, 3 x Gyro, 1 x Temperature. |
| 96 | + // On the NEO-M8U, numEsfRawBlocks will be 70: 10 sets of sensor data. The sensor time tag (sTag) |
| 97 | + // indicates the timing of each sample. |
| 98 | + // Serial output will be approx. 110 bytes depending on how many digits are in the sensor readings. |
| 99 | + // To keep up, Serial needs to be running at 100k baud minimum. 230400 is recommended. |
| 100 | + |
| 101 | + uint32_t sTag = 0xFFFFFFFF; // Sensor time tag |
| 102 | + |
| 103 | + // Only print the first seven sensor readings (on the NEO-M8U) |
| 104 | + for (uint8_t i = 0; (i < myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks) && (i < 7); i++) |
| 105 | + // For fun, and to prove it works, uncomment use this line instead to get the full 100Hz data on the NEO-M8U |
| 106 | + //for (uint8_t i = 0; i < myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks; i++) |
| 107 | + { |
| 108 | + // Print sTag the first time - and also if it changes |
| 109 | + if (sTag != myGNSS.packetUBXESFRAW->callbackData->data[i].sTag) |
| 110 | + { |
| 111 | + sTag = myGNSS.packetUBXESFRAW->callbackData->data[i].sTag; |
| 112 | + Serial.print(F("Time:")); |
| 113 | + Serial.println(sTag); |
| 114 | + } |
| 115 | + |
| 116 | + // Print the sensor data type |
| 117 | + // From the M8 interface description: |
| 118 | + // 0: None |
| 119 | + // 1-4: Reserved |
| 120 | + // 5: z-axis gyroscope angular rate deg/s * 2^-12 signed |
| 121 | + // 6: front-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward) |
| 122 | + // 7: front-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward) |
| 123 | + // 8: rear-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward) |
| 124 | + // 9: rear-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward) |
| 125 | + // 10: speed ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward) |
| 126 | + // 11: speed m/s * 1e-3 signed |
| 127 | + // 12: gyroscope temperature deg Celsius * 1e-2 signed |
| 128 | + // 13: y-axis gyroscope angular rate deg/s * 2^-12 signed |
| 129 | + // 14: x-axis gyroscope angular rate deg/s * 2^-12 signed |
| 130 | + // 16: x-axis accelerometer specific force m/s^2 * 2^-10 signed |
| 131 | + // 17: y-axis accelerometer specific force m/s^2 * 2^-10 signed |
| 132 | + // 18: z-axis accelerometer specific force m/s^2 * 2^-10 signed |
| 133 | + switch (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType) |
| 134 | + { |
| 135 | + case 5: |
| 136 | + Serial.print(F("Zgyr:")); |
| 137 | + break; |
| 138 | + case 12: |
| 139 | + Serial.print(F("Temp:")); |
| 140 | + break; |
| 141 | + case 13: |
| 142 | + Serial.print(F("Ygyr:")); |
| 143 | + break; |
| 144 | + case 14: |
| 145 | + Serial.print(F("Xgyr:")); |
| 146 | + break; |
| 147 | + case 16: |
| 148 | + Serial.print(F("Xacc:")); |
| 149 | + break; |
| 150 | + case 17: |
| 151 | + Serial.print(F("Yacc:")); |
| 152 | + break; |
| 153 | + case 18: |
| 154 | + Serial.print(F("Zacc:")); |
| 155 | + break; |
| 156 | + default: |
| 157 | + break; |
| 158 | + } |
| 159 | + |
| 160 | + // Gyro data |
| 161 | + if ((myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 5) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 13) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 14)) |
| 162 | + { |
| 163 | + union |
| 164 | + { |
| 165 | + int32_t signed32; |
| 166 | + uint32_t unsigned32; |
| 167 | + } signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t |
| 168 | + // The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t |
| 169 | + signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data |
| 170 | + float rate = signedUnsigned.signed32; // Extract the signed data. Convert to float |
| 171 | + rate /= 256.0; // Divide by 256 to undo the shift |
| 172 | + rate *= 0.000244140625; // Convert from deg/s * 2^-12 to deg/s |
| 173 | + Serial.println(rate); |
| 174 | + } |
| 175 | + // Accelerometer data |
| 176 | + else if ((myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 16) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 17) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 18)) |
| 177 | + { |
| 178 | + union |
| 179 | + { |
| 180 | + int32_t signed32; |
| 181 | + uint32_t unsigned32; |
| 182 | + } signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t |
| 183 | + // The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t |
| 184 | + signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data |
| 185 | + float force = signedUnsigned.signed32; // Extract the signed data. Convert to float |
| 186 | + force /= 256.0; // Divide by 256 to undo the shift |
| 187 | + force *= 0.0009765625; // Convert from m/s^2 * 2^-10 to m/s^2 |
| 188 | + Serial.println(force); |
| 189 | + } |
| 190 | + // Gyro Temperature |
| 191 | + else if (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 12) |
| 192 | + { |
| 193 | + union |
| 194 | + { |
| 195 | + int32_t signed32; |
| 196 | + uint32_t unsigned32; |
| 197 | + } signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t |
| 198 | + // The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t |
| 199 | + signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data |
| 200 | + float temperature = signedUnsigned.signed32; // Extract the signed data. Convert to float |
| 201 | + temperature /= 256.0; // Divide by 256 to undo the shift |
| 202 | + temperature *= 0.01; // Convert from C * 1e-2 to C |
| 203 | + Serial.println(temperature); |
| 204 | + } |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed. There will not be any in this example, unless you commented the line above |
| 209 | +} |
0 commit comments