Contains added code for stm32-L432KC compatibility

Dependents:   BNO080_stm32_compatible

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BNO080.cpp Source File

BNO080.cpp

00001 //
00002 // USC RPL BNO080 driver.
00003 //
00004 
00005 /*
00006  * Overview of BNO080 Communications
00007  * ===============================================
00008  *
00009  * Hilcrest has developed a protocol called SHTP (Sensor Hub Transport Protocol) for binary communications with
00010  * the BNO080 and the other IMUs it sells.  Over this protocol, SH-2 (Sensor Hub 2) messages are sent to configure
00011  * the chip and read data back.
00012  *
00013  * SHTP messages are divided at two hierarchical levels: first the channel, then the report ID.  Each category
00014  * of messages (system commands, sensor data reports, etc.) has its own channel, and the individual messages
00015  * in each channel are identified by their report id, which is the first byte of the message payload (note that the
00016  * datasheets don't *always* call the first byte the report ID, but that byte does identify the report, so I'm going
00017  * with it).
00018  *
00019  * ===============================================
00020  *
00021  * Information about the BNO080 is split into three datasheets.  Here's the download links and what they cover:
00022  *
00023  * - the BNO080 datasheet: http://www.hillcrestlabs.com/download/5a05f340566d07c196001ec1
00024  * -- Chip pinouts
00025  * -- Example circuits
00026  * -- Physical specifications
00027  * -- Supported reports and configuration settings (at a high level)
00028  * -- List of packets on the SHTP executable channel
00029  *
00030  * - the SHTP protocol: http://www.hillcrestlabs.com/download/59de8f99cd829e94dc0029d7
00031  * -- SHTP transmit and receive protcols (for SPI, I2C, and UART)
00032  * -- SHTP binary format
00033  * -- packet types on the SHTP command channel
00034  *
00035  * - the SH-2 reference: http://www.hillcrestlabs.com/download/59de8f398934bf6faa00293f
00036  * -- list of packets and their formats for all channels other than command and executable
00037  * -- list of FRS (Flash Record System) entries and their formats
00038  *
00039  * ===============================================
00040  *
00041  * Overview of SHTP channels:
00042  *
00043  * 0 -> Command
00044  * -- Used for protocol-global packets, currently only the advertisement packet (which lists all the channels) and error reports
00045  *
00046  * 1 -> Executable
00047  * -- Used for things that control the software on the chip: commands to reset and sleep
00048  * -- Also used by the chip to report when it's done booting up
00049  *
00050  * 2 -> Control
00051  * -- Used to send configuration commands to the IMU and for it to send back responses.
00052  * -- Common report IDs: Command Request (0xF2), Set Feature (0xFD)
00053  *
00054  * 3 -> Sensor Reports
00055  * -- Used for sensors to send back data reports.
00056  * -- AFAIK the only report ID on this channel will be 0xFB (Report Base Timestamp); sensor data is send in a series of structures
00057  *    following an 0xFB
00058  *
00059  * 4 -> Wake Sensor Reports
00060  * -- same as above, but for sensors configured to wake the device
00061  *
00062  * 5 -> Gyro Rotation Vector
00063  * -- a dedicated channel for the Gyro Rotation Vector sensor report
00064  * -- Why does this get its own channel?  I don't know!!!
00065  */
00066 
00067 #include "BNO080.h"
00068 #include "BNO080Constants.h"
00069 /// Set to 1 to enable debug printouts.  Should be very useful if the chip is giving you trouble.
00070 /// When debugging, it is recommended to use the highest possible serial baudrate so as not to interrupt the timing of operations.
00071 #define BNO_DEBUG 0
00072 
00073 BNO080::BNO080(Serial *debugPort, PinName user_SDApin, PinName user_SCLpin, PinName user_INTPin, PinName user_RSTPin,
00074                uint8_t i2cAddress, int i2cPortSpeed) :
00075     _debugPort(debugPort),
00076     _i2cPort(user_SDApin, user_SCLpin),
00077     _i2cAddress(i2cAddress),
00078     _int(user_INTPin),
00079     _rst(user_RSTPin, 1),
00080     commandSequenceNumber(0),
00081     stability(UNKNOWN),
00082     stepDetected(false),
00083     stepCount(0),
00084     significantMotionDetected(false),
00085     shakeDetected(false),
00086     xAxisShake(false),
00087     yAxisShake(false),
00088     zAxisShake(false)
00089 {
00090     // zero sequence numbers
00091     memset(sequenceNumber, 0, sizeof(sequenceNumber));
00092 
00093     //Get user settings
00094     _i2cPortSpeed = i2cPortSpeed;
00095     if(_i2cPortSpeed > 4000000) {
00096         _i2cPortSpeed = 4000000; //BNO080 max is 400Khz
00097     }
00098     _i2cPort.frequency(_i2cPortSpeed);
00099     
00100 
00101 }
00102 
00103 bool BNO080::begin()
00104 {
00105     //Configure the BNO080 for SPI communication
00106 
00107     _rst = 0; // Reset BNO080
00108     wait(.002f); // Min length not specified in datasheet?
00109     _rst = 1; // Bring out of reset
00110 
00111     // wait for a falling edge (NOT just a low) on the INT pin to denote startup
00112     Timer timeoutTimer;
00113 
00114     bool highDetected = false;
00115     bool lowDetected = false;
00116 
00117     while(true) {
00118         if(timeoutTimer.read() > BNO080_RESET_TIMEOUT) {
00119             _debugPort->printf("Error: BNO080 reset timed out, chip not detected.\n");
00120             return false;
00121         }
00122 
00123         // simple edge detector
00124         if(!highDetected) {
00125             if(_int == 1) {
00126                 highDetected = true;
00127             }
00128         } else if(!lowDetected) {
00129             if(_int == 0) {
00130                 lowDetected = true;
00131             }
00132         } else {
00133             // high and low detected
00134             break;
00135         }
00136     }
00137 
00138     _debugPort->printf("BNO080 detected!\n");
00139 
00140     // At system startup, the hub must send its full advertisement message (see SHTP 5.2 and 5.3) to the
00141     // host. It must not send any other data until this step is complete.
00142     // We don't actually care what's in it, we're just using it as a signal to indicate that the reset is complete.
00143     receivePacket();
00144 
00145     // now, after startup, the BNO will send an Unsolicited Initialize response (SH-2 section 6.4.5.2), and an Executable Reset command
00146     waitForPacket(CHANNEL_EXECUTABLE, EXECUTABLE_REPORTID_RESET);
00147 
00148     // Next, officially tell it to initialize, and wait for a successful Initialize Response
00149     zeroBuffer();
00150     //changed from sendCommand
00151     sendCommand(COMMAND_INITIALIZE);
00152     
00153     wait(0.02f);
00154     
00155     if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_COMMAND_RESPONSE) || shtpData[2] != COMMAND_INITIALIZE || shtpData[5] != 0) {
00156         _debugPort->printf("BNO080 reports initialization failed.\n");
00157         __enable_irq();
00158         return false;
00159     } else {
00160 #if BNO_DEBUG
00161         _debugPort->printf("BNO080 reports initialization successful!\n");
00162 #endif
00163     }
00164 
00165 
00166     // Finally, we want to interrogate the device about its model and version.
00167     zeroBuffer();
00168     shtpData[0] = SHTP_REPORT_PRODUCT_ID_REQUEST; //Request the product ID and reset info
00169     shtpData[1] = 0; //Reserved
00170     sendPacket(CHANNEL_CONTROL, 2);
00171 
00172     waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_PRODUCT_ID_RESPONSE, 5);
00173 
00174     if (shtpData[0] == SHTP_REPORT_PRODUCT_ID_RESPONSE) {
00175         majorSoftwareVersion = shtpData[2];
00176         minorSoftwareVersion = shtpData[3];
00177         patchSoftwareVersion = (shtpData[13] << 8) | shtpData[12];
00178         partNumber = (shtpData[7] << 24) | (shtpData[6] << 16) | (shtpData[5] << 8) | shtpData[4];
00179         buildNumber = (shtpData[11] << 24) | (shtpData[10] << 16) | (shtpData[9] << 8) | shtpData[8];
00180 
00181 #if BNO_DEBUG
00182         _debugPort->printf("BNO080 reports as SW version %hhu.%hhu.%hu, build %lu, part no. %lu\n",
00183                            majorSoftwareVersion, minorSoftwareVersion, patchSoftwareVersion,
00184                            buildNumber, partNumber);
00185 #endif
00186 
00187     } else {
00188         _debugPort->printf("Bad response from product ID command.\n");
00189         return false;
00190     }
00191 
00192     // successful init
00193     return true;
00194 
00195 }
00196 
00197 void BNO080::tare(bool zOnly)
00198 {
00199     zeroBuffer();
00200 
00201     // from SH-2 section 6.4.4.1
00202     shtpData[3] = 0; // perform tare now
00203 
00204     if(zOnly) {
00205         shtpData[4] = 0b100; // tare Z axis
00206     } else {
00207         shtpData[4] = 0b111; // tare X, Y, and Z axes
00208     }
00209 
00210     shtpData[5] = 0; // reorient all motion outputs
00211 
00212     sendCommand(COMMAND_TARE);
00213 }
00214 
00215 bool BNO080::enableCalibration(bool calibrateAccel, bool calibrateGyro, bool calibrateMag)
00216 {
00217     // send the Configure ME Calibration command
00218     zeroBuffer();
00219 
00220     shtpData[3] = static_cast<uint8_t>(calibrateAccel ? 1 : 0);
00221     shtpData[4] = static_cast<uint8_t>(calibrateGyro ? 1 : 0);
00222     shtpData[5] = static_cast<uint8_t>(calibrateMag ? 1 : 0);
00223 
00224     shtpData[6] = 0; // Configure ME Calibration command
00225 
00226     shtpData[7] = 0; // planar accelerometer calibration always disabled
00227 
00228     sendCommand(COMMAND_ME_CALIBRATE);
00229 
00230     // now, wait for the response
00231     if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_COMMAND_RESPONSE)) {
00232 #if BNO_DEBUG
00233         _debugPort->printf("Timeout waiting for calibration response!\n");
00234 #endif
00235         return false;
00236     }
00237 
00238     if(shtpData[2] != COMMAND_ME_CALIBRATE) {
00239 #if BNO_DEBUG
00240         _debugPort->printf("Received wrong response to calibration command!\n");
00241 #endif
00242         return false;
00243     }
00244 
00245     if(shtpData[5] != 0) {
00246 #if BNO_DEBUG
00247         _debugPort->printf("IMU reports calibrate command failed!\n");
00248 #endif
00249         return false;
00250     }
00251 
00252     // acknowledge checks out!
00253     return true;
00254 }
00255 
00256 bool BNO080::saveCalibration()
00257 {
00258     zeroBuffer();
00259 
00260     // no arguments
00261     sendCommand(COMMAND_SAVE_DCD);
00262 
00263     // now, wait for the response
00264     if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_COMMAND_RESPONSE)) {
00265 #if BNO_DEBUG
00266         _debugPort->printf("Timeout waiting for calibration response!\n");
00267 #endif
00268         return false;
00269     }
00270 
00271     if(shtpData[2] != COMMAND_SAVE_DCD) {
00272 #if BNO_DEBUG
00273         _debugPort->printf("Received wrong response to calibration command!\n");
00274 #endif
00275         return false;
00276     }
00277 
00278     if(shtpData[5] != 0) {
00279 #if BNO_DEBUG
00280         _debugPort->printf("IMU reports calibrate command failed!\n");
00281 #endif
00282         return false;
00283     }
00284 
00285     // acknowledge checks out!
00286     return true;
00287 }
00288 
00289 void BNO080::setSensorOrientation(Quaternion orientation)
00290 {
00291     zeroBuffer();
00292 
00293     _debugPort->printf("y: %f", orientation.y());
00294 
00295     // convert floats to Q
00296     int16_t Q_x = floatToQ(orientation.x(), ORIENTATION_QUAT_Q_POINT);
00297     int16_t Q_y = floatToQ(orientation.y(), ORIENTATION_QUAT_Q_POINT);
00298     int16_t Q_z = floatToQ(orientation.z(), ORIENTATION_QUAT_Q_POINT);
00299     int16_t Q_w = floatToQ(orientation.w(), ORIENTATION_QUAT_Q_POINT);
00300 
00301     _debugPort->printf("Q_y: %hd", Q_y);
00302 
00303     shtpData[3] = 2; // set reorientation
00304 
00305     shtpData[4] = static_cast<uint8_t>(Q_x & 0xFF); //P1 - X component LSB
00306     shtpData[5] = static_cast<uint8_t>(Q_x >> 8); //P2 - X component MSB
00307 
00308     shtpData[6] = static_cast<uint8_t>(Q_y & 0xFF); //P3 - Y component LSB
00309     shtpData[7] = static_cast<uint8_t>(Q_y >> 8); //P4 - Y component MSB
00310 
00311     shtpData[8] = static_cast<uint8_t>(Q_z & 0xFF); //P5 - Z component LSB
00312     shtpData[9] = static_cast<uint8_t>(Q_z >> 8); //P6 - Z component MSB
00313 
00314     shtpData[10] = static_cast<uint8_t>(Q_w & 0xFF); //P7 - W component LSB
00315     shtpData[11] = static_cast<uint8_t>(Q_w >> 8); //P8 - W component MSB
00316 
00317     //Using this shtpData packet, send a command
00318     sendCommand(COMMAND_TARE); // Send tare command
00319 
00320     // NOTE: unlike literally every other command, a sensor orientation command is never acknowledged in any way.
00321 }
00322 
00323 
00324 bool BNO080::updateData()
00325 {
00326     if(_int.read() != 0) {
00327         // no waiting packets
00328         return false;
00329     }
00330 
00331     while(_int.read() == 0) {
00332         if(!receivePacket()) {
00333             // comms error
00334             return false;
00335         }
00336 
00337         processPacket();
00338         //wait(0.002f); //added
00339     }
00340 
00341     // packets were received, so data may have changed
00342     return true;
00343 }
00344 
00345 uint8_t BNO080::getReportStatus(Report report)
00346 {
00347     uint8_t reportNum = static_cast<uint8_t>(report);
00348     if(reportNum > STATUS_ARRAY_LEN) {
00349         return 0;
00350     }
00351 
00352     return reportStatus[reportNum];
00353 }
00354 
00355 const char* BNO080::getReportStatusString(Report report)
00356 {
00357     switch(getReportStatus(report)) {
00358         case 0:
00359             return "Unreliable";
00360         case 1:
00361             return "Accuracy Low";
00362         case 2:
00363             return "Accuracy Medium";
00364         case 3:
00365             return "Accuracy High";
00366         default:
00367             return "Error";
00368     }
00369 }
00370 
00371 bool BNO080::hasNewData(Report report)
00372 {
00373     uint8_t reportNum = static_cast<uint8_t>(report);
00374     if(reportNum > STATUS_ARRAY_LEN) {
00375         return false;
00376     }
00377 
00378     bool newData = reportHasBeenUpdated[reportNum];
00379     reportHasBeenUpdated[reportNum] = false; // clear flag
00380     return newData;
00381 }
00382 
00383 //Sends the packet to enable the rotation vector
00384 void BNO080::enableReport(Report report, uint16_t timeBetweenReports)
00385 {
00386     // check time
00387     float periodSeconds = timeBetweenReports / 1000.0;
00388 
00389     if(periodSeconds < getMinPeriod(report)) {
00390         _debugPort->printf("Error: attempt made to set report 0x%02hhx to period of %.06f s, which is smaller than its min period of %.06f s.\n",
00391                            static_cast<uint8_t>(report), periodSeconds, getMinPeriod(report));
00392         return;
00393     }
00394     /*
00395     else if(getMaxPeriod(report) > 0 && periodSeconds > getMaxPeriod(report))
00396     {
00397         _debugPort->printf("Error: attempt made to set report 0x%02hhx to period of %.06f s, which is larger than its max period of %.06f s.\n",
00398                            static_cast<uint8_t>(report), periodSeconds, getMaxPeriod(report));
00399         return;
00400     }
00401     */
00402     setFeatureCommand(static_cast<uint8_t>(report), timeBetweenReports);
00403 
00404     // note: we don't wait for ACKs on these packets because they can take quite a while, like half a second, to come in
00405 }
00406 
00407 void BNO080::disableReport(Report report)
00408 {
00409     // set the report's polling period to zero to disable it
00410     setFeatureCommand(static_cast<uint8_t>(report), 0);
00411 }
00412 
00413 uint32_t BNO080::getSerialNumber()
00414 {
00415     uint32_t serNoBuffer;
00416 
00417     if(!readFRSRecord(FRS_RECORDID_SERIAL_NUMBER, &serNoBuffer, 1)) {
00418         return 0;
00419     }
00420 
00421     return serNoBuffer;
00422 }
00423 
00424 float BNO080::getRange(Report report)
00425 {
00426     loadReportMetadata(report);
00427 
00428     return qToFloat_dword(metadataRecord[1], getQ1(report));
00429 }
00430 
00431 
00432 float BNO080::getResolution(Report report)
00433 {
00434     loadReportMetadata(report);
00435 
00436     return qToFloat_dword(metadataRecord[2], getQ1(report));
00437 }
00438 
00439 float BNO080::getPower(Report report)
00440 {
00441     loadReportMetadata(report);
00442 
00443     uint16_t powerQ = static_cast<uint16_t>(metadataRecord[3] & 0xFFFF);
00444 
00445     return qToFloat_dword(powerQ, POWER_Q_POINT);
00446 }
00447 
00448 float BNO080::getMinPeriod(Report report)
00449 {
00450     loadReportMetadata(report);
00451 
00452     return metadataRecord[4] / 1e6f; // convert from microseconds to seconds
00453 }
00454 
00455 float BNO080::getMaxPeriod(Report report)
00456 {
00457     loadReportMetadata(report);
00458 
00459     if(getMetaVersion() == 3) {
00460         // no max period entry in this record format
00461         return -1.0f;
00462     }
00463 
00464     return metadataRecord[9] / 1e6f; // convert from microseconds to seconds
00465 }
00466 
00467 void BNO080::printMetadataSummary(Report report)
00468 {
00469 #if BNO_DEBUG
00470     if(!loadReportMetadata(report)) {
00471         _debugPort->printf("Failed to load report metadata!\n");
00472     }
00473 
00474     _debugPort->printf("======= Metadata for report 0x%02hhx =======\n", static_cast<uint8_t>(report));
00475 
00476     _debugPort->printf("Range: +- %.04f units\n", getRange(report));
00477     _debugPort->printf("Resolution: %.04f units\n", getResolution(report));
00478     _debugPort->printf("Power Used: %.03f mA\n", getPower(report));
00479     _debugPort->printf("Min Period: %.06f s\n", getMinPeriod(report));
00480     _debugPort->printf("Max Period: %.06f s\n\n", getMaxPeriod(report));
00481 
00482 #endif
00483 }
00484 
00485 int16_t BNO080::getQ1(Report report)
00486 {
00487     loadReportMetadata(report);
00488 
00489     return static_cast<int16_t>(metadataRecord[7] & 0xFFFF);
00490 }
00491 
00492 int16_t BNO080::getQ2(Report report)
00493 {
00494     loadReportMetadata(report);
00495 
00496     return static_cast<int16_t>(metadataRecord[7] >> 16);
00497 }
00498 
00499 int16_t BNO080::getQ3(Report report)
00500 {
00501     loadReportMetadata(report);
00502 
00503     return static_cast<int16_t>(metadataRecord[8] >> 16);
00504 }
00505 
00506 void BNO080::processPacket()
00507 {
00508     if(shtpHeader[2] == CHANNEL_CONTROL) {
00509         // currently no command reports are read
00510     } else if(shtpHeader[2] == CHANNEL_EXECUTABLE) {
00511         // currently no executable reports are read
00512     } else if(shtpHeader[2] == CHANNEL_COMMAND) {
00513 
00514     } else if(shtpHeader[2] == CHANNEL_REPORTS || shtpHeader[2] == CHANNEL_WAKE_REPORTS) {
00515         if(shtpData[0] == SHTP_REPORT_BASE_TIMESTAMP) {
00516             parseSensorDataPacket();
00517             
00518         }
00519     }
00520 }
00521 
00522 // sizes of various sensor data packet elements
00523 #define SIZEOF_BASE_TIMESTAMP 5
00524 #define SIZEOF_TIMESTAMP_REBASE 5
00525 #define SIZEOF_ACCELEROMETER 10
00526 #define SIZEOF_LINEAR_ACCELERATION 10
00527 #define SIZEOF_GYROSCOPE_CALIBRATED 10
00528 #define SIZEOF_MAGNETIC_FIELD_CALIBRATED 10
00529 #define SIZEOF_MAGNETIC_FIELD_UNCALIBRATED 16
00530 #define SIZEOF_ROTATION_VECTOR 14
00531 #define SIZEOF_GAME_ROTATION_VECTOR 12
00532 #define SIZEOF_GEOMAGNETIC_ROTATION_VECTOR 14
00533 #define SIZEOF_TAP_DETECTOR 5
00534 #define SIZEOF_STABILITY_REPORT 6
00535 #define SIZEOF_STEP_DETECTOR 8
00536 #define SIZEOF_STEP_COUNTER 12
00537 #define SIZEOF_SIGNIFICANT_MOTION 6
00538 #define SIZEOF_SHAKE_DETECTOR 6
00539 
00540 void BNO080::parseSensorDataPacket()
00541 {
00542     size_t currReportOffset = 0;
00543 
00544     // every sensor data report first contains a timestamp offset to show how long it has been between when
00545     // the host interrupt was sent and when the packet was transmitted.
00546     // We don't use interrupts and don't care about times, so we can throw this out.
00547     currReportOffset += SIZEOF_BASE_TIMESTAMP;
00548 
00549     while(currReportOffset < packetLength) {
00550         if(currReportOffset >= STORED_PACKET_SIZE) {
00551             _debugPort->printf("Error: sensor report longer than packet buffer!\n");
00552             return;
00553         }
00554 
00555         // lots of sensor reports use 3 16-bit numbers stored in bytes 4 through 9
00556         // we can save some time by parsing those out here.
00557         uint16_t data1 = (uint16_t)shtpData[currReportOffset + 5] << 8 | shtpData[currReportOffset + 4];
00558         uint16_t data2 = (uint16_t)shtpData[currReportOffset + 7] << 8 | shtpData[currReportOffset + 6];
00559         uint16_t data3 = (uint16_t)shtpData[currReportOffset + 9] << 8 | shtpData[currReportOffset + 8];
00560 
00561         uint8_t reportNum = shtpData[currReportOffset];
00562 
00563         if(reportNum != SENSOR_REPORTID_TIMESTAMP_REBASE) {
00564             // set status from byte 2
00565             reportStatus[reportNum] = static_cast<uint8_t>(shtpData[currReportOffset + 2] & 0b11);
00566 
00567             // set updated flag
00568             reportHasBeenUpdated[reportNum] = true;
00569         }
00570 
00571         switch(shtpData[currReportOffset]) {
00572             case SENSOR_REPORTID_TIMESTAMP_REBASE:
00573                 currReportOffset += SIZEOF_TIMESTAMP_REBASE;
00574                 break;
00575 
00576             case SENSOR_REPORTID_ACCELEROMETER:
00577 
00578                 totalAcceleration = TVector3 (
00579                                         qToFloat(data1, ACCELEROMETER_Q_POINT),
00580                                         qToFloat(data2, ACCELEROMETER_Q_POINT),
00581                                         qToFloat(data3, ACCELEROMETER_Q_POINT));
00582 
00583                 currReportOffset += SIZEOF_ACCELEROMETER;
00584                 break;
00585 
00586             case SENSOR_REPORTID_LINEAR_ACCELERATION:
00587 
00588                 linearAcceleration = TVector3 (
00589                                          qToFloat(data1, ACCELEROMETER_Q_POINT),
00590                                          qToFloat(data2, ACCELEROMETER_Q_POINT),
00591                                          qToFloat(data3, ACCELEROMETER_Q_POINT));
00592 
00593                 currReportOffset += SIZEOF_LINEAR_ACCELERATION;
00594                 break;
00595 
00596             case SENSOR_REPORTID_GRAVITY:
00597 
00598                 gravityAcceleration = TVector3 (
00599                                           qToFloat(data1, ACCELEROMETER_Q_POINT),
00600                                           qToFloat(data2, ACCELEROMETER_Q_POINT),
00601                                           qToFloat(data3, ACCELEROMETER_Q_POINT));
00602 
00603                 currReportOffset += SIZEOF_LINEAR_ACCELERATION;
00604                 break;
00605 
00606             case SENSOR_REPORTID_GYROSCOPE_CALIBRATED:
00607 
00608                 gyroRotation = TVector3 (
00609                                    qToFloat(data1, GYRO_Q_POINT),
00610                                    qToFloat(data2, GYRO_Q_POINT),
00611                                    qToFloat(data3, GYRO_Q_POINT));
00612 
00613                 currReportOffset += SIZEOF_GYROSCOPE_CALIBRATED;
00614                 break;
00615 
00616             case SENSOR_REPORTID_MAGNETIC_FIELD_CALIBRATED:
00617 
00618                 magField = TVector3 (
00619                                qToFloat(data1, MAGNETOMETER_Q_POINT),
00620                                qToFloat(data2, MAGNETOMETER_Q_POINT),
00621                                qToFloat(data3, MAGNETOMETER_Q_POINT));
00622 
00623                 currReportOffset += SIZEOF_MAGNETIC_FIELD_CALIBRATED;
00624                 break;
00625 
00626             case SENSOR_REPORTID_MAGNETIC_FIELD_UNCALIBRATED: {
00627                 magFieldUncalibrated = TVector3 (
00628                                            qToFloat(data1, MAGNETOMETER_Q_POINT),
00629                                            qToFloat(data2, MAGNETOMETER_Q_POINT),
00630                                            qToFloat(data3, MAGNETOMETER_Q_POINT));
00631 
00632                 uint16_t ironOffsetXQ = shtpData[currReportOffset + 11] << 8 | shtpData[currReportOffset + 10];
00633                 uint16_t ironOffsetYQ = shtpData[currReportOffset + 13] << 8 | shtpData[currReportOffset + 12];
00634                 uint16_t ironOffsetZQ = shtpData[currReportOffset + 15] << 8 | shtpData[currReportOffset + 14];
00635 
00636                 hardIronOffset = TVector3 (
00637                                      qToFloat(ironOffsetXQ, MAGNETOMETER_Q_POINT),
00638                                      qToFloat(ironOffsetYQ, MAGNETOMETER_Q_POINT),
00639                                      qToFloat(ironOffsetZQ, MAGNETOMETER_Q_POINT));
00640 
00641                 currReportOffset += SIZEOF_MAGNETIC_FIELD_UNCALIBRATED;
00642             }
00643             break;
00644 
00645             case SENSOR_REPORTID_ROTATION_VECTOR: {
00646                 uint16_t realPartQ = (uint16_t) shtpData[currReportOffset + 11] << 8 | shtpData[currReportOffset + 10];
00647                 uint16_t accuracyQ = (uint16_t) shtpData[currReportOffset + 13] << 8 | shtpData[currReportOffset + 12];
00648 
00649                 rotationVector = TVector4(
00650                                      qToFloat(data1, ROTATION_Q_POINT),
00651                                      qToFloat(data2, ROTATION_Q_POINT),
00652                                      qToFloat(data3, ROTATION_Q_POINT),
00653                                      qToFloat(realPartQ, ROTATION_Q_POINT));
00654 
00655                 rotationAccuracy = qToFloat(accuracyQ, ROTATION_ACCURACY_Q_POINT);
00656 
00657                 currReportOffset += SIZEOF_ROTATION_VECTOR;
00658             }
00659             break;
00660 
00661             case SENSOR_REPORTID_GAME_ROTATION_VECTOR: {
00662                 uint16_t realPartQ = (uint16_t) shtpData[currReportOffset + 11] << 8 | shtpData[currReportOffset + 10];
00663 
00664                 gameRotationVector = TVector4(
00665                                          qToFloat(data1, ROTATION_Q_POINT),
00666                                          qToFloat(data2, ROTATION_Q_POINT),
00667                                          qToFloat(data3, ROTATION_Q_POINT),
00668                                          qToFloat(realPartQ, ROTATION_Q_POINT));
00669 
00670                 currReportOffset += SIZEOF_GAME_ROTATION_VECTOR;
00671             }
00672             break;
00673 
00674             case SENSOR_REPORTID_GEOMAGNETIC_ROTATION_VECTOR: {
00675                 uint16_t realPartQ = (uint16_t) shtpData[currReportOffset + 11] << 8 | shtpData[currReportOffset + 10];
00676                 uint16_t accuracyQ = (uint16_t) shtpData[currReportOffset + 13] << 8 | shtpData[currReportOffset + 12];
00677 
00678                 geomagneticRotationVector = TVector4(
00679                                                 qToFloat(data1, ROTATION_Q_POINT),
00680                                                 qToFloat(data2, ROTATION_Q_POINT),
00681                                                 qToFloat(data3, ROTATION_Q_POINT),
00682                                                 qToFloat(realPartQ, ROTATION_Q_POINT));
00683 
00684                 geomagneticRotationAccuracy = qToFloat(accuracyQ, ROTATION_ACCURACY_Q_POINT);
00685 
00686                 currReportOffset += SIZEOF_GEOMAGNETIC_ROTATION_VECTOR;
00687             }
00688             break;
00689 
00690             case SENSOR_REPORTID_TAP_DETECTOR:
00691 
00692                 // since we got the report, a tap was detected
00693                 tapDetected = true;
00694 
00695                 doubleTap = (shtpData[currReportOffset + 4] & (1 << 6)) != 0;
00696 
00697                 currReportOffset += SIZEOF_TAP_DETECTOR;
00698                 break;
00699 
00700             case SENSOR_REPORTID_STABILITY_CLASSIFIER: {
00701                 uint8_t classificationNumber = shtpData[currReportOffset + 4];
00702 
00703                 if(classificationNumber > 4) {
00704                     classificationNumber = 0;
00705                 }
00706 
00707                 stability = static_cast<Stability>(classificationNumber);
00708 
00709                 currReportOffset += SIZEOF_STABILITY_REPORT;
00710             }
00711             break;
00712 
00713             case SENSOR_REPORTID_STEP_DETECTOR:
00714 
00715                 // the fact that we got the report means that a step was detected
00716                 stepDetected = true;
00717 
00718                 currReportOffset += SIZEOF_STEP_DETECTOR;
00719 
00720                 break;
00721 
00722             case SENSOR_REPORTID_STEP_COUNTER:
00723 
00724                 stepCount = shtpData[currReportOffset + 9] << 8 | shtpData[currReportOffset + 8];
00725 
00726                 currReportOffset += SIZEOF_STEP_COUNTER;
00727 
00728                 break;
00729 
00730             case SENSOR_REPORTID_SIGNIFICANT_MOTION:
00731 
00732                 // the fact that we got the report means that significant motion was detected
00733                 significantMotionDetected = true;
00734 
00735                 currReportOffset += SIZEOF_SIGNIFICANT_MOTION;
00736 
00737             case SENSOR_REPORTID_SHAKE_DETECTOR:
00738 
00739                 shakeDetected = true;
00740 
00741                 xAxisShake = (shtpData[currReportOffset + 4] & 1) != 0;
00742                 yAxisShake = (shtpData[currReportOffset + 4] & (1 << 1)) != 0;
00743                 zAxisShake = (shtpData[currReportOffset + 4] & (1 << 2)) != 0;
00744 
00745                 currReportOffset += SIZEOF_SHAKE_DETECTOR;
00746 
00747             default:
00748                 _debugPort->printf("Error: unrecognized report ID in sensor report: %hhx.  Byte %u, length %hu\n", shtpData[currReportOffset], currReportOffset, packetLength);
00749                 return;
00750         }
00751     }
00752 
00753 }
00754 
00755 bool BNO080::waitForPacket(int channel, uint8_t reportID, float timeout)
00756 {
00757     Timer timeoutTimer;
00758     timeoutTimer.start();
00759 
00760     while(timeoutTimer.read() <= 2*timeout) {
00761         if(_int.read() == 0) {
00762             if(!receivePacket(timeout)) {
00763                 return false;
00764             }
00765 
00766             if(channel == shtpHeader[2] && reportID == shtpData[0]) {
00767                 // found correct packet!
00768                 _debugPort->printf("\r\t found the correct packet \r\n");
00769                 return true;
00770             } else {
00771                 // other data packet, send to proper channels
00772                 _debugPort->printf("\r\t other data packets, sending to proper channel\r\n");
00773                 processPacket();
00774             }
00775         }
00776     }
00777 
00778     _debugPort->printf("Packet wait timeout.\n");
00779     return false;
00780 }
00781 
00782 //Given a register value and a Q point, convert to float
00783 //See https://en.wikipedia.org/wiki/Q_(number_format)
00784 float BNO080::qToFloat(int16_t fixedPointValue, uint8_t qPoint)
00785 {
00786     float qFloat = fixedPointValue;
00787     qFloat *= pow(2.0, qPoint * -1.0);
00788     return (qFloat);
00789 }
00790 
00791 float BNO080::qToFloat_dword(uint32_t fixedPointValue, int16_t qPoint)
00792 {
00793     float qFloat = fixedPointValue;
00794     qFloat *= pow(2.0, qPoint * -1.0);
00795     return (qFloat);
00796 }
00797 
00798 //Given a floating point value and a Q point, convert to Q
00799 //See https://en.wikipedia.org/wiki/Q_(number_format)
00800 int16_t BNO080::floatToQ(float qFloat, uint8_t qPoint)
00801 {
00802     int16_t qVal = static_cast<int16_t>(qFloat * pow(2.0, qPoint));
00803     return qVal;
00804 }
00805 
00806 //Tell the sensor to do a command
00807 //See 6.3.8 page 41, Command request
00808 //The caller is expected to set P0 through P8 prior to calling
00809 void BNO080::sendCommand(uint8_t command)
00810 {
00811     shtpData[0] = SHTP_REPORT_COMMAND_REQUEST; //Command Request
00812     shtpData[1] = commandSequenceNumber++; //Increments automatically each function call
00813     shtpData[2] = command; //Command
00814 
00815     //Caller must set these
00816     shtpData[3] = 0; //P0
00817         shtpData[4] = 0; //P1
00818         shtpData[5] = 0; //P2
00819         shtpData[6] = 0;
00820         shtpData[7] = 0;
00821         shtpData[8] = 0;
00822         shtpData[9] = 0;
00823         shtpData[10] = 0;
00824         shtpData[11] = 0;
00825 
00826     //Transmit packet on channel 2, 12 bytes
00827     sendPacket(CHANNEL_CONTROL, 12);
00828 }
00829 
00830 //Given a sensor's report ID, this tells the BNO080 to begin reporting the values
00831 //Also sets the specific config word. Useful for personal activity classifier
00832 void BNO080::setFeatureCommand(uint8_t reportID, uint16_t timeBetweenReports, uint32_t specificConfig)
00833 {
00834     uint32_t microsBetweenReports = static_cast<uint32_t>(timeBetweenReports * 1000);
00835 
00836     const uint32_t batchMicros = 0;
00837 
00838     shtpData[0] = SHTP_REPORT_SET_FEATURE_COMMAND; //Set feature command. Reference page 55
00839     shtpData[1] = reportID; //Feature Report ID. 0x01 = Accelerometer, 0x05 = Rotation vector
00840     shtpData[2] = 0; //Feature flags
00841     shtpData[3] = 0; //Change sensitivity (LSB)
00842     shtpData[4] = 0; //Change sensitivity (MSB)
00843     shtpData[5] = (microsBetweenReports >> 0) & 0xFF; //Report interval (LSB) in microseconds. 0x7A120 = 500ms
00844     shtpData[6] = (microsBetweenReports >> 8) & 0xFF; //Report interval
00845     shtpData[7] = (microsBetweenReports >> 16) & 0xFF; //Report interval
00846     shtpData[8] = (microsBetweenReports >> 24) & 0xFF; //Report interval (MSB)
00847     shtpData[9] = (batchMicros >> 0) & 0xFF;  //Batch Interval (LSB)
00848     shtpData[10] = (batchMicros >> 8) & 0xFF; //Batch Interval
00849     shtpData[11] = (batchMicros >> 16) & 0xFF;//Batch Interval
00850     shtpData[12] = (batchMicros >> 24) & 0xFF;//Batch Interval (MSB)
00851     shtpData[13] = (specificConfig >> 0) & 0xFF; //Sensor-specific config (LSB)
00852     shtpData[14] = (specificConfig >> 8) & 0xFF; //Sensor-specific config
00853     shtpData[15] = (specificConfig >> 16) & 0xFF; //Sensor-specific config
00854     shtpData[16] = (specificConfig >> 24) & 0xFF; //Sensor-specific config (MSB)
00855 
00856     //Transmit packet on channel 2, 17 bytes
00857     sendPacket(CHANNEL_CONTROL, 17);
00858 }
00859 
00860 bool BNO080::readFRSRecord(uint16_t recordID, uint32_t* readBuffer, uint16_t readLength)
00861 {
00862     // send initial read request
00863     zeroBuffer();
00864 
00865     shtpData[0] = SHTP_REPORT_FRS_READ_REQUEST;
00866     // read offset of 0 -> start at the start of the record
00867     shtpData[2] = 0;
00868     shtpData[3] = 0;
00869     // record ID
00870     shtpData[4] = static_cast<uint8_t>(recordID & 0xFF);
00871     shtpData[5] = static_cast<uint8_t>(recordID >> 8);
00872     // block size
00873     shtpData[6] = static_cast<uint8_t>(readLength & 0xFF);
00874     shtpData[7] = static_cast<uint8_t>(readLength >> 8);
00875 
00876     sendPacket(CHANNEL_CONTROL, 8);
00877 
00878     // now, read back the responses
00879     size_t readOffset = 0;
00880     while(readOffset < readLength) {
00881         if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_FRS_READ_RESPONSE)) {
00882 #if BNO_DEBUG
00883             _debugPort->printf("Error: did not receive FRS read response after sending read request!\n");
00884 #endif
00885             return false;
00886         }
00887 
00888         uint8_t status = static_cast<uint8_t>(shtpData[1] & 0b1111);
00889         uint8_t dataLength = shtpData[1] >> 4;
00890 
00891         // check status
00892         if(status == 1) {
00893 #if BNO_DEBUG
00894             _debugPort->printf("Error: FRS reports invalid record ID!\n");
00895 #endif
00896             return false;
00897         } else if(status == 2) {
00898 #if BNO_DEBUG
00899             _debugPort->printf("Error: FRS is busy!\n");
00900 #endif
00901             return false;
00902         } else if(status == 4) {
00903 #if BNO_DEBUG
00904             _debugPort->printf("Error: FRS reports offset is out of range!\n");
00905 #endif
00906             return false;
00907         } else if(status == 5) {
00908 #if BNO_DEBUG
00909             _debugPort->printf("Error: FRS reports record %hx is empty!\n", recordID);
00910 #endif
00911             return false;
00912         } else if(status == 8) {
00913 #if BNO_DEBUG
00914             _debugPort->printf("Error: FRS reports flash memory device unavailable!\n");
00915 #endif
00916             return false;
00917         }
00918 
00919         // check data length
00920         if(dataLength == 0) {
00921 #if BNO_DEBUG
00922             _debugPort->printf("Error: Received FRS packet with 0 data length!\n");
00923 #endif
00924             return false;
00925         } else if(dataLength == 1) {
00926             if(readOffset + 1 != readLength) {
00927 #if BNO_DEBUG
00928                 _debugPort->printf("Error: Received 1 length packet but more than 1 byte remains to be be read!\n");
00929 #endif
00930                 return false;
00931             }
00932         }
00933 
00934         // now, _finally_, read the dang words
00935         readBuffer[readOffset] = (shtpData[7] << 24) | (shtpData[6] << 16) | (shtpData[5] << 8) | (shtpData[4]);
00936 
00937         // check if we only wanted the first word
00938         ++readOffset;
00939         if(readOffset == readLength) {
00940             break;
00941         }
00942 
00943         readBuffer[readOffset] = (shtpData[11] << 24) | (shtpData[10] << 16) | (shtpData[9] << 8) | (shtpData[8]);
00944         readOffset++;
00945     }
00946 
00947     // read successful
00948     return true;
00949 
00950 }
00951 
00952 //Given the data packet, send the header then the data
00953 //Returns false if sensor does not ACK
00954 bool BNO080::sendPacket(uint8_t channelNumber, uint8_t dataLength)
00955 {
00956     
00957     uint16_t totalLength = dataLength + 4; //Add four bytes for the header
00958     packetLength = dataLength;
00959 
00960     shtpHeader[0] = totalLength & 0xFF;
00961     shtpHeader[1] = totalLength >> 8;
00962     shtpHeader[2] = channelNumber;
00963     shtpHeader[3] = sequenceNumber[channelNumber]++;
00964 #if BNO_DEBUG
00965 
00966     _debugPort->printf("Transmitting packet: ----------------\n");
00967     printPacket();
00968 #endif
00969     
00970     readBuffer[0] = shtpHeader[0];
00971     readBuffer[1] = shtpHeader[1];
00972     readBuffer[2] = shtpHeader[2];
00973     readBuffer[3] = shtpHeader[3];
00974     
00975     for(size_t index = 0; index < dataLength; ++index)
00976     {
00977         readBuffer[index + 4] = shtpData[index];
00978     }
00979     
00980     int writeRetval = _i2cPort.write(
00981         _i2cAddress << 1,
00982         reinterpret_cast<char*>(readBuffer),
00983         totalLength);
00984         
00985     if(writeRetval < 0) 
00986     {
00987         _debugPort->printf("BNO I2C body write failed!\n");
00988         return false;
00989     }
00990     
00991    
00992     
00993     return (true);
00994 }
00995 
00996 //Check to see if there is any new data available
00997 //Read the contents of the incoming packet into the shtpData array
00998 bool BNO080::receivePacket(float timeout)
00999 {
01000     Timer waitStartTime;
01001     waitStartTime.start();
01002 
01003     while(_int.read() != 0) {
01004         if(waitStartTime.read() > timeout) {
01005             _debugPort->printf("BNO I2C wait timeout\n");
01006             return false;
01007         }
01008     }
01009     
01010     const size_t headerLen = 4;
01011     uint8_t headerData[headerLen];
01012     int readRetval = _i2cPort.read(
01013         (_i2cAddress << 1) | 0x1,
01014         reinterpret_cast<char*>(headerData),
01015         headerLen);
01016         
01017     if(readRetval < 0) 
01018     {
01019         _debugPort->printf("BNO I2C header read failed!\n");
01020         return false;
01021     }
01022 
01023 
01024     //Get the first four bytes, aka the packet header
01025     uint8_t packetLSB = headerData[0];
01026     uint8_t packetMSB = headerData[1];
01027     uint8_t channelNumber = headerData[2];
01028     uint8_t sequenceNum = headerData[3]; //Not sure if we need to store this or not
01029 
01030     //Store the header info
01031     shtpHeader[0] = packetLSB;
01032     shtpHeader[1] = packetMSB;
01033     shtpHeader[2] = channelNumber;
01034     shtpHeader[3] = sequenceNum;
01035 
01036     if(shtpHeader[0] == 0xFF && shtpHeader[1] == 0xFF) {
01037         // invalid according to BNO080 datasheet section 1.4.1
01038 
01039         _debugPort->printf("Recieved 0xFFFF packet length, protocol error!\n");
01040         return false;
01041     }
01042 
01043     //Calculate the number of data bytes in this packet
01044     packetLength = (static_cast<uint16_t>(packetMSB) << 8 | packetLSB);
01045 
01046     // Clear the MSbit.
01047     // This bit indicates if this package is a continuation of the last. TBH, I don't really know what this means (it's not really explained in the datasheet)
01048     // but we don't actually care about any of the advertisement packets
01049     // that use this, so we can just cut off the rest of the packet by releasing chip select.
01050     packetLength &= ~(1 << 15);
01051 
01052     if (packetLength == 0) {
01053         // Packet is empty
01054         return (false); //All done
01055     }
01056     else if(packetLength > READ_BUFFER_SIZE)
01057     {
01058         return false; // read buffer too small
01059     }
01060 
01061     packetLength -= headerLen; //Remove the header bytes from the data count
01062     
01063     readRetval = _i2cPort.read(
01064         (_i2cAddress << 1) | 0x1,
01065         reinterpret_cast<char*>(readBuffer),
01066         packetLength + headerLen,
01067         false);
01068         
01069     if(readRetval < 0) 
01070     {
01071         _debugPort->printf("BNO I2C body read failed!\n");
01072         return false;
01073     }
01074 
01075     //Read incoming data into the shtpData array
01076     for (uint16_t dataSpot = 0 ; dataSpot < packetLength ; dataSpot++) {
01077 
01078         if (dataSpot < STORED_PACKET_SIZE) //BNO080 can respond with upto 270 bytes, avoid overflow
01079             shtpData[dataSpot] = readBuffer[dataSpot + headerLen]; //Store data into the shtpData array
01080     }
01081 
01082 #if BNO_DEBUG
01083     _debugPort->printf("Recieved packet: ----------------\n");
01084     printPacket(); // note: add 4 for the header length
01085 #endif
01086     return (true); //We're done!
01087 }
01088 
01089 //Pretty prints the contents of the current shtp header and data packets
01090 void BNO080::printPacket()
01091 {
01092 #if BNO_DEBUG
01093     //Print the four byte header
01094     _debugPort->printf("Header:");
01095     for (uint8_t x = 0 ; x < 4 ; x++) {
01096         _debugPort->printf(" ");
01097         if (shtpHeader[x] < 0x10) _debugPort->printf("0");
01098         _debugPort->printf("%hhx", shtpHeader[x]);
01099     }
01100 
01101     uint16_t printLength = packetLength;
01102     if (printLength > 40) printLength = 40; //Artificial limit. We don't want the phone book.
01103 
01104     _debugPort->printf(" Body:");
01105     for (uint16_t x = 0 ; x < printLength ; x++) {
01106         _debugPort->printf(" ");
01107         if (shtpData[x] < 0x10) _debugPort->printf("0");
01108         _debugPort->printf("%hhx", shtpData[x]);
01109     }
01110 
01111     _debugPort->printf(", Length:");
01112     _debugPort->printf("%hhu", packetLength + SHTP_HEADER_SIZE);
01113 
01114     if(shtpHeader[1] >> 7) {
01115         _debugPort->printf("[C]");
01116     }
01117 
01118     _debugPort->printf(", SeqNum: %hhu", shtpHeader[3]);
01119 
01120     _debugPort->printf(", Channel:");
01121     if (shtpHeader[2] == 0) _debugPort->printf("Command");
01122     else if (shtpHeader[2] == 1) _debugPort->printf("Executable");
01123     else if (shtpHeader[2] == 2) _debugPort->printf("Control");
01124     else if (shtpHeader[2] == 3) _debugPort->printf("Sensor-report");
01125     else if (shtpHeader[2] == 4) _debugPort->printf("Wake-report");
01126     else if (shtpHeader[2] == 5) _debugPort->printf("Gyro-vector");
01127     else _debugPort->printf("%hhu", shtpHeader[2]);
01128 
01129     _debugPort->printf("\n");
01130 #endif
01131 }
01132 
01133 
01134 void BNO080::zeroBuffer()
01135 {
01136     memset(shtpHeader, 0, SHTP_HEADER_SIZE);
01137     memset(shtpData, 0, STORED_PACKET_SIZE);
01138     packetLength = 0;
01139 }
01140 
01141 bool BNO080::loadReportMetadata(BNO080::Report report)
01142 {
01143     uint16_t reportMetaRecord;
01144     
01145     // first, convert the report into the correct FRS record ID for that report's metadata
01146     // data from SH-2 section 5.1
01147     switch(report) {
01148         case TOTAL_ACCELERATION:
01149             reportMetaRecord = 0xE301;
01150             break;
01151         case LINEAR_ACCELERATION:
01152             reportMetaRecord = 0xE303;
01153             break;
01154         case GRAVITY_ACCELERATION:
01155             reportMetaRecord = 0xE304;
01156             break;
01157         case GYROSCOPE:
01158             reportMetaRecord = 0xE306;
01159             break;
01160         case MAG_FIELD:
01161             reportMetaRecord = 0xE309;
01162             break;
01163         case MAG_FIELD_UNCALIBRATED:
01164             reportMetaRecord = 0xE30A;
01165             break;
01166         case ROTATION:
01167             reportMetaRecord = 0xE30B;
01168             break;
01169         case GEOMAGNETIC_ROTATION:
01170             reportMetaRecord = 0xE30D;
01171             break;
01172         case GAME_ROTATION:
01173             reportMetaRecord = 0xE30C;
01174             break;
01175         case TAP_DETECTOR:
01176             reportMetaRecord = 0xE313;
01177             break;
01178         case STABILITY_CLASSIFIER:
01179             reportMetaRecord = 0xE317;
01180             break;
01181         case STEP_DETECTOR:
01182             reportMetaRecord = 0xE314;
01183             break;
01184         case STEP_COUNTER:
01185             reportMetaRecord = 0xE315;
01186             break;
01187         case SIGNIFICANT_MOTION:
01188             reportMetaRecord = 0xE316;
01189             break;
01190         case SHAKE_DETECTOR:
01191             reportMetaRecord = 0xE318;
01192             break;
01193     }
01194 
01195     // if we already have that data stored, everything's OK
01196     if(bufferMetadataRecord == reportMetaRecord) {
01197         return true;
01198     }
01199 
01200     // now, load the metadata into the buffer
01201     if(!readFRSRecord(reportMetaRecord, metadataRecord, METADATA_BUFFER_LEN)) {
01202         // clear this so future calls won't try to use the cached version
01203         bufferMetadataRecord = 0;
01204 
01205         return false;
01206     }
01207 
01208     bufferMetadataRecord = reportMetaRecord;
01209 
01210     return true;
01211 }