STM32 I2C Library for BNO08x 9 axis IMU

STM32 I2C Library for BNO08x 9 axis IMU

Please read Liability Disclaimer and License Agreement CAREFULLY

Before going further please read BNO086 datasheet

In BNO_08x_I2C.h add the following code

#ifndef __BNO_08x_I2C_H
#define __BNO_08x_I2C_H

#ifdef __cplusplus
extern "C" {
#endif

#include "i2c.h"

// Used to eneble or disable functions needed for rough telescope position
// Uncomment lne below for fully featured driver

#define USE_FOR_TELESCOPE

// Enable disable extra features
//#define USE_ERROR_REPORT
//#define USE_COUNTER_REPORT
//#define USE_REORIENT //Tare

// For telescope use we need to be able to calibrate and change the orientation
#ifdef USE_FOR_TELESCOPE
	#define ACCELEROMETER									0X01
	#define GYROSCOPE_CALIBRATED					0X02
	#define MAGNETIC_FIELD_CALIBRATED			0X03
	#define ROTATION_VECTOR								0X05
	#define GAME_ROTATION_VECTOR					0X08
	#define USE_REORIENT // Used to set X to North, Y to East and Z down
#else
	//Features that can be used. Play with the conditions above to activate them
	#define RAW_ACCELEROMETER							0X14
	#define ACCELEROMETER									0X01
	#define LINEAR_ACCELERATION						0X04
	#define GRAVITY												0X06
	#define RAW_GYROSCOPE									0X15
	#define GYROSCOPE_CALIBRATED					0X02
	#define GYROSCOPE_UNCALIBRATED				0X07
	#define RAW_MAGNETOMETER							0X16
	#define MAGNETIC_FIELD_CALIBRATED			0X03
	#define MAGNETIC_FIELD_UNCALIBRATED		0X0F
	#define ROTATION_VECTOR								0X05
	#define GAME_ROTATION_VECTOR					0X08
	#define GEOMAGNETIC_ROTATION_VECTOR		0X09
	#define PRESSURE											0X0A
	#define AMBIENT_LIGHT									0X0B
	#define HUMIDITY											0X0C
	#define PROXIMITY											0X0D
	#define TEMPERATURE										0X0E
	#define RESERVED											0X17
	#define TAP_DETECTOR									0X10
	#define STEP_DETECTOR									0X18
	#define STEP_COUNTER									0X11
	#define SIGNIFICANT_MOTION						0X12
	#define STABILITY_CLASSIFIER					0X13
	#define SHAKE_DETECTOR								0X19
	#define FLIP_DETECTOR									0X1A
	#define PICKUP_DETECTOR								0X1B
	#define STABILITY_DETECTOR						0X1C
	#define PERSONAL_ACTIVITY_CLASSIFIER	0X1E
	#define SLEEP_DETECTOR								0X1F
	#define TILT_DETECTOR									0X20
	#define POCKET_DETECTOR								0X21
	#define CIRCLE_DETECTOR								0X22
	#define HEART_RATE_MONITOR						0X23
	#define ARVR_STABILIZED_RV						0X28
	#define ARVR_STABILIZED_GRV						0X29
	#define GYRO_INTEGRATED_RV						0X2A
	#define IZRO_MOTION_REQUEST						0X2B
	#define RAW_OPTICAL_FLOW							0X2C
	#define DEAD_RECKONING_POSE						0X2D
	#define WHEEL_ENCODER									0X2E
#endif

// Address
#define BNO_W_ADDR											0x96
#define BNO_R_ADDR											0x97
#define ADDR_BNO_DFU_W_ADDR							0x52
#define ADDR_BNO_DFU_R_ADDR							0x53

// Delays
#define RESET_DELAY											200
#define PORT_TIMEOUT										500
#define Q_FLIP												0.70710678118654752440084436210485
#define _180_DIV_PI											57.295779515f // = 180 / PI

// Array sizes
#define SEQUENCE_SIZE										6
#define HEADER_SIZE											4
#define RX_PACKET_SIZE									284 // For BNO086 276 for BNO080
#define TX_PACKET_SIZE									21
	
// Channels
#define CHANNEL_COMMAND									0
#define CHANNEL_EXECUTABLE							1
#define CHANNEL_CONTROL									2
#define CHANNEL_REPORTS									3
#define CHANNEL_WAKE_REPORTS						4
#define CHANNEL_GYRO																5

// Reports
#define REPORT_UNSOLICITED_RESPONSE			0x00
#define REPORT_UNSOLICITED_RESPONSE1		0x01

#define REPORT_SENSOR_FLUSH_REQUEST			0xF0
#define REPORT_SENSOR_FLUSH_RESPONSE		0xEF

#define REPORT_COMMAND_REQUEST					0xF2
#define REPORT_COMMAND_RESPONSE					0xF1

#define REPORT_FRS_READ_REQUEST					0xF4
#define REPORT_FRS_READ_RESPONSE				0xF3

#define REPORT_FRS_WRITE_REQUEST				0xF7
#define REPORT_FRS_WRITE_DATA_REQUEST		0xF6
#define REPORT_FRS_WRITE_RESPONSE				0xF5

#define REPORT_PRODUCT_ID_REQUEST				0xF9
#define REPORT_PRODUCT_ID_RESPONSE			0xF8


#define REPORT_TIMESTAMP_REBASE					0xFA
#define REPORT_BASE_TIMESTAMP_REF				0xFB

#define REPORT_GET_FEATURE_REQUEST			0xFE
#define REPORT_GET_FEATURE_RESPONSE			0xFC

#define REPORT_SET_FEATURE_COMMAND			0xFD

// Command and Subcommand values
#define COMMAND_ERRORS									1
#define COMMAND_COUNTER									2

#define COMMAND_TARE										3
#define COMMAND_TARE_NOW								0
#define COMMAND_TARE_PERSIST						1
#define COMMAND_TARE_REORIENT						2

#define COMMAND_INITIALIZE							4
#define COMMAND_INITIALIZE_RESET				1
#define COMMAND_INITIALIZE_ON						2
#define COMMAND_INITIALIZE_SLEEP				3

#define COMMAND_SAVE_DCD								6

#define COMMAND_ME_CALIBRATE						7
#define COMMAND_ME_CALIBRATE_CONFIG			0
#define COMMAND_ME_CALIBRATE_GET				1

#define COMMAND_DCD_PERIOD_SAVE					9

#define COMMAND_OSCILLATOR							0x0A
#define COMMAND_CLEAR_DCD_RESET					0x0B
#define COMMAND_TURNTABLE_CAL						0x0C

#define COMMAND_BOOTLOADER							0x0D
#define COMMAND_BOOTLOADER_MODE_REQ			0x00
#define COMMAND_BOOTLOADER_STATUS_REQ		0x01

#define COMMAND_INTERACTIVE_CAL_REQ			0x0E
#define COMMAND_WHEEL_REQ								0x0F

#define COMMAND_UNSOLICITED_INITIALIZE	0x84

#define CALIBRATE_ACCEL									0
#define CALIBRATE_GYRO									1
#define CALIBRATE_MAG										2
#define CALIBRATE_PLANAR_ACCEL					3
#define CALIBRATE_ON_TABLE							4
#define CALIBRATE_ACCEL_GYRO_MAG				5
#define CALIBRATE_ALL										6

#define CALIBRATE_STOP									5

// FRS Record Ids
#define STATIC_CALIBRATION_AGM                   0x7979
#define NOMINAL_CALIBRATION                      0x4D4D
#define STATIC_CALIBRATION_SRA                   0x8A8A
#define NOMINAL_CALIBRATION_SRA                  0x4E4E
#define DYNAMIC_CALIBRATION                      0x1F1F
#define ME_POWER_MGMT                            0xD3E2
#define SYSTEM_ORIENTATION                       0x2D3E
#define ACCEL_ORIENTATION                        0x2D41
#define SCREEN_ACCEL_ORIENTATION                 0x2D43
#define GYROSCOPE_ORIENTATION                    0x2D46
#define MAGNETOMETER_ORIENTATION                 0x2D4C
#define ARVR_STABILIZATION_RV                    0x3E2D
#define ARVR_STABILIZATION_GRV                   0x3E2E
#define TAP_DETECT_CONFIG                        0xC269
#define SIG_MOTION_DETECT_CONFIG                 0xC274
#define SHAKE_DETECT_CONFIG                      0x7D7D
#define MAX_FUSION_PERIOD                        0xD7D7
#define SERIAL_NUMBER                            0x4B4B
#define ES_PRESSURE_CAL                          0x39AF
#define ES_TEMPERATURE_CAL                       0x4D20
#define ES_HUMIDITY_CAL                          0x1AC9
#define ES_AMBIENT_LIGHT_CAL                     0x39B1
#define ES_PROXIMITY_CAL                         0x4DA2
#define ALS_CAL                                  0xD401
#define PROXIMITY_SENSOR_CAL                     0xD402
#define PICKUP_DETECTOR_CONFIG                   0x1B2A
#define FLIP_DETECTOR_CONFIG                     0xFC94
#define STABILITY_DETECTOR_CONFIG                0xED85
#define ACTIVITY_TRACKER_CONFIG                  0xED88
#define SLEEP_DETECTOR_CONFIG                    0xED87
#define TILT_DETECTOR_CONFIG                     0xED89
#define POCKET_DETECTOR_CONFIG                   0xEF27
#define CIRCLE_DETECTOR_CONFIG                   0xEE51
#define USER_RECORD                              0x74B4
#define ME_TIME_SOURCE_SELECT                    0xD403
#define UART_FORMAT                              0xA1A1
#define GYRO_INTEGRATED_RV_CONFIG                0xA1A2
#define DR_IMU_CONFIG                            0xDED2
#define DR_VEL_EST_CONFIG                        0xDED3
#define DR_SYNC_CONFIG                           0xDED4
#define DR_QUAL_CONFIG                           0xDED5
#define DR_CAL_CONFIG                            0xDED6
#define DR_LIGHT_REC_CONFIG                      0xDED8
#define DR_FUSION_CONFIG                         0xDED9
#define DR_OF_CONFIG                             0xDEDA
#define DR_WHEEL_CONFIG                          0xDEDB
#define DR_CAL                                   0xDEDC
#define DR_WHEEL_SELECT                          0xDEDF
#define FRS_ID_META_RAW_ACCELEROMETER            0xE301
#define FRS_ID_META_ACCELEROMETER                0xE302
#define FRS_ID_META_LINEAR_ACCELERATION          0xE303
#define FRS_ID_META_GRAVITY                      0xE304
#define FRS_ID_META_RAW_GYROSCOPE                0xE305
#define FRS_ID_META_GYROSCOPE_CALIBRATED         0xE306
#define FRS_ID_META_GYROSCOPE_UNCALIBRATED       0xE307
#define FRS_ID_META_RAW_MAGNETOMETER             0xE308
#define FRS_ID_META_MAGNETIC_FIELD_CALIBRATED    0xE309
#define FRS_ID_META_MAGNETIC_FIELD_UNCALIBRATED  0xE30A
#define FRS_ID_META_ROTATION_VECTOR              0xE30B
#define FRS_ID_META_GAME_ROTATION_VECTOR         0xE30C
#define FRS_ID_META_GEOMAGNETIC_ROTATION_VECTOR  0xE30D
#define FRS_ID_META_PRESSURE                     0xE30E
#define FRS_ID_META_AMBIENT_LIGHT                0xE30F
#define FRS_ID_META_HUMIDITY                     0xE310
#define FRS_ID_META_PROXIMITY                    0xE311
#define FRS_ID_META_TEMPERATURE                  0xE312
#define FRS_ID_META_TAP_DETECTOR                 0xE313
#define FRS_ID_META_STEP_DETECTOR                0xE314
#define FRS_ID_META_STEP_COUNTER                 0xE315
#define FRS_ID_META_SIGNIFICANT_MOTION           0xE316
#define FRS_ID_META_STABILITY_CLASSIFIER         0xE317
#define FRS_ID_META_SHAKE_DETECTOR               0xE318
#define FRS_ID_META_FLIP_DETECTOR                0xE319
#define FRS_ID_META_PICKUP_DETECTOR              0xE31A
#define FRS_ID_META_STABILITY_DETECTOR           0xE31B
#define FRS_ID_META_PERSONAL_ACTIVITY_CLASSIFIER 0xE31C
#define FRS_ID_META_SLEEP_DETECTOR               0xE31D
#define FRS_ID_META_TILT_DETECTOR                0xE31E
#define FRS_ID_META_POCKET_DETECTOR              0xE31F
#define FRS_ID_META_CIRCLE_DETECTOR              0xE320
#define FRS_ID_META_HEART_RATE_MONITOR           0xE321
#define FRS_ID_META_ARVR_STABILIZED_RV           0xE322
#define FRS_ID_META_ARVR_STABILIZED_GRV          0xE323
#define FRS_ID_META_GYRO_INTEGRATED_RV           0xE324
#define FRS_ID_META_RAW_OPTICAL_FLOW             0xE326
//Record IDs from SH-2 figure 28
//These are used to read and set various configuration options
#define FRS_RECORDID_SERIAL_NUMBER								0x4B4B
#define FRS_RECORDID_SYSTEM_ORIENTATION						0x2D3E

// FRS write status values
#define FRS_WRITE_STATUS_RECEIVED									0
#define FRS_WRITE_STATUS_UNRECOGNIZED_FRS_TYPE		1
#define FRS_WRITE_STATUS_BUSY											2
#define FRS_WRITE_STATUS_WRITE_COMPLETED					3
#define FRS_WRITE_STATUS_READY										4
#define FRS_WRITE_STATUS_FAILED										5
#define FRS_WRITE_STATUS_NOT_READY								6 // data received when not in write mode
#define FRS_WRITE_STATUS_INVALID_LENGTH						7
#define FRS_WRITE_STATUS_RECORD_VALID							8
#define FRS_WRITE_STATUS_INVALID_RECORD						9
#define FRS_WRITE_STATUS_DEVICE_ERROR							10
#define FRS_WRITE_STATUS_READ_ONLY								11

// Status values
#define FRS_READ_STATUS_NO_ERROR                        0
#define FRS_READ_STATUS_UNRECOGNIZED_FRS_TYPE           1
#define FRS_READ_STATUS_BUSY                            2
#define FRS_READ_STATUS_READ_RECORD_COMPLETED           3
#define FRS_READ_STATUS_OFFSET_OUT_OF_RANGE             4
#define FRS_READ_STATUS_RECORD_EMPTY                    5
#define FRS_READ_STATUS_READ_BLOCK_COMPLETED            6
#define FRS_READ_STATUS_READ_BLOCK_AND_RECORD_COMPLETED 7
#define FRS_READ_STATUS_DEVICE_ERROR                    8

// The below values are pre-calculated using SCALE_Q(n) = (1.0f / (1 << n))
#define SCALE_Q4			0.06250000000000000000
#define SCALE_Q7			0.00781250000000000000
#define SCALE_Q8			0.00390625000000000000
#define SCALE_Q9			0.00195312500000000000
#define SCALE_Q10			0.00097656250000000000
#define SCALE_Q12			0.00024414062500000000
#define SCALE_Q14			0.00006103515625000000
#define SCALE_Q17			0.00000762939453125000
#define SCALE_Q20			0.00000095367431640625
#define SCALE_Q25			0.00000002980232238770
#define SCALE_Q30			0.00000000093132257462
#define SCALE_TO_Q14	16384.0

// General Enums
typedef enum {
	NA = 0,
	PowerOnReset,
	InternalSystemReset,
	WatchdogTimeout,
	ExternalReset,
	Other,
} BNO_ResetCause_t;

typedef enum {
	InternalOscillator = 0,
	ExternalCrystal,
	ExternalClock,
	OscillatorError
} BNO_Oscillator_t;

typedef enum {
	ResetToBootloader = 0, // Reset to bootloader Mode
	UpgradeApp, //1 – Upgrade Application Mode; upgrade the application image in flash.
	ValidateImage, //2 – Validate Image Mode; validate an application image without updating the flash
	LauchApp, //3 – Launch Application; launch the application image in flash
	BootInvalid
} BNO_BootMode_t;

typedef enum {
	MOTION_INTENT_UNKNOWN = 0, // FME_MOBILE_MOTION_INTENT_UNKNOWN – this is the initial state assumed by the sensor hub
	INTENT_STATIONARY_WITHOUT_VIBRATION, //
	MOTION_INTENT_STATIONARY_WITH_VIBRATION, //
	MOTION_INTENT_IN_MOTION, //
	MOTION_INTENT_IN_MOTION_ACCELERATING
} BNO_MotionIntent_t;

// General structures

typedef struct __attribute__((packed)) {
	BNO_ResetCause_t	rst_Cause;	// report 0xF8 byte 1 Reset Cause
	uint8_t						sw_Maj;			// report 0xF8 byte 2 SW Version Major
	uint8_t						sw_Min;			// report 0xF8 byte 3 SW Version Minor
	uint32_t					sw_PN;			// report 0xF8 byte 4-7 SW Part Number
	uint32_t					sw_BN;			// report 0xF8 byte 8-11 SW Build Number
	uint16_t					sw_VP;			// report 0xF8 byte 12-13 SW Version Patch
} BNO_productID_t;

typedef struct __attribute__((packed)) {
	uint8_t status;
	uint16_t wordOffset;
} BNO_FrsWriteResp_t;

typedef struct __attribute__((packed)) {
	uint8_t len_status;
	uint16_t wordOffset;
	uint32_t data0;
	uint32_t data1;
	uint16_t frsType;
} BNO_FrsReadResp_t;

#define RESPONSE_VALUES	11
typedef struct __attribute__((packed)) {
	uint8_t seq;
	uint8_t command;
	uint8_t commandSeq;
	uint8_t respSeq;
	uint8_t r[RESPONSE_VALUES];
} BNO_CommandResp_t;

#define COMMAND_PARAMS	9
typedef struct __attribute__((packed)) {
    uint8_t reportId;
    uint8_t seq;
    uint8_t command;
    uint8_t p[COMMAND_PARAMS];
} BNO_CommandReq_t;

#ifdef USE_ERROR_REPORT
typedef struct __attribute__((packed)) {
	uint8_t	Severity;	// Severity – the severity of the error currently being reported
	uint8_t	SeqNo;		// Error sequence number. A monotonically incrementing uin8_t that counts all the errors generated for the reported severity. It may rollover.
	uint8_t	Source;		// Error source. 0 – reserved, 1 – MotionEngine, 2 – MotionHub, 3 – SensorHub, 4 – Chip level executable, 5-254 reserved. 255 – no error to report.
	uint8_t	Error;		// Error. See library API
	uint8_t	Module;		// Error module. See library API
	uint8_t	Code;			// Error code. See library API
} BNO_Error_t;
#endif

#ifdef USE_COUNTER_REPORT
typedef struct __attribute__((packed)) {
	uint32_t offered;   // [events]
	uint32_t accepted;  // [events]
	uint32_t on;        // [events]
	uint32_t attempted;	// [events]
} BNO_Counts_t;
#endif

#ifdef USE_REORIENT
typedef enum {
	TARE_Z = 4,
	TARE_ALL = 7,
} BNO_TareAxis_t;

typedef enum {
	RotationVector = 0,
	GamingRotationVector = 1,
	GeomagneticRotationVector = 2,
	GyroIntegratedRotationVector = 3,
	ArVrStabilizedRotationVector = 4,
	ArVrStabilizedGameRotationVector =5,
} BNO_TareRV_t;

#endif
typedef struct __attribute__((packed)) {
	uint8_t	Status;					// Status (0 – success. Non Zero – Operation failed)
	uint8_t	AccCalEnable;		// Accel Cal Enable (1 – enabled, 0 – disabled)
	uint8_t	GyroCalEnable;	// Gyro Cal Enable (1 – enabled, 0 – disabled)
	uint8_t	MagCalEnable;		// Mag Cal Enable (1 – enabled, 0 – disabled)
	uint8_t	PlanCalEnable;	// Planar Accel Cal Enable (1 – enabled, 0 – disabled)
	uint8_t	OnTableEnable;	// On Table Cal Enable (1 – enabled, 0 – disabled)
} BNO_calibrationStat_t;

typedef struct __attribute__((packed)) {
	uint8_t OperationMode;
	uint8_t reserved;
	uint32_t Status;
	uint32_t Errors;
} BNO_Boot_t;

typedef struct __attribute__((packed)) {
	uint8_t sensorID; // sensor id
	uint8_t flags; // FEAT_... values
	uint16_t changeSensitivity;
	uint32_t reportInterval_uS;
	uint32_t batchInterval_uS;
	uint32_t sensorSpecific;
} BNO_Feature_t;

// Sensor values 

typedef struct __attribute__((packed)) {
	float Roll;
	float Pitch;
	float Yaw;
} BNO_RollPitchYaw_t;

#ifdef RAW_ACCELEROMETER
// Raw Accelerometer
typedef struct __attribute__((packed)) {
	// Units are ADC counts
	int16_t X; // [ADC counts]
	int16_t Y; // [ADC counts]
	int16_t Z; // [ADC counts]
	uint32_t TimeStamp; // [uS]
} BNO_RawAccelerometer_t;
#endif

#if defined(ACCELEROMETER) || defined(LINEAR_ACCELERATION) || defined(GRAVITY)
// Accelerometer
typedef struct __attribute__((packed)) {
	float X;
	float Y;
	float Z;
} BNO_Accelerometer_t;
#endif

#ifdef RAW_GYROSCOPE
// Raw Gyroscope
typedef struct __attribute__((packed)) {
	// Units are ADC counts
	int16_t X; // [ADC Counts]
	int16_t Y; // [ADC Counts]
	int16_t Z; // [ADC Counts]
	int16_t Temperature; // [ADC Counts]
	uint32_t TimeStamp; // [uS]
} BNO_RawGyroscope_t;
#endif

#ifdef GYROSCOPE_CALIBRATED
// Gyroscope
typedef struct __attribute__((packed)) {
	// Units are rad/s
	float X;
	float Y;
	float Z;
} BNO_Gyroscope_t;
#endif

#ifdef GYROSCOPE_UNCALIBRATED
// Uncalibrated Gyroscope
typedef struct __attribute__((packed)) {
	// Units are rad/s
	float X; // [rad/s]
	float Y; // [rad/s]
	float Z; // [rad/s]
	float BiasX; // [rad/s]
	float BiasY; // [rad/s]
	float BiasZ; // [rad/s]
} BNO_GyroscopeUncalibrated_t;
#endif

#ifdef RAW_MAGNETOMETER
// Raw Magnetometer
typedef struct __attribute__((packed)) {
	// Units are ADC counts
	int16_t X; // [ADC Counts]
	int16_t Y; // [ADC Counts]
	int16_t Z; // [ADC Counts]
	uint32_t TimeStamp; // [uS]
} BNO_RawMagnetometer_t;
#endif

#ifdef MAGNETIC_FIELD_CALIBRATED
// Magnetic Field
typedef struct __attribute__((packed)) {
	// Units are uTesla
	float X; // [uTesla]
	float Y; // [uTesla]
	float Z; // [uTesla]
} BNO_MagneticField_t;
#endif

#ifdef MAGNETIC_FIELD_UNCALIBRATED
// Uncalibrated Magnetic Field
typedef struct __attribute__((packed)) {
	// Units are uTesla
	float X; // [uTesla]
	float Y; // [uTesla]
	float Z; // [uTesla]
	float BiasX; // [uTesla]
	float BiasY; // [uTesla]
	float BiasZ; // [uTesla]
} BNO_MagneticFieldUncalibrated_t;
#endif

#if defined(ROTATION_VECTOR) || defined(GEOMAGNETIC_ROTATION_VECTOR) || defined(ARVR_STABILIZED_RV)
// Rotation Vector with Accuracy
typedef struct __attribute__((packed)) {
	float I; // Quaternion component i
	float J; // Quaternion component j
	float K; // Quaternion component k
	float Real; // Quaternion component, real
	float Accuracy; // Accuracy estimate [radians]
} BNO_RotationVectorWAcc_t;
#endif

#if defined(GAME_ROTATION_VECTOR) || defined(ARVR_STABILIZED_GRV)
// Rotation Vector
typedef struct __attribute__((packed)) {
	float I; // Quaternion component i
	float J; // Quaternion component j
	float K; // Quaternion component k
	float Real; // Quaternion component real
} BNO_RotationVector_t;
#endif

#ifdef TAP_DETECTOR
typedef enum {
	TAPDET_X			= 1,
	TAPDET_X_POS	= 2,
	TAPDET_Y			= 4,
	TAPDET_Y_POS	= 8,
	TAPDET_Z			= 16,
	TAPDET_Z_POS	= 32,
	TAPDET_DOUBLE = 64,
} BNO_Tap_t;

#endif

#ifdef STEP_COUNTER
typedef struct __attribute__((packed)) {
    uint32_t Latency; // Step counter latency [uS].
    uint16_t Steps; // Steps counted.
} BNO_StepCounter_t;
#endif

#ifdef STABILITY_CLASSIFIER
typedef enum {
	STABILITY_CLASSIFIER_UNKNOWN = 0,
	STABILITY_CLASSIFIER_ON_TABLE,
	STABILITY_CLASSIFIER_STATIONARY,
	STABILITY_CLASSIFIER_STABLE,
	STABILITY_CLASSIFIER_MOTION,
} BNO_Stability_t;
#endif

#ifdef SHAKE_DETECTOR
typedef enum {
	SHAKE_X = 1,
	SHAKE_Y = 2,
	SHAKE_Z = 4,
} BNO_Shake_t;
#endif

#ifdef PICKUP_DETECTOR
typedef enum {
	PICKUP_LEVEL_TO_NOT_LEVEL = 1,
	PICKUP_STOP_WITHIN_REGION = 2,
} BNO_Pickup_t;
#endif

#ifdef STABILITY_DETECTOR
typedef enum {
	STABILITY_ENTERED = 1,
	STABILITY_EXITED	= 2,
} BNO_StabilityDetector_t;
#endif

#ifdef PERSONAL_ACTIVITY_CLASSIFIER
typedef enum {
	PAC_UNKNOWN = 0,
	PAC_IN_VEHICLE,
	PAC_ON_BICYCLE,
	PAC_ON_FOOT,
	PAC_STILL,
	PAC_TILTING,
	PAC_WALKING,
	PAC_RUNNING,
} BNO_PAC_t;
// Personal Activity Classifier
typedef struct __attribute__((packed)) {
	uint8_t Page;
	uint8_t LastPage;
	BNO_PAC_t MostLikelyState;
	uint8_t Confidence[10];
} BNO_PersonalActivityClassifier_t;
#endif

#ifdef GYRO_INTEGRATED_RV
// Gyro Integrated Rotation Vector
typedef struct __attribute__((packed)) {
	float I;   // Quaternion component i
	float J;   // Quaternion component j
	float K;   // Quaternion component k
	float Real;// Quaternion component real
	float AngleVelX; // Angular velocity about x [rad/s]
	float AngleVelY; // Angular velocity about y [rad/s]
	float AngleVelZ; // Angular velocity about z [rad/s]
} BNO_GyroIntegratedRV_t;
#endif

#ifdef IZRO_MOTION_REQUEST
// Interactive ZRO Motion Intent. See the SH-2 Reference Manual, 6.4.13
typedef enum {
	IZRO_MI_UNKNOWN = 0,
	IZRO_MI_STATIONARY_NO_VIBRATION,
	IZRO_MI_STATIONARY_WITH_VIBRATION,
	IZRO_MI_IN_MOTION,
	IZRO_MI_ACCELERATING,
} BNO_IZroMotionIntent_t;

typedef enum {
	IZRO_MR_NO_REQUEST = 0,
	IZRO_MR_STAY_STATIONARY,
	IZRO_MR_STATIONARY_NON_URGENT,
	IZRO_MR_STATIONARY_URGENT,
} BNO_IZroMotionRequest_t;

typedef struct __attribute__((packed)) {
	BNO_IZroMotionIntent_t Intent;
	BNO_IZroMotionRequest_t Request;
} BNO_IZroRequest_t;
#endif

#ifdef RAW_OPTICAL_FLOW
typedef struct __attribute__((packed)) {
	uint32_t TimeStamp;
	int16_t Dt;
	int16_t Dx;
	int16_t Dy;
	int16_t Iq;
	uint8_t ResX;
	uint8_t ResY;
	uint8_t Shutter;
	uint8_t FrameMax;
	uint8_t FrameAvg;
	uint8_t FrameMin;
	uint8_t LaserOn;
} BNO_RawOptFlow_t;
#endif

#ifdef DEAD_RECKONING_POSE
typedef struct __attribute__((packed)) {
	uint32_t TimeStamp;
	float LinPosX;
	float LinPosY;
	float LinPosZ;
	float I;
	float J;
	float K;
	float Real;
	float LinVelX;
	float LinVelY;
	float LinVelZ;
	float AngleVelX;
	float AngleVelY;
	float AngleVelZ;
} BNO_DeadReckoningPose_t;
#endif

#ifdef WHEEL_ENCODER
typedef struct __attribute__((packed)) {
	uint32_t TimeStamp;
	uint8_t WheelIndex;
	uint8_t DataType;
	uint16_t Data;
} BNO_WheelEncoder_t;
#endif

typedef struct __attribute__((packed)) {
	uint8_t sensorId; // Which sensor produced this event.
	uint8_t sequence; // The sequence number increments once for each report sent. Gaps in the sequence numbers indicate missing or dropped reports.
	uint8_t status; // bits 7-5: reserved, 4-2: exponent delay, 1-0: Accuracy 0 - Unreliable 1 - Accuracy low 2 - Accuracy medium 3 - Accuracy high
	uint64_t timestamp;  // [uS]
	uint32_t delay; /// [uS] value is delay * 2^exponent (see status)
	union {
		#ifdef RAW_ACCELEROMETER
			BNO_RawAccelerometer_t RawAccelerometer;
		#endif
		#ifdef ACCELEROMETER
			BNO_Accelerometer_t Accelerometer;
		#endif
		#ifdef LINEAR_ACCELERATION
			BNO_Accelerometer_t LinearAcceleration;
		#endif
		#ifdef GRAVITY
			BNO_Accelerometer_t Gravity;
		#endif
		#ifdef RAW_GYROSCOPE
			BNO_RawGyroscope_t RawGyroscope;
		#endif
		#ifdef GYROSCOPE_CALIBRATED
			BNO_Gyroscope_t Gyroscope;
		#endif
		#ifdef GYROSCOPE_UNCALIBRATED
			BNO_GyroscopeUncalibrated_t GyroscopeUncal;
		#endif
		#ifdef RAW_MAGNETOMETER
			BNO_RawMagnetometer_t RawMagnetometer;
		#endif
		#ifdef MAGNETIC_FIELD_CALIBRATED
			BNO_MagneticField_t MagneticField;
		#endif
		#ifdef MAGNETIC_FIELD_UNCALIBRATED
			BNO_MagneticFieldUncalibrated_t MagneticFieldUncal;
		#endif
		#ifdef ROTATION_VECTOR
			BNO_RotationVectorWAcc_t RotationVector;
		#endif
		#ifdef GAME_ROTATION_VECTOR
			BNO_RotationVector_t GameRotationVector;
		#endif
		#ifdef GEOMAGNETIC_ROTATION_VECTOR
			BNO_RotationVectorWAcc_t GeoMagRotationVector;
		#endif
		#ifdef PRESSURE
			float Pressure; // Atmospheric Pressure.  [hectopascals]
		#endif
		#ifdef AMBIENT_LIGHT
			float AmbientLight; // Ambient Light.  [lux]
		#endif
		#ifdef HUMIDITY
			float Humidity; // Relative Humidity.  [percent]
		#endif
		#ifdef PROXIMITY
			float Proximity; // Proximity.  [cm]
		#endif
		#ifdef TEMPERATURE
			float Temperature; // Temperature.  [C]
		#endif
		#ifdef RESERVED
			float Reserved;  // Reserved
		#endif
		#ifdef TAP_DETECTOR
			BNO_Tap_t TapDetectorFlag;
		#endif
		#ifdef STEP_DETECTOR
			uint32_t StepDetectorLatency; // Step detect latency [uS]
		#endif
		#ifdef STEP_COUNTER
			BNO_StepCounter_t StepCounter;
		#endif
		#ifdef SIGNIFICANT_MOTION
			uint16_t SignificantMotion;
		#endif
		#ifdef STABILITY_CLASSIFIER
			BNO_Stability_t StabilityClassifier;
		#endif
		#ifdef SHAKE_DETECTOR
			BNO_Shake_t ShakeDetector;
		#endif
		#ifdef FLIP_DETECTOR
			uint16_t FlipDetector;
		#endif
		#ifdef PICKUP_DETECTOR
			BNO_Pickup_t PickupDetector;
		#endif
		#ifdef STABILITY_DETECTOR
			BNO_StabilityDetector_t StabilityDetector;
		#endif
		#ifdef PERSONAL_ACTIVITY_CLASSIFIER
			BNO_PersonalActivityClassifier_t PersonalActivityClassifier;
		#endif
		#ifdef SLEEP_DETECTOR
			uint8_t SleepDetector;
		#endif
		#ifdef TILT_DETECTOR
			uint16_t TiltDetector;
		#endif
		#ifdef POCKET_DETECTOR
			uint16_t PocketDetector;
		#endif
		#ifdef CIRCLE_DETECTOR
			uint16_t CircleDetector;
		#endif
		#ifdef HEART_RATE_MONITOR
			uint16_t HeartRateMonitor; // Heart rate in beats per minute.
		#endif
		#ifdef ARVR_STABILIZED_RV
			BNO_RotationVectorWAcc_t ArVrStabilizedRV;
		#endif
		#ifdef ARVR_STABILIZED_GRV
			BNO_RotationVector_t ArVrStabilizedGRV;
		#endif
		#ifdef GYRO_INTEGRATED_RV
			BNO_GyroIntegratedRV_t GyroIntegratedRV;
		#endif
		#ifdef IZRO_MOTION_REQUEST
			BNO_IZroRequest_t IzroRequest;
		#endif
		#ifdef RAW_OPTICAL_FLOW
			BNO_RawOptFlow_t RawOptFlow;
		#endif
		#ifdef DEAD_RECKONING_POSE
			BNO_DeadReckoningPose_t DeadReckoningPose;
		#endif
		#ifdef WHEEL_ENCODER
			BNO_WheelEncoder_t WheelEncoder;
		#endif
	} SenVal;
} BNO_SensorValue_t;

extern BNO_SensorValue_t sensorData;
extern BNO_RollPitchYaw_t rpy;
// Initialize the sesnor
// During reset or power-on sequence, the bootloader first checks the status of the BOOTN pin.
// If the pin is pulled low during reset or poweron, the BNO08X will enter the bootloader mode.
// If the BOOTN pin is pulled high, then the bootloader starts the application
HAL_StatusTypeDef BNO_Init(void);
HAL_StatusTypeDef BNO_setHighAccuracyMode(void);
// Check if we have unexpected reset
uint8_t isResetOccurred(void);
// Get the sensor that has new data
uint8_t BNO_getSensorEventID(void);
// Soft reset the sensor
uint8_t BNO_Reset(void);
// Turn sensor ON
HAL_StatusTypeDef BNO_On(void);
// When sleep command is issued all sensors that are configured as always on or wake (see 1.3.5.1) will continue to operate all, other sensors will be disabled
HAL_StatusTypeDef BNO_Sleep(void);
// Returns the product id data structure
BNO_productID_t BNO_getProductID(void);
HAL_StatusTypeDef BNO_writeFRS(const uint16_t length, const uint16_t frsType);
HAL_StatusTypeDef BNO_readFRS(const uint16_t frsType);
#ifdef USE_ERROR_REPORT
// See 6.4.1 Report Errors (0x01)
HAL_StatusTypeDef BNO_getErrors(void);
#endif

#ifdef USE_COUNTER_REPORT
// See 6.4.2 Counter Commands (0x02)
HAL_StatusTypeDef BNO_getCounter(const uint8_t sensorID);
HAL_StatusTypeDef BNO_clearCounter(const uint8_t sensorID);
#endif

#ifdef USE_REORIENT
// See 6.4.3.1 Tare Now (0x00)
HAL_StatusTypeDef BNO_TareNow(const BNO_TareAxis_t axis, const BNO_TareRV_t vector);
//This command instructs SH-2 to persist the results of the last tare operation to flash for use at the next system restart.
HAL_StatusTypeDef BNO_TarePerist(void);
// This command instructs SH-2 to set the current run-time sensor reorientation.
// The rotation vector is a signed, 16-bit 2’s-complement fixed point number with a Q-point of 14.
HAL_StatusTypeDef BNO_TareSetReorientation(const double X, const double Y, const double Z, const double W);
// Clears the previously applied tare operation.
HAL_StatusTypeDef BNO_ClearTare(void);
#endif

// !!! ToDo check why is not responding
// The sensor hub responds to the Initialize command with an Initialize Response. In the case
// where the sensor hub reinitializes itself, this response is unsolicited. An unsolicited response is
// also generated after startup
HAL_StatusTypeDef BNO_Initialize(void);
// Save Dynamic Calibration Data (DCD) to flash
HAL_StatusTypeDef BNO_saveCalibration(void);
// This command is sent by the host to configure the ME calibration of the accelerometer, gyro and
// magnetometer giving the host the ability to control when calibration is performed.
HAL_StatusTypeDef BNO_condigureCalibration(const uint8_t sensors);
// This command is sent by the host to request the enable/disable state of the accelerometer, gyro and magnetometer calibration routines
HAL_StatusTypeDef BNO_enableCalibration(void);
// The Configure Periodic DCD Save command configures the automatic saving of DCD. There is
// no response to this command. This command does not inhibit the Save DCD command.
// 0 Enable, 1 Disable
HAL_StatusTypeDef BNO_configurePeriodicDcdSave(const uint8_t enableStatus);
// The Get Oscillator Type command is used to get information about the oscillator type used in the clock system of the SH-2
BNO_Oscillator_t BNO_getOscillatorType(void);
// This command performs an atomic clearDCD (from RAM) and system reset
HAL_StatusTypeDef BNO_clearDcdReset(void);
// See 6.4.10 Simple Calibration Commands (0x0C)
HAL_StatusTypeDef BNO_simpleCalibration(const uint32_t usInterval, const uint16_t calibrationTimeMs);
// !!! ToDo check why is not responding
// The bootloader operating mode request is used to request various operating modes of the FSP200 bootloader
HAL_StatusTypeDef BNO_setBootMode(const BNO_BootMode_t mode);
// !!! ToDo check why is not responding
// Request product ID information about the FSP200 bootloader
BNO_BootMode_t BNO_getBootMode(void);
// The interactive calibration feature requires that the sensor hub be told of the device’s intended motion.
HAL_StatusTypeDef BNO_interactiveCalibration(const BNO_MotionIntent_t intent);
#ifdef WHEEL_ENCODER
// Provide a single sample of wheel encoder data. No response is sent for this command.
HAL_StatusTypeDef BNO_WheelRequest(const uint8_t wheelIndex, const uint32_t timeStampUs, const int16_t wheelData, const uint8_t dataType);
#endif

// This is sent from the host to the hub to trigger a flush of outstanding data from a given sensor
HAL_StatusTypeDef BNO_forceFlush(const uint8_t sensorID);

#ifdef USE_FOR_TELESCOPE
// Sets -X North, Y East Z down
HAL_StatusTypeDef setTelescopeOrientation(void);
// This command is sent by the host to request the enable/disable state of the accelerometer, gyro
// and magnetometer calibration routines.
HAL_StatusTypeDef BNO_getCalibrationStatus(void);

HAL_StatusTypeDef BNO_isCalibrationComplete(void);

HAL_StatusTypeDef BNO_calibrateHighAccuracyAndReset(void);
#endif

HAL_StatusTypeDef BNO_dataAvailable(void);
// Enable features an set report time in mili seconds
BNO_Feature_t BNO_getFeature(const uint8_t sensorID);
HAL_StatusTypeDef BNO_setFeature(const uint8_t reportID, const uint32_t microsBetweenReports, const uint32_t specificConfig);	
	
BNO_RollPitchYaw_t BNO_getRollPitchYaw(void);


#ifdef RAW_ACCELEROMETER
// Raw uncalibrated accelerometer data (ADC units), used for testing
BNO_RawAccelerometer_t getRawAccelerometer(void);
#endif
#ifdef ACCELEROMETER
// Calibrated Acceleration (m/s2). Acceleration of the device including gravity
BNO_Accelerometer_t getaccelerometer(void);
#endif
#ifdef LINEAR_ACCELERATION
// Linear acceleration (m/s2). Acceleration of the device with gravity removed
BNO_Accelerometer_t getLinearAcceleration(void);
#endif
#ifdef GRAVITY
// Gravity (m/s2)
BNO_Accelerometer_t getGravity(void);
#endif
#ifdef RAW_GYROSCOPE
// Raw uncalibrated gyroscope (ADC units). Data direct from the gyroscope, used for testing
BNO_RawGyroscope_t getRawGyroscope(void);
#endif
#ifdef GYROSCOPE_CALIBRATED
// Calibrated gyroscope (rad/s). The angular velocity of the device
BNO_Gyroscope_t getGyroscope(void);
#endif
#ifdef GYROSCOPE_UNCALIBRATED
// Uncalibrated gyroscope (rad/s). Angular velocity without bias compensation.
BNO_GyroscopeUncalibrated_t getGyroscopeUncal(void);
#endif
#ifdef RAW_MAGNETOMETER
// Raw magnetic field measurement (in ADC units). Direct data from the magnetometer, used for testing
BNO_RawMagnetometer_t getRawMagnetometer(void);
#endif
#ifdef MAGNETIC_FIELD_CALIBRATED
// Magnetic field calibrated (in µTesla). The fully calibrated magnetic field measurement.
BNO_MagneticField_t getMagneticField(void);
#endif
#ifdef MAGNETIC_FIELD_UNCALIBRATED
// Magnetic field uncalibrated (in µTesla). The magnetic field measurement without hard-iron offset applied
BNO_MagneticFieldUncalibrated_t getMagneticFieldUncal(void);
#endif
#ifdef ROTATION_VECTOR
// THE ROTATION VECTOR PROVIDES an orientation output that is expressed as a quaternion referenced to magnetic north
// and gravity. It is produced by fusing the outputs of the accelerometer, gyroscope and magnetometer. The rotation
// vector is the most accurate orientation estimate available. The magnetometer provides correction in yaw to
// reduce drift and the gyroscope enables the most responsive performance.
BNO_RotationVectorWAcc_t getRotationVector(void);
#endif
#ifdef GAME_ROTATION_VECTOR
// THE GAME ROTATION VECTOR is an orientation output that is expressed as a quaternion with no specific reference for
// heading, while roll and pitch are referenced against gravity. It is produced by fusing the outputs of the
// accelerometer and the gyroscope (i.e. no magnetometer). The game rotation vector does not use the
// magnetometer to correct the gyroscopes drift in yaw. This is a deliberate omission (as specified by Google) to
// allow gaming applications to use a smoother representation of the orientation without the jumps that an
// instantaneous correction provided by a magnetic field update could provide.
BNO_RotationVector_t getGameRotationVector(void);
#endif
#ifdef GEOMAGNETIC_ROTATION_VECTOR
// THE GEOMAGNETIC ROTATION VECTOR is an orientation output that is expressed as a quaternion referenced to magnetic
// north and gravity. It is produced by fusing the outputs of the accelerometer and magnetometer. The gyroscope is
// specifically excluded in order to produce a rotation vector output using less power than is required to produce the
// rotation vector of section 2.2.4. The consequences of removing the gyroscope are:
// • Less responsive output since the highly dynamic outputs of the gyroscope are not used
// • More errors in the presence of varying magnetic fields
BNO_RotationVectorWAcc_t getGeoMagRotationVector(void);
#endif
#ifdef PRESSURE
// The pressures sensor reports atmospheric pressure. The units are hectopascals. 
float getPressure(void);
#endif
#ifdef AMBIENT_LIGHT
// The ambient light sensor reports the measures the amount of light entering the sensor. The units are lux. 
float getAmbientLight(void);
#endif
#ifdef HUMIDITY
// The humidity sensor reports relative humidity in the ambient air. The units are percent.
float getHumidity(void);
#endif
#ifdef PROXIMITY
// The proximity sensor reports distance from the device. The units are centimeters.
float getProximity(void);
#endif
#ifdef TEMPERATURE
// The temperature sensor reports ambient temperature. The units are °C. 
float getTemperature(void);
#endif
#ifdef RESERVED
// 
float getReserved(void);
#endif
#ifdef TAP_DETECTOR
// The tap detector reports single and double taps. 
BNO_Tap_t getTapDetectorFlag(void);
#endif
#ifdef STEP_DETECTOR
// The step detector reports steps detected. Each report indicates a single step.
uint32_t getStepDetectorLatency(void);
#endif
#ifdef STEP_COUNTER
// The step counter reports steps counted. The units are steps.
BNO_StepCounter_t getStepCounter(void);
#endif
#ifdef SIGNIFICANT_MOTION
// The significant motion detector sends a single report when it detects significant motion. It
// automatically turns itself off after sending its single report. 
uint16_t getSignificantMotion(void);
#endif
#ifdef STABILITY_CLASSIFIER
// The stability classifier sensor reports the type of stability detected.
BNO_Stability_t getStabilityClassifier(void);
#endif
#ifdef SHAKE_DETECTOR
// The shake detector sends a report each time it detects a shake. 
BNO_Shake_t getShakeDetector(void);
#endif
#ifdef FLIP_DETECTOR
// The flip detector sends a report each time it detects a flip.
uint16_t getFlipDetector(void);
#endif
#ifdef PICKUP_DETECTOR
// The pickup detector sends a report each time it detects a pickup.
BNO_Pickup_t getPickupDetector(void);
#endif
#ifdef STABILITY_DETECTOR
// The stability detector sends a report each time it detects entry in or exit from a stable state.
BNO_StabilityDetector_t getStabilityDetector(void);
#endif
#ifdef PERSONAL_ACTIVITY_CLASSIFIER
// See 6.5.36 Personal Activity Classifier (0x1E)
BNO_PersonalActivityClassifier_t getPersonalActivityClassifier(void);
#endif
#ifdef SLEEP_DETECTOR
// The sleep detector sends a report each time it detects a change in sleep state.
uint8_t getSleepDetector(void);
#endif
#ifdef TILT_DETECTOR
// The tilt detector sends a report each time it detects a tilt.
uint16_t getTiltDetector(void);
#endif
#ifdef POCKET_DETECTOR
// The pocket detector sends a report each time it detects entry in or exit from a pocket state.
uint16_t getPocketDetector(void);
#endif
#ifdef CIRCLE_DETECTOR
// The circle detector sends a report each time it detects a double circle gesture. 
uint16_t getCircleDetector(void);
#endif
#ifdef HEART_RATE_MONITOR
// The heart rate monitor measures the user’s heart rate reported in beats per minute (bpm)
uint16_t getHeartRateMonitor(void);
#endif
#ifdef ARVR_STABILIZED_RV
// AR/VR STABILIZED ROTATION VECTOR
// Estimates of the magnetic field and the roll/pitch of the device can create a potential correction in the rotation
// vector produced. For applications (typically augmented or virtual reality applications) where a sudden jump can be
// disturbing, the output is adjusted to prevent these jumps in a manner that takes account of the velocity of the
// sensor system. This process is called AR/VR stabilization. An FRS (Flash Record System – see Figure 1-31)
// record is provided to allow configuration of this feature
BNO_RotationVectorWAcc_t BNO_getArvrStabilizedRV(void);
#endif
#ifdef ARVR_STABILIZED_GRV
// AR/VR STABILIZED GAME ROTATION VECTOR
// While the magnetometer is removed from the calculation of the game rotation vector, the accelerometer itself can
// create a potential correction in the rotation vector produced (i.e. the estimate of gravity changes). For applications
// (typically augmented or virtual reality applications) where a sudden jump can be disturbing, the output is adjusted
// to prevent these jumps in a manner that takes account of the velocity of the sensor system. This process is called
// AR/VR stabilization. An FRS (Flash Record System – see Figure 1-31) record is provided to allow configuration of this feature.
BNO_RotationVector_t BNO_getArVrStabilizedGRV(void);
#endif
#ifdef GYRO_INTEGRATED_RV
// GYRO ROTATION VECTOR
// Head tracking systems within a virtual reality headset require low latency processing of motion. To facilitate this,
// the BNO08X can provide a rotation vector at rates up to 1kHz. The gyro rotation Vector provides an alternate
// orientation to the standard rotation vector. Compared to the standard rotation vector the gyro rotation vector has
// an optimized processing path and correction style (correction is the adjustments made to the output based on
// more accurate estimates of gravity, mag field, angular velocity) that is suitable for head tracking applications.
BNO_GyroIntegratedRV_t BNO_getGyroIntegratedRV(void);
#endif
#ifdef IZRO_MOTION_REQUEST
BNO_IZroRequest_t getIzroRequest(void);
#endif
#ifdef RAW_OPTICAL_FLOW
// The Optical Flow report provides raw optical flow sensor data.
BNO_RawOptFlow_t getRawOptFlow(void);
#endif
#ifdef DEAD_RECKONING_POSE
// The Dead Reckoning Pose report provides the linear position, angular position, linear velocity,
// and angular velocity as determined by the Dead Reckoning system.
BNO_DeadReckoningPose_t getDeadReckoningPose(void);
#endif
#ifdef WHEEL_ENCODER
BNO_WheelEncoder_t getWheelEncoder(void);
#endif

#ifdef __cplusplus
}
#endif

#endif /* __BNO_08x_I2C_H */

In BNO_08x_I2C.c add the following code

#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "BNO_08x_I2C.h"
#include "i2c.h"
#include "gpio.h"
#include "tim.h"

//Global Variables

// The interrupt variable
extern volatile uint8_t BNO_Ready;
//extern volatile uint8_t i2c1_transfer_complete;
//A monotonically incrementing uint8_t that rolls over. It is used to detect missing commands and to synchronize responses
static uint8_t commandSequenceNumber = 0;
// Similar with  the above var
static uint8_t cmdSeqNo = 0;
// The data array for read and write operations
static uint8_t bufferIO[RX_PACKET_SIZE];
// 6 SHTP channels. Each channel has its own sequence number
static uint8_t sequenceNumber[SEQUENCE_SIZE];
// Variable that hold the reset status of the sensor
static uint8_t resetOccurred = 0;

static uint8_t saveDcdStatus = 1;

// Structure to hold the product ID
static BNO_productID_t myID = {0};
#ifdef USE_ERROR_REPORT
static BNO_Error_t errors  = {0};
#endif
#ifdef USE_COUNTER_REPORT
static BNO_Counts_t counts  = {0};
#endif
#ifdef USE_ERROR_REPORT
static BNO_Error_t errors  = {0};
#endif
// Structure to hold the product calibration status data
static BNO_calibrationStat_t calibrationStatus = {0};
// Structure to hold the product calibration status data
static BNO_FrsWriteResp_t frsWriteResponse = {0};
static BNO_FrsReadResp_t frsReadResponse = {0};
static BNO_Oscillator_t oscillatorType = {0};
static BNO_Boot_t bootLoader = {0};

#ifdef USE_FOR_TELESCOPE
static BNO_CommandReq_t cmdRequest = {0};
static BNO_CommandResp_t cmdResponse = {0};
static BNO_Feature_t sensorFeartures = {0};
BNO_RollPitchYaw_t rpy = {0};
#endif

BNO_SensorValue_t sensorData = {0};

uint32_t counter;

// Wait for an interrupt to occur or timeout in 200ms
static uint8_t waitInt(void) {
	uint32_t timeOut = HAL_GetTick() + RESET_DELAY;
	while(timeOut > HAL_GetTick()) {
		if(BNO_Ready) {
			BNO_Ready = 0;
			return 1;
		}
	}
	return 0;	
}

// Timer 2 set to get microseconds
static void start_timer(void) {
	__HAL_RCC_TIM2_CLK_ENABLE();
	// Prescale to get 1 count per uS
	uint16_t prescaler = 274;
	htim2.Instance = TIM2;
	htim2.Init.Period = 0xFFFFFFFF;
	htim2.Init.Prescaler = prescaler;
	htim2.Init.ClockDivision = 0;
	htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
	HAL_TIM_Base_Init(&htim2);
	HAL_TIM_Base_Start(&htim2);
}

// Get microseconds
static uint32_t timeNowUs(void) {
	return __HAL_TIM_GET_COUNTER(&htim2);
}

// Delay microseconds
static void delay_us(uint32_t t) {
	uint32_t now = timeNowUs();
	uint32_t start = now;
	while ((now - start) < t) {
		now = timeNowUs();
	}
}

#ifdef USE_FOR_TELESCOPE
// get 1/sqrt(x)
static float invSqrt(const float x) {
    float halfx = 0.5f * x;
    float y = x;
    int i = *(int*)&y;  // Interpret float bits as integer
    i = 0x5f3759df - (i >> 1);  // Magic number for fast approximation
    y = *(float*)&i;
    y = y * (1.5f - (halfx * y * y));  // Refine approximation
    return y;
}

// Update structures quatIJKR and angles
static void quaternionUpdate(const BNO_RotationVectorWAcc_t *inRotVector) {
    // Normalize quaternion
    float norm = invSqrt(inRotVector->I * inRotVector->I + 
                         inRotVector->J * inRotVector->J + 
                         inRotVector->K * inRotVector->K + 
                         inRotVector->Real * inRotVector->Real);

    // Apply normalization to quaternion components
    float q1 = inRotVector->I * norm;  // x
    float q2 = inRotVector->J * norm;  // y
    float q3 = inRotVector->K * norm;  // z
    float q4 = inRotVector->Real * norm;  // w

    // Precompute repeated terms to save computation time
    float q1q1 = q1 * q1;
    float q2q2 = q2 * q2;
    float q3q3 = q3 * q3;
    float q4q4 = q4 * q4;

    float q2q3 = q2 * q3;
    float q1q4 = q1 * q4;
    float q2q4 = q2 * q4;
    float q1q3 = q1 * q3;
    float q1q2 = q1 * q2;
    float q3q4 = q3 * q4;

    // Calculate roll, pitch, and yaw
    rpy.Pitch = atan2f(2.0f * (q2q3 + q1q4), q1q1 + q2q2 - q3q3 - q4q4);
    rpy.Roll  = -asinf(2.0f * (q2q4 - q1q3));
    rpy.Yaw   = atan2f(2.0f * (q1q2 + q3q4), q1q1 - q2q2 - q3q3 + q4q4);

    // Convert to degrees
    rpy.Pitch *= _180_DIV_PI;
    rpy.Roll  *= _180_DIV_PI;
    rpy.Yaw   *= _180_DIV_PI;

    // Normalize yaw to the [0, 360) range
    rpy.Yaw = (rpy.Yaw >= 0.0f) ? 360.0f - rpy.Yaw : -rpy.Yaw;

    // Normalize pitch to the [-180, 180] range
    rpy.Pitch = (rpy.Pitch >= 0.0f) ? 180.0f - rpy.Pitch : -(rpy.Pitch + 180.0f);
}
#endif

// Sets to buffrIO first 21 bytes to 0
static void resetHeader(const uint8_t id) {
	memset(bufferIO, 0, TX_PACKET_SIZE);
	bufferIO[4] = id;
}

// Send a packet data to BNO

static HAL_StatusTypeDef sendPacket(const uint8_t channelNumber) {
    // dataLength includes the SHTP_HEADER_SIZE
    uint8_t dataLength = 0;
    bufferIO[2] = channelNumber;
    bufferIO[3] = sequenceNumber[channelNumber]++;
    
    // Determine the data length based on the channel type and command
    if (bufferIO[2] == CHANNEL_EXECUTABLE) {
        dataLength = 5;
    } else {
        switch (bufferIO[4]) {
            case REPORT_SENSOR_FLUSH_REQUEST:
            case REPORT_GET_FEATURE_REQUEST:
            case REPORT_PRODUCT_ID_REQUEST:
                dataLength = 6;
                break;
            case REPORT_FRS_READ_REQUEST:
                dataLength = 12;
                break;
            case COMMAND_ME_CALIBRATE:
            case COMMAND_TARE:
            case COMMAND_SAVE_DCD:
            case REPORT_COMMAND_REQUEST:
            case REPORT_FRS_WRITE_REQUEST:
                dataLength = 16;
                break;
            case REPORT_SET_FEATURE_COMMAND:
                dataLength = 21;
                break;
        }
    }

    bufferIO[0] = dataLength & 0xFF;
    bufferIO[1] = (dataLength >> 8) & 0x7F;

    // Send packet to IMU
    #ifdef USE_I2C_DMA
        //i2c1_transfer_complete = 0;  // Reset DMA transfer complete flag
        if (HAL_I2C_Master_Transmit_DMA(&hi2c1, BNO_W_ADDR, bufferIO, dataLength) != HAL_OK) {
            return HAL_ERROR;  // Return error if DMA transmission fails
        }
        // Wait for DMA transfer to complete
        //while (!i2c1_transfer_complete);
				while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) { }
    #else
        if (HAL_I2C_Master_Transmit(&hi2c1, BNO_W_ADDR, bufferIO, dataLength, HAL_MAX_DELAY) != HAL_OK) {
            return HAL_ERROR;  // Return error if transmission fails
        }
    #endif

    delay_us(RESET_DELAY);  // Delay 100 microseconds before next I2C transmission
    return HAL_OK;
}

// Get a data packet from BNO
static HAL_StatusTypeDef receivePacket(void) {
    // Reset interrupt status
    BNO_Ready = 0;
    memset(bufferIO, 0, TX_PACKET_SIZE);  // Clear the buffer

    // First, receive the header (4 bytes) to determine the full packet size
    #ifdef USE_I2C_DMA
        //i2c1_transfer_complete = 0;  // Reset DMA transfer complete flag
        if (HAL_I2C_Master_Receive_DMA(&hi2c1, BNO_R_ADDR, bufferIO, HEADER_SIZE) != HAL_OK) {
            return HAL_ERROR;  // Return error if DMA reception fails
        }
        // Wait for DMA transfer to complete
        //while (!i2c1_transfer_complete);
				while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) { }
    #else
        if (HAL_I2C_Master_Receive(&hi2c1, BNO_R_ADDR, bufferIO, HEADER_SIZE, HAL_MAX_DELAY) != HAL_OK) {
            return HAL_ERROR;  // Return error if reception fails
        }
    #endif

    // Calculate the number of data bytes in the packet
    uint16_t rxPacketLength = *(uint16_t *)&bufferIO;
    if (!rxPacketLength || rxPacketLength > RX_PACKET_SIZE) {
        return HAL_ERROR;  // Invalid packet size
    }

    delay_us(RESET_DELAY);  // Delay 100 microseconds before receiving the rest of the packet

    // Now, receive the full packet based on the calculated size
    #ifdef USE_I2C_DMA
        //i2c1_transfer_complete = 0;  // Reset DMA transfer complete flag
        if (HAL_I2C_Master_Receive_DMA(&hi2c1, BNO_R_ADDR, bufferIO, rxPacketLength) != HAL_OK) {
            return HAL_ERROR;  // Return error if DMA reception fails
        }
        // Wait for DMA transfer to complete
        //while (!i2c1_transfer_complete);
				while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) { }
    #else
        if (HAL_I2C_Master_Receive(&hi2c1, BNO_R_ADDR, bufferIO, rxPacketLength, HAL_MAX_DELAY) != HAL_OK) {
            return HAL_ERROR;  // Return error if reception fails
        }
    #endif

    delay_us(RESET_DELAY);  // Delay 100 microseconds after receiving the packet
    return HAL_OK;
}


// Send a command on exe channel
static HAL_StatusTypeDef sendExecutable(const uint8_t cmd) {
	resetHeader(cmd);
	return sendPacket(CHANNEL_EXECUTABLE);
}
// Compute a sensor value from bufferIO
//bufferIO - 0..3 Header
//bufferIO - 4..8 Time stamp
//bufferIO - 9 Which sensor produced this event
//bufferIO - 10 Sequence number increments once for each report sent. Gaps in the sequence numbers indicate missing or dropped reports.
//bufferIO - 11 Status bits 7-5: reserved, 4-2: exponent delay, 1-0: Accuracy
static void getSensorValue(void) {
	//Calculate the number of data bytes in this packet
	//int16_t dataLength = *(uint16_t *)&bufferIO - 4;
	sensorData.sensorId = bufferIO[9];
	sensorData.timestamp = *(uint32_t *)&bufferIO[5];
	#ifdef GYRO_INTEGRATED_RV
	if(sensorData.sensorId != GYRO_INTEGRATED_RV) {
		sensorData.sequence = bufferIO[10];
		sensorData.status = bufferIO[11] & 0x03; //Get status bits
	} else {
		sensorData.sequence = 0;
		sensorData.status = 0; //Get status bits
	}
	#else
		sensorData.sequence = bufferIO[10];
		sensorData.status = bufferIO[11] & 0x03; //Get status bits
	#endif


	switch(sensorData.sensorId)	{
		#ifdef RAW_ACCELEROMETER
		case RAW_ACCELEROMETER:
			sensorData.SenVal.RawAccelerometer.X = *(int16_t *)&bufferIO[13];
			sensorData.SenVal.RawAccelerometer.Y = *(int16_t *)&bufferIO[15];
			sensorData.SenVal.RawAccelerometer.Z = *(int16_t *)&bufferIO[17];
			sensorData.SenVal.RawAccelerometer.TimeStamp = *(uint32_t *)&bufferIO[21];
		break;
		#endif
		#ifdef ACCELEROMETER
		case ACCELEROMETER:
			sensorData.SenVal.Accelerometer.X = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q8;
			sensorData.SenVal.Accelerometer.Y = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q8;
			sensorData.SenVal.Accelerometer.Z = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q8;
		break;
		#endif
		#ifdef LINEAR_ACCELERATION
		case LINEAR_ACCELERATION:
			sensorData.SenVal.LinearAcceleration.X = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q8;
			sensorData.SenVal.LinearAcceleration.Y = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q8;
			sensorData.SenVal.LinearAcceleration.Z = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q8;
		break;
		#endif
		#ifdef GRAVITY
		case GRAVITY:
			sensorData.SenVal.Gravity.X = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q8;
			sensorData.SenVal.Gravity.Y = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q8;
			sensorData.SenVal.Gravity.Z = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q8;
		break;
		#endif
		#ifdef RAW_GYROSCOPE
		case RAW_GYROSCOPE:
			sensorData.SenVal.RawGyroscope.X = *(int16_t *)&bufferIO[13];
			sensorData.SenVal.RawGyroscope.Y = *(int16_t *)&bufferIO[15];
			sensorData.SenVal.RawGyroscope.Z = *(int16_t *)&bufferIO[17];
			sensorData.SenVal.RawGyroscope.Temperature = *(int16_t *)&bufferIO[19];
			sensorData.SenVal.RawGyroscope.TimeStamp = *(uint32_t *)&bufferIO[21];
		break;
		#endif
		#ifdef GYROSCOPE_CALIBRATED
		case GYROSCOPE_CALIBRATED:
			sensorData.SenVal.Gyroscope.X = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q9;
			sensorData.SenVal.Gyroscope.Y = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q9;
			sensorData.SenVal.Gyroscope.Z = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q9;
		break;
		#endif
		#ifdef GYROSCOPE_UNCALIBRATED
		case GYROSCOPE_UNCALIBRATED:
			sensorData.SenVal.GyroscopeUncal.X = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q9;
			sensorData.SenVal.GyroscopeUncal.Y = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q9;
			sensorData.SenVal.GyroscopeUncal.Z = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q9;
			sensorData.SenVal.GyroscopeUncal.BiasX = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q9;
			sensorData.SenVal.GyroscopeUncal.BiasY = (float)(*(int16_t *)&bufferIO[21]) * SCALE_Q9;
			sensorData.SenVal.GyroscopeUncal.BiasZ = (float)(*(int16_t *)&bufferIO[23]) * SCALE_Q9;
		break;
		#endif
		#ifdef RAW_MAGNETOMETER
		case RAW_MAGNETOMETER:
			sensorData.SenVal.RawMagnetometer.X = *(int16_t *)&bufferIO[13];
			sensorData.SenVal.RawMagnetometer.Y = *(int16_t *)&bufferIO[15];
			sensorData.SenVal.RawMagnetometer.Z = *(int16_t *)&bufferIO[17];
			sensorData.SenVal.RawMagnetometer.TimeStamp = *(uint32_t *)&bufferIO[21];
		break;
		#endif
		#ifdef MAGNETIC_FIELD_CALIBRATED
		case MAGNETIC_FIELD_CALIBRATED:
			sensorData.SenVal.MagneticField.X = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q4;
			sensorData.SenVal.MagneticField.Y = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q4;
			sensorData.SenVal.MagneticField.Z = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q4;
		break;
		#endif
		#ifdef MAGNETIC_FIELD_UNCALIBRATED
		case MAGNETIC_FIELD_UNCALIBRATED:
			sensorData.SenVal.MagneticFieldUncal.X = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q4;
			sensorData.SenVal.MagneticFieldUncal.Y = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q4;
			sensorData.SenVal.MagneticFieldUncal.Z = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q4;
			sensorData.SenVal.MagneticFieldUncal.BiasX = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q4;
			sensorData.SenVal.MagneticFieldUncal.BiasY = (float)(*(int16_t *)&bufferIO[21]) * SCALE_Q4;
			sensorData.SenVal.MagneticFieldUncal.BiasZ = (float)(*(int16_t *)&bufferIO[23]) * SCALE_Q4;
		break;
		#endif
		#ifdef ROTATION_VECTOR
		case ROTATION_VECTOR:
			#ifdef USE_FOR_TELESCOPE
			sensorData.SenVal.RotationVector.I = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q14;
			sensorData.SenVal.RotationVector.J = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q14;
			sensorData.SenVal.RotationVector.K = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q14;
			sensorData.SenVal.RotationVector.Real = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q14;
			sensorData.SenVal.RotationVector.Accuracy = (float)(*(int16_t *)&bufferIO[21]) * SCALE_Q14;
			// Update Euler 
			quaternionUpdate(&sensorData.SenVal.RotationVector);
			#else
			sensorData.SenVal.RotationVector.I = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q14;
			sensorData.SenVal.RotationVector.J = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q14;
			sensorData.SenVal.RotationVector.K = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q14;
			sensorData.SenVal.RotationVector.Real = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q14;
			sensorData.SenVal.RotationVector.Accuracy = (float)(*(int16_t *)&bufferIO[21]) * SCALE_Q14;
			#endif
		break;
		#endif
		#ifdef GAME_ROTATION_VECTOR
			case GAME_ROTATION_VECTOR:
			sensorData.SenVal.GameRotationVector.I = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q14;
			sensorData.SenVal.GameRotationVector.J = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q14;
			sensorData.SenVal.GameRotationVector.K = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q14;
			sensorData.SenVal.GameRotationVector.Real = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q14;
		break;
		#endif
		#ifdef GEOMAGNETIC_ROTATION_VECTOR
		case GEOMAGNETIC_ROTATION_VECTOR:
			sensorData.SenVal.GeoMagRotationVector.I = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q14;
			sensorData.SenVal.GeoMagRotationVector.J = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q14;
			sensorData.SenVal.GeoMagRotationVector.K = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q14;
			sensorData.SenVal.GeoMagRotationVector.Real = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q14;
			sensorData.SenVal.GeoMagRotationVector.Accuracy = (float)(*(int16_t *)&bufferIO[21]) * SCALE_Q14;
		break;
		#endif
		#ifdef PRESSURE
		case PRESSURE:
			sensorData.SenVal.Pressure = (float)(*(int32_t *)&bufferIO[13]) * SCALE_Q20;
		break;
		#endif
		#ifdef AMBIENT_LIGHT
		case AMBIENT_LIGHT:
			sensorData.SenVal.AmbientLight = (float)(*(int32_t *)&bufferIO[13]) * SCALE_Q8;
		break;
		#endif
		#ifdef HUMIDITY
		case HUMIDITY:
			sensorData.SenVal.Humidity = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q8;
		break;
		#endif
		#ifdef PROXIMITY
		case PROXIMITY:
			sensorData.SenVal.Proximity = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q4;
		break;
		#endif
		#ifdef TEMPERATURE
		case TEMPERATURE:
			sensorData.SenVal.Temperature = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q7;
		break;
		#endif
		#ifdef RESERVED
		case RESERVED:
			sensorData.SenVal.Reserved = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q7;
		break;
		#endif
		#ifdef TAP_DETECTOR
		case TAP_DETECTOR:
			sensorData.SenVal.TapDetectorFlag = bufferIO[13];
		break;
		#endif
		#ifdef STEP_DETECTOR
		case STEP_DETECTOR:
			sensorData.SenVal.StepDetectorLatency = *(uint32_t *)&bufferIO[13];
		break;
		#endif
		#ifdef STEP_COUNTER
		case STEP_COUNTER:
			sensorData.SenVal.StepCounter.Latency = *(uint32_t *)&bufferIO[13];
			sensorData.SenVal.StepCounter.Steps = *(uint32_t *)&bufferIO[17];
		break;
		#endif
		#ifdef SIGNIFICANT_MOTION
		case SIGNIFICANT_MOTION:
			sensorData.SenVal.SignificantMotion = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef STABILITY_CLASSIFIER
		case STABILITY_CLASSIFIER: 
			sensorData.SenVal.StabilityClassifier = bufferIO[13];
		break;
		#endif
		#ifdef SHAKE_DETECTOR
		case SHAKE_DETECTOR:
			sensorData.SenVal.ShakeDetector = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef FLIP_DETECTOR
		case FLIP_DETECTOR:
			sensorData.SenVal.FlipDetector = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef PICKUP_DETECTOR
		case PICKUP_DETECTOR:
			sensorData.SenVal.PickupDetector = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef STABILITY_DETECTOR
		case STABILITY_DETECTOR:
			sensorData.SenVal.StabilityDetector = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef PERSONAL_ACTIVITY_CLASSIFIER
		case PERSONAL_ACTIVITY_CLASSIFIER:
			sensorData.SenVal.PersonalActivityClassifier.Page = bufferIO[13] & 0x7F;
			sensorData.SenVal.PersonalActivityClassifier.LastPage = ((bufferIO[13] & 0x80) != 0);
			sensorData.SenVal.PersonalActivityClassifier.MostLikelyState = bufferIO[14];
			// ToDo remove for loop, use pointer
			for (int n = 0; n < 10; n++) {
				sensorData.SenVal.PersonalActivityClassifier.Confidence[n] = bufferIO[15+n];
			}
		break;
		#endif
		#ifdef SLEEP_DETECTOR
		case SLEEP_DETECTOR:
			sensorData.SenVal.SleepDetector = bufferIO[13];
		break;
		#endif
		#ifdef TILT_DETECTOR
		case TILT_DETECTOR:
			sensorData.SenVal.TiltDetector = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef POCKET_DETECTOR
		case POCKET_DETECTOR:
			sensorData.SenVal.PocketDetector = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef CIRCLE_DETECTOR
		case CIRCLE_DETECTOR:
			sensorData.SenVal.CircleDetector = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef HEART_RATE_MONITOR
		case HEART_RATE_MONITOR:
			sensorData.SenVal.HeartRateMonitor = *(uint16_t *)&bufferIO[13];
		break;
		#endif
		#ifdef ARVR_STABILIZED_RV
		case ARVR_STABILIZED_RV:
			sensorData.SenVal.ArVrStabilizedRV.I = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q14;
			sensorData.SenVal.ArVrStabilizedRV.J = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q14;
			sensorData.SenVal.ArVrStabilizedRV.K = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q14;
			sensorData.SenVal.ArVrStabilizedRV.Real = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q14;
			sensorData.SenVal.ArVrStabilizedRV.Accuracy = (float)(*(int16_t *)&bufferIO[21]) * SCALE_Q12;
		break;
		#endif
		#ifdef ARVR_STABILIZED_GRV
		case ARVR_STABILIZED_GRV:
			sensorData.SenVal.ArVrStabilizedGRV.I = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q14;
			sensorData.SenVal.ArVrStabilizedGRV.J = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q14;
			sensorData.SenVal.ArVrStabilizedGRV.K = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q14;
			sensorData.SenVal.ArVrStabilizedGRV.Real = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q14;
		break;
		#endif
		#ifdef GYRO_INTEGRATED_RV
		case GYRO_INTEGRATED_RV:
			sensorData.SenVal.GyroIntegratedRV.I = (float)(*(int16_t *)&bufferIO[9]) * SCALE_Q14;
			sensorData.SenVal.GyroIntegratedRV.J = (float)(*(int16_t *)&bufferIO[11]) * SCALE_Q14;
			sensorData.SenVal.GyroIntegratedRV.J = (float)(*(int16_t *)&bufferIO[13]) * SCALE_Q14;
			sensorData.SenVal.GyroIntegratedRV.Real = (float)(*(int16_t *)&bufferIO[15]) * SCALE_Q14;
			sensorData.SenVal.GyroIntegratedRV.AngleVelX = (float)(*(int16_t *)&bufferIO[17]) * SCALE_Q10;
			sensorData.SenVal.GyroIntegratedRV.AngleVelY = (float)(*(int16_t *)&bufferIO[19]) * SCALE_Q10;
			sensorData.SenVal.GyroIntegratedRV.AngleVelZ = (float)(*(int16_t *)&bufferIO[21]) * SCALE_Q10;
		break;
		#endif
		#ifdef IZRO_MOTION_REQUEST
		case IZRO_MOTION_REQUEST:
			sensorData.SenVal.IzroRequest.Intent = (BNO_IZroMotionIntent_t)bufferIO[13];
			sensorData.SenVal.IzroRequest.Request = (BNO_IZroMotionRequest_t)bufferIO[14];
		break;
		#endif
		#ifdef RAW_OPTICAL_FLOW
		case RAW_OPTICAL_FLOW:
			sensorData.SenVal.RawOptFlow.Dx = *(int16_t *)&bufferIO[13];
			sensorData.SenVal.RawOptFlow.Dy = *(int16_t *)&bufferIO[15];
			sensorData.SenVal.RawOptFlow.Iq = *(int16_t *)&bufferIO[17];
			sensorData.SenVal.RawOptFlow.ResX = bufferIO[19];
			sensorData.SenVal.RawOptFlow.ResY = bufferIO[20];
			sensorData.SenVal.RawOptFlow.Shutter = bufferIO[21];
			sensorData.SenVal.RawOptFlow.FrameMax = bufferIO[22];
			sensorData.SenVal.RawOptFlow.FrameAvg = bufferIO[23];
			sensorData.SenVal.RawOptFlow.FrameMin = bufferIO[24];
			sensorData.SenVal.RawOptFlow.LaserOn = bufferIO[25];
			sensorData.SenVal.RawOptFlow.Dt = *(int16_t *)&bufferIO[27];
			sensorData.SenVal.RawOptFlow.TimeStamp = *(int32_t *)&bufferIO[29];
		break;
		#endif
		#ifdef DEAD_RECKONING_POSE
		case DEAD_RECKONING_POSE:
			sensorData.SenVal.DeadReckoningPose.TimeStamp = *(int32_t *)&bufferIO[13];
			sensorData.SenVal.DeadReckoningPose.LinPosX = (float)(*(int32_t *)&bufferIO[17]) * SCALE_Q17;
			sensorData.SenVal.DeadReckoningPose.LinPosY = (float)(*(int32_t *)&bufferIO[21]) * SCALE_Q17;
			sensorData.SenVal.DeadReckoningPose.LinPosZ = (float)(*(int32_t *)&bufferIO[25]) * SCALE_Q17;

			sensorData.SenVal.DeadReckoningPose.I = (float)(*(int32_t *)&bufferIO[29]) * SCALE_Q30;
			sensorData.SenVal.DeadReckoningPose.J = (float)(*(int32_t *)&bufferIO[33]) * SCALE_Q30;
			sensorData.SenVal.DeadReckoningPose.K = (float)(*(int32_t *)&bufferIO[37]) * SCALE_Q30;
			sensorData.SenVal.DeadReckoningPose.Real = (float)(*(int32_t *)&bufferIO[41]) * SCALE_Q30;

			sensorData.SenVal.DeadReckoningPose.LinVelX = (float)(*(int32_t *)&bufferIO[45]) * SCALE_Q25;
			sensorData.SenVal.DeadReckoningPose.LinVelY = (float)(*(int32_t *)&bufferIO[49]) * SCALE_Q25;
			sensorData.SenVal.DeadReckoningPose.LinVelZ = (float)(*(int32_t *)&bufferIO[53]) * SCALE_Q25;

			sensorData.SenVal.DeadReckoningPose.AngleVelX = (float)(*(int32_t *)&bufferIO[57]) * SCALE_Q25;
			sensorData.SenVal.DeadReckoningPose.AngleVelY = (float)(*(int32_t *)&bufferIO[61]) * SCALE_Q25;
			sensorData.SenVal.DeadReckoningPose.AngleVelZ = (float)(*(int32_t *)&bufferIO[65]) * SCALE_Q25;
		break;
		#endif
		#ifdef WHEEL_ENCODER
		case WHEEL_ENCODER:
			sensorData.SenVal.WheelEncoder.TimeStamp = *(int32_t *)&bufferIO[13];
			sensorData.SenVal.WheelEncoder.WheelIndex = bufferIO[17];
			sensorData.SenVal.WheelEncoder.DataType = bufferIO[18];
			sensorData.SenVal.WheelEncoder.Data = *(int16_t *)&bufferIO[19];
		break;
		#endif
	}
}

// Process a command response
static HAL_StatusTypeDef processCommandResponse(void) {
	// Reset complete
	switch(cmdResponse.command) {
		#ifdef USE_ERROR_REPORT
		case COMMAND_ERRORS: // 0x01 – report all errors in the error queue
			errors = *(BNO_Error_t *)&bufferIO[9];
			return HAL_OK;
		break;
		#endif
		#ifdef USE_COUNTER_REPORT
		case COMMAND_COUNTER: // 0x02 – Counter command
			counts = *(BNO_Counts_t *)&bufferIO[9];
			return HAL_OK;
		break;
		#endif
		case COMMAND_UNSOLICITED_INITIALIZE: // 0x84 – Initialize (unsolicited)
		case COMMAND_INITIALIZE: // 0x04 – Initialize
			if (!bufferIO[9]) {
				resetOccurred = 1;
				return HAL_OK;
			}
		break;
		case COMMAND_SAVE_DCD: // 0x06 – Save DCD
			saveDcdStatus = bufferIO[9];
			return HAL_OK;
		break;	
		case COMMAND_ME_CALIBRATE: // 0x07 – Configure ME Calibration
			calibrationStatus = *(BNO_calibrationStat_t *)&bufferIO[9];
			return HAL_OK;
		break;
		case COMMAND_OSCILLATOR: // 0x0A – Get Oscillator Type Command
			oscillatorType = bufferIO[9];
			return HAL_OK;
		break;	
		case COMMAND_TURNTABLE_CAL: // 0x0C – Turntable Calibration
			calibrationStatus.Status = bufferIO[9];
			return HAL_OK;
		break;	
		case COMMAND_BOOTLOADER:// 0x0D – Bootloader command
			bootLoader = *(BNO_Boot_t *)&bufferIO[10];
			return HAL_OK;
		break;		
	}
	return HAL_ERROR;
}

// Process a response
static HAL_StatusTypeDef processResponse(void) {
	switch(bufferIO[4]) { 
		case REPORT_UNSOLICITED_RESPONSE: // 0x00
			if(bufferIO[2] == CHANNEL_COMMAND) return HAL_OK;
		break;
		case REPORT_UNSOLICITED_RESPONSE1: // 0x01
			if(bufferIO[2] == CHANNEL_EXECUTABLE) return HAL_OK;
		break;	
		case REPORT_COMMAND_RESPONSE: // 0xF1
			cmdResponse = *(BNO_CommandResp_t *)&bufferIO[5];
			return processCommandResponse();
		break;	
		case REPORT_FRS_READ_RESPONSE: // 0xF3
			frsReadResponse = *(BNO_FrsReadResp_t *)&bufferIO[5];
			return HAL_OK;
		break;		
		case REPORT_FRS_WRITE_RESPONSE: // 0xF5
			frsWriteResponse = *(BNO_FrsWriteResp_t *)&bufferIO[5];
			return HAL_OK;
		break;				
		case REPORT_PRODUCT_ID_RESPONSE: // 0xF8
			myID = *(BNO_productID_t *)&bufferIO[5];
			return HAL_OK;
		break;
		case REPORT_BASE_TIMESTAMP_REF: // 0xFB
			if(bufferIO[2] == CHANNEL_REPORTS) {
				getSensorValue();
				return HAL_OK;
			}
			return HAL_ERROR;
		break;
		case REPORT_GET_FEATURE_RESPONSE: // 0xFC
			sensorFeartures = *(BNO_Feature_t *)&bufferIO[5];
			return HAL_OK;
		break;
		case REPORT_SENSOR_FLUSH_RESPONSE: // 0xEF
			if(bufferIO[2] == CHANNEL_REPORTS) {
				// Not using them, so....
				return HAL_OK;
			}
			return HAL_ERROR;
		break;
	}
	return HAL_ERROR;
}

// Wait to receive a packet from BNO
static HAL_StatusTypeDef waitForPacket(void) {
	if(waitInt()) {
		return receivePacket();
	}
	return HAL_ERROR;
}

// Wait for a response from sensor
static HAL_StatusTypeDef waitForCommandResponse(void) {
	uint8_t sendChannel = CHANNEL_CONTROL; 
	uint8_t receiveChannel = CHANNEL_CONTROL;
	uint8_t expectedResponse = REPORT_COMMAND_RESPONSE;
	switch(bufferIO[4]) {
		case REPORT_PRODUCT_ID_REQUEST:
			expectedResponse = REPORT_PRODUCT_ID_RESPONSE;
		break;
		case REPORT_SENSOR_FLUSH_REQUEST:
			receiveChannel = CHANNEL_REPORTS;
			expectedResponse = REPORT_SENSOR_FLUSH_RESPONSE;
		break;
		case REPORT_GET_FEATURE_REQUEST:
			expectedResponse = REPORT_GET_FEATURE_RESPONSE;
		break;
		case REPORT_FRS_WRITE_REQUEST:
			expectedResponse = REPORT_FRS_WRITE_RESPONSE;
		break;
		case REPORT_FRS_READ_REQUEST:
			expectedResponse = REPORT_FRS_READ_RESPONSE;
		break;
	}
	if(sendPacket(sendChannel) == HAL_OK) {
		uint8_t retry = 5;
		while(retry) {
			if(waitForPacket() == HAL_OK) {
				if((bufferIO[2] == receiveChannel)  && (bufferIO[4] == expectedResponse)) {
					return processResponse(); // Found correct packet!
				}
			}
			retry--;
		}
	}
	return HAL_ERROR;
}

// Gets the sensor SW information
static HAL_StatusTypeDef getID(void) {
	resetHeader(REPORT_PRODUCT_ID_REQUEST);
	return waitForCommandResponse();
}

//--------------------------------------------------------------------------------------------------------------------------
// Initialize the sesnor
// During reset or power-on sequence, the bootloader first checks the status of the BOOTN pin.
// If the pin is pulled low during reset or poweron, the BNO08X will enter the bootloader mode.
// If the BOOTN pin is pulled high, then the bootloader starts the application
HAL_StatusTypeDef BNO_Init(void) {
	BNO_BOOT_On; // Off only for DFU mode 0x52 I2C Address
	// Set reset pin low
	BNO_RST_Off;
	HAL_Delay(RESET_DELAY);
	// Start us timer
	start_timer();
	// Enable interrupt BNO_Ready
	HAL_NVIC_EnableIRQ(EXTI0_IRQn);
	// Delay for RESET_DELAY_US to ensure reset takes effect
	BNO_RST_On;
	HAL_Delay(RESET_DELAY);
	// If we got the initiat packet we make a soft reset
	//if(waitForPacket()) {
		if(processResponse() == HAL_OK) {
			// Wait for intterupt
			if(waitInt()) {
				if(BNO_Reset() == HAL_OK) {
					// Finally, we want to interrogate the device about its model and version.
					BNO_On();
					return getID();
				}
			}
		}
	//}
	return HAL_ERROR;
}

HAL_StatusTypeDef BNO_setHighAccuracyMode(void) {
    const uint32_t reportIntervalUs = 200000;  // 200 ms = 200,000 microseconds (5Hz)

    // Enable high accuracy for the accelerometer, gyroscope, and magnetometer
    if (BNO_condigureCalibration(CALIBRATE_ACCEL_GYRO_MAG) == HAL_OK) {
        // Set high accuracy mode for the Magnetometer (MAGNETIC_FIELD_CALIBRATED)
        if (BNO_setFeature(MAGNETIC_FIELD_CALIBRATED, reportIntervalUs, 0) == HAL_OK) {
            // Set high accuracy mode for the Rotation Vector
            if (BNO_setFeature(ROTATION_VECTOR, reportIntervalUs, 0) == HAL_OK) {
                // Optionally: Set high accuracy for the accelerometer, gyroscope, etc.
                // Uncomment these if you need high accuracy on other sensors.
                
                if (BNO_setFeature(ACCELEROMETER, reportIntervalUs, 0) != HAL_OK) return HAL_ERROR;
                if (BNO_setFeature(GYROSCOPE_CALIBRATED, reportIntervalUs, 0) != HAL_OK) return HAL_ERROR;

                // All configurations successful
                return HAL_OK;
            }
        }
    }
    return HAL_ERROR;  // Return an error if any step fails
}

// Check if we have unexpected reset
uint8_t isResetOccurred(void) {
	if(resetOccurred) {
		resetOccurred = 0;
		return 1;
	}
	return resetOccurred;
}
// Get the sensor that has new data
uint8_t BNO_getSensorEventID(void) {
	return sensorData.sensorId;
}

// Soft reset the sensor
HAL_StatusTypeDef BNO_Reset(void) {
	if(sendExecutable(COMMAND_INITIALIZE_RESET) != HAL_OK) { // Write 1 byte to chan EXE
		return HAL_ERROR;
	}
	HAL_Delay(700); // 700 millisecs for reboot
	// 2 packet to be ignored after reset
	if(waitForCommandResponse() == HAL_OK) {
		if(resetOccurred) {
			resetOccurred = 0;
		} else {
			return HAL_ERROR;
		}
	}
	return HAL_OK;
}

// Turn sensor ON
HAL_StatusTypeDef BNO_On(void) {
	return sendExecutable(COMMAND_INITIALIZE_ON);
}

// When sleep command is issued all sensors that are configured as always on or wake (see 1.3.5.1) will continue to operate all, other sensors will be disabled
HAL_StatusTypeDef BNO_Sleep(void) {
	return sendExecutable(COMMAND_INITIALIZE_SLEEP);
}

// Returns the product id data structure see 6.3.1 and 6.3.2
BNO_productID_t BNO_getProductID(void) {
	return myID;
}

// Length in 32-bit words of the record to be written. If the length is set to 0 then the record is erased
HAL_StatusTypeDef BNO_writeFRS(const uint16_t length, const uint16_t frsType) {
	// FRS Write Request (0xF7)
	resetHeader(REPORT_FRS_WRITE_REQUEST);
	//bufferIO[5] = 0; // Reserved
	*(uint16_t *)&bufferIO[6] = length;
	*(uint16_t *)&bufferIO[8] = frsType;
	// No response for this
	if(waitForCommandResponse() == HAL_OK) { 
		//!!! ToDo analize the response and do something with it
		uint8_t sendMoreData = 0;
		uint8_t completed = 0;
		// Status is bufferIO[5] see 6.3.5 FRS Write Response (0xF5)
		switch(bufferIO[5]) {
			case FRS_WRITE_STATUS_RECEIVED:
			case FRS_WRITE_STATUS_READY:
				sendMoreData = 1;
			break;
			case FRS_WRITE_STATUS_UNRECOGNIZED_FRS_TYPE:
			case FRS_WRITE_STATUS_BUSY:
			case FRS_WRITE_STATUS_FAILED:
			case FRS_WRITE_STATUS_NOT_READY:
			case FRS_WRITE_STATUS_INVALID_LENGTH:
			case FRS_WRITE_STATUS_INVALID_RECORD:
			case FRS_WRITE_STATUS_DEVICE_ERROR:
			case FRS_WRITE_STATUS_READ_ONLY:
				completed = 1;
			break;
			case FRS_WRITE_STATUS_WRITE_COMPLETED:
				// Successful completion
				completed = 1;
			break;
			case FRS_WRITE_STATUS_RECORD_VALID:

			break;
		}
		return HAL_OK;
	}
	return HAL_ERROR;
}

// See 6.3.6 FRS Read Request (0xF4) 6.3.7 FRS Read Response (0xF3)
HAL_StatusTypeDef BNO_readFRS(const uint16_t frsType) {
	resetHeader(REPORT_FRS_READ_REQUEST);
	//bufferIO[5] = 0; // Reserved
	//bufferIO[6] = 0; // Read from start
	*(uint16_t *)&bufferIO[8] = frsType;
	//bufferIO[10] = 0; // Read all avail data
	if(waitForCommandResponse() == HAL_OK) { 
		//!!! ToDo analize the response and do something with it
		// Get status portion of len_status field
		uint8_t status = bufferIO[5] & 0x0F;
		// Get Datalen portion of len_status field
		uint8_t Datalen = ((bufferIO[5] >> 4) & 0x0F);
		
		switch(status) { 
			// Check for errors: Unrecognized FRS type, Busy, Out of range, Device error
			case FRS_READ_STATUS_UNRECOGNIZED_FRS_TYPE:
			case FRS_READ_STATUS_BUSY:
			case FRS_READ_STATUS_OFFSET_OUT_OF_RANGE:
			case FRS_READ_STATUS_DEVICE_ERROR:
				// Operation failed
				return HAL_ERROR;
			break;
			case FRS_READ_STATUS_RECORD_EMPTY: // Empty record
				return HAL_OK;
			break;
			// If read is done...complete the operation
			case FRS_READ_STATUS_READ_RECORD_COMPLETED:
			case FRS_READ_STATUS_READ_BLOCK_COMPLETED:
			case FRS_READ_STATUS_READ_BLOCK_AND_RECORD_COMPLETED:
				return HAL_OK;
			break; 
		}
	}
	return HAL_ERROR;
}

#ifdef USE_ERROR_REPORT
HAL_StatusTypeDef BNO_getErrors(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_ERRORS; // 0x01 – report all errors in the error queue
	//bufferIO[7] = 0; // he severity of errors to report. Errors of this severity and higher will be reported. 0 – highest priority
	if(waitForCommandResponse() == HAL_OK) {
		// Error source. 0 – reserved, 1 – MotionEngine, 2 – MotionHub, 3 – SensorHub, 4 – Chip level executable, 5-254 reserved. 255 – no error to report.
		if(errors.Source == 0xFF) {
			return HAL_OK;
		}
	}
	return HAL_ERROR;
}
#endif

#ifdef USE_COUNTER_REPORT
HAL_StatusTypeDef BNO_getCounter(const uint8_t sensorID) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_COUNTER; // 0x02 – Counter command
	//bufferIO[7] = 0; // 0x00 – Sub-command: get counts
	bufferIO[8] = sensorID;
	if(waitForCommandResponse() == HAL_OK) {
		// Error source. 0 – reserved, 1 – MotionEngine, 2 – MotionHub, 3 – SensorHub, 4 – Chip level executable, 5-254 reserved. 255 – no error to report.
		if(errors.Source == 0xFF) {
			return HAL_OK;
		}
	}
	return HAL_ERROR;
}

HAL_StatusTypeDef BNO_clearCounter(const uint8_t sensorID) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_COUNTER; // 0x02 – Counter command
	bufferIO[7] = 1; // 0x01 – Sub-command: clear counts
	bufferIO[8] = sensorID;
	return sendPacket(CHANNEL_CONTROL);
}
#endif

#ifdef USE_REORIENT
// See 6.4.3.1 Tare Now (0x00)
HAL_StatusTypeDef BNO_TareNow(const BNO_TareAxis_t axis, const BNO_TareRV_t vector) {
 	// from SH-2 6.4.3 Tare (0x03)
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_TARE; // 0x03 – Tare command
	//bufferIO[7] = 0; // 0x00 – Subcommand: Perform Tare now
	// Bitmap of axes to tare: Bit 0= X, Bit 1= Y, Bit 2= Z 7 all
	bufferIO[8] = axis;
	//Which rotation vector to use as the basis for Tare adjustment.
	//Rotation Vector to use as basis for tare.
	//0: Rotation Vector
	//1: Gaming Rotation Vector
	//2: Geomagnetic Rotation Vector
	//3: Gyro-Integrated Rotation Vector
	//4: ARVR-Stabilized Rotation Vector
	//5: ARVR-Stabilized Game Rotation Vector
	bufferIO[9] = vector; // 
	return sendPacket(CHANNEL_CONTROL);
}

//This command instructs SH-2 to persist the results of the last tare operation to flash for use at the next system restart.
HAL_StatusTypeDef BNO_TarePerist(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_TARE; // 0x03 – Tare command
	bufferIO[7] = COMMAND_TARE_PERSIST; // 0x01 – Persist Tare
	return sendPacket(CHANNEL_CONTROL);
}

// This command instructs SH-2 to set the current run-time sensor reorientation.
// The rotation vector is a signed, 16-bit 2’s-complement fixed point number with a Q-point of 14.
HAL_StatusTypeDef BNO_TareSetReorientation(const double X, const double Y, const double Z, const double W) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_TARE; // 0x03 – Tare command
	bufferIO[7] = COMMAND_TARE_REORIENT; // 0x02 – Set Reorientation
	*(int16_t *)&bufferIO[8] = (int16_t)(X * SCALE_TO_Q14);
	*(int16_t *)&bufferIO[10] = (int16_t)(Y * SCALE_TO_Q14);
	*(int16_t *)&bufferIO[12] = (int16_t)(Z * SCALE_TO_Q14);
	*(int16_t *)&bufferIO[14] = (int16_t)(W * SCALE_TO_Q14);
	return sendPacket(CHANNEL_CONTROL);
}

// Clears the previously applied tare operation.
HAL_StatusTypeDef BNO_ClearTare(void){
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_TARE; // 0x03 – Tare command
	bufferIO[7] = COMMAND_TARE_REORIENT; // 0x02 – Set Reorientation
	return sendPacket(CHANNEL_CONTROL);
}

#endif


// The sensor hub responds to the Initialize command with an Initialize Response. In the case
// where the sensor hub reinitializes itself, this response is unsolicited. An unsolicited response is
// also generated after startup
HAL_StatusTypeDef BNO_Initialize(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_INITIALIZE; // 0x04 – Initialize command
	bufferIO[7] = COMMAND_INITIALIZE_RESET; //1 Reinitialize the entire sensor hub.
	return waitForCommandResponse();
}

// Save Dynamic Calibration Data (DCD) to flash
HAL_StatusTypeDef BNO_saveCalibration(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_SAVE_DCD; // 0x06 – Save DCD Command
	saveDcdStatus = 1; // Set it as non 0
	if(waitForCommandResponse() == HAL_OK) {
		if(!saveDcdStatus) return HAL_OK;
	}
	return HAL_ERROR;
}

// This command is sent by the host to configure the ME calibration of the accelerometer, gyro and
// magnetometer giving the host the ability to control when calibration is performed.
HAL_StatusTypeDef BNO_condigureCalibration(const uint8_t sensors) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_ME_CALIBRATE; // 0x07 – ME Calibration Command
	//bufferIO[10] = 0; // 0x00 – Subcommand: Configure ME Calibration
	// Make the internal calStatus variable non-zero (fail)
	calibrationStatus.Status = 1;
	switch(sensors) {
		case CALIBRATE_ACCEL:
			bufferIO[7] = 1; // Accel Cal Enable (1 – enabled, 0 – disabled)
		break;
		case CALIBRATE_GYRO:
			bufferIO[8] = 1; // Gyro Cal Enable (1 – enabled, 0 – disabled)
		break;
		case CALIBRATE_MAG:
			bufferIO[9] = 1; // Mag Cal Enable (1 – enabled, 0 – disabled)
		break;
		case CALIBRATE_PLANAR_ACCEL:
			bufferIO[11] = 1; // Planar Accel Cal Enable (1 – enabled, 0 – disabled)
		break;
		case CALIBRATE_ON_TABLE:
			bufferIO[12] = 1; // On Table Cal Enable (1 – enabled, 0 – disabled)
		break;
		case CALIBRATE_ACCEL_GYRO_MAG:
			bufferIO[7] = bufferIO[8] = bufferIO[9] = 1;
		break;
		case CALIBRATE_ALL:
			bufferIO[7] = bufferIO[8] = bufferIO[9] = bufferIO[11] = bufferIO[12] = 1;
		break;
	}
	bufferIO[13] = ((sensors & 0x60) >> 5);
	if(waitForCommandResponse() == HAL_OK) {
		if(!calibrationStatus.Status) {
			return HAL_OK;
		}
	}
	return HAL_ERROR;
}

// This command is sent by the host to request the enable/disable state of the accelerometer, gyro and magnetometer calibration routines
HAL_StatusTypeDef BNO_enableCalibration(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_ME_CALIBRATE;
	bufferIO[10] = COMMAND_ME_CALIBRATE_GET;
	return waitForCommandResponse();
}

// The Configure Periodic DCD Save command configures the automatic saving of DCD. There is
// no response to this command. This command does not inhibit the Save DCD command.
// 0 Enable, 1 Disable
HAL_StatusTypeDef BNO_configurePeriodicDcdSave(const uint8_t enableStatus) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_DCD_PERIOD_SAVE; // 0x09 Configure Periodic DCD Save
	bufferIO[7] = enableStatus;
	return sendPacket(CHANNEL_CONTROL);
}


// The Get Oscillator Type command is used to get information about the oscillator type used in the clock system of the SH-2
BNO_Oscillator_t BNO_getOscillatorType(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_OSCILLATOR;
	if(waitForCommandResponse() == HAL_OK) {
		if(oscillatorType > ExternalClock) return OscillatorError;
	}
	return oscillatorType;
}

// This command performs an atomic clearDCD (from RAM) and system reset
HAL_StatusTypeDef BNO_clearDcdReset(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_CLEAR_DCD_RESET;
	if(waitForCommandResponse() == HAL_OK) {
		if(resetOccurred) {
			resetOccurred = 0;
			return HAL_OK;
		}
	}
	return HAL_ERROR;
}

// ToDo Check functionality Not getting a reponce to start
// See 6.4.10 Simple Calibration Commands (0x0C)
HAL_StatusTypeDef BNO_simpleCalibration(const uint32_t usInterval, const uint16_t calibrationTimeMs) {
	if(BNO_condigureCalibration(CALIBRATE_ON_TABLE) == HAL_OK) {
		resetHeader(REPORT_COMMAND_REQUEST);
		bufferIO[5] = cmdSeqNo++;
		bufferIO[6] = COMMAND_TURNTABLE_CAL; // 0x0C – Turntable Calibration
		//bufferIO[7] = 0x00; // 0x00 – Start Calibration
		*(uint32_t *)&bufferIO[8] = usInterval;
		if(waitForCommandResponse() == HAL_OK) {//CHANNEL_CONTROL, REPORT_COMMAND_RESPONSE
			if(!calibrationStatus.Status) {
				uint32_t endCalibrationTime = HAL_GetTick() + calibrationTimeMs;
				while(HAL_GetTick() < endCalibrationTime) {} // Just wait
				// Stop the calibration and get response	
				resetHeader(REPORT_COMMAND_REQUEST);
				bufferIO[5] = cmdSeqNo++;
				bufferIO[6] = COMMAND_TURNTABLE_CAL; // 0x0C – Turntable Calibration
				bufferIO[7] = 0x01; // 0x01 – Finish Calibration
				if(waitForCommandResponse() == HAL_OK) {	
					if(!calibrationStatus.Status) {
						return HAL_OK;
					}
				}
			}
		}
	}
	return HAL_ERROR;
}

// ToDo Check functionality Not getting a reponce to start
// The bootloader operating mode request is used to request various operating modes of the FSP200 bootloader
HAL_StatusTypeDef BNO_setBootMode(const BNO_BootMode_t mode) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_BOOTLOADER; // 0x0D – Bootloader command
	bufferIO[7] = COMMAND_BOOTLOADER_MODE_REQ; // 0x00 – Sub-command: Bootloader Operating Mode Request
	bufferIO[8] = mode; // Bootloader Operating Mode ID
	return sendPacket(CHANNEL_CONTROL);
}

// ToDo Check functionality Not getting a reponce to start
// Request product ID information about the FSP200 bootloader
BNO_BootMode_t BNO_getBootMode(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_BOOTLOADER; // 0x0D – Bootloader command
	bufferIO[7] = COMMAND_BOOTLOADER_STATUS_REQ; // 0x01 – Sub-command: Bootloader Status Request
	if(waitForCommandResponse() == HAL_OK) {
		return bootLoader.OperationMode;
	}
	return BootInvalid;
}

// The interactive calibration feature requires that the sensor hub be told of the device’s intended motion.
HAL_StatusTypeDef BNO_interactiveCalibration(const BNO_MotionIntent_t intent) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_INTERACTIVE_CAL_REQ; // 0x0E – Interactive Calibration command
	bufferIO[7] = intent; // Motion intent
	return sendPacket(CHANNEL_CONTROL);
}

#ifdef WHEEL_ENCODER
// Provide a single sample of wheel encoder data. No response is sent for this command.
HAL_StatusTypeDef BNO_WheelRequest(const uint8_t wheelIndex, const uint32_t timeStampUs, const int16_t wheelData, const uint8_t dataType) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_WHEEL_REQ; // 0x0F – Wheel Request
	bufferIO[7] = wheelIndex; // Wheel Index (0 = Left Wheel, 1 = Right Wheel)
	*(uint32_t *)&bufferIO[8] = timeStampUs; // The timestamp is a 32-bit unsigned timestamp in microseconds.
	*(int16_t *)&bufferIO[12] = wheelData; // The timestamp is a 32-bit unsigned timestamp in microseconds.
	bufferIO[14] = dataType; // Data Type (0 = Position, 1 = Velocity)
	return sendPacket(CHANNEL_CONTROL);
}
#endif

// This is sent from the host to the hub to trigger a flush of outstanding data from a given sensor
HAL_StatusTypeDef  BNO_forceFlush(const uint8_t sensorID) {
	resetHeader(REPORT_SENSOR_FLUSH_REQUEST);
	bufferIO[5] = sensorID;
	return waitForCommandResponse();
}

// This command is sent by the host to request the enable/disable state of the accelerometer, gyro
// and magnetometer calibration routines.
HAL_StatusTypeDef  BNO_getCalibrationStatus(void) {
	resetHeader(REPORT_COMMAND_REQUEST);
	bufferIO[5] = cmdSeqNo++;
	bufferIO[6] = COMMAND_ME_CALIBRATE;
	bufferIO[10] = 0x01; // 0x01 – Subcommand: Get ME Calibration
	return waitForCommandResponse();
}

// Check if calibration is complete
HAL_StatusTypeDef BNO_isCalibrationComplete(void) {
	if(!calibrationStatus.Status) {
		return HAL_OK;
	}
	return HAL_ERROR;
}

#ifdef USE_FOR_TELESCOPE

HAL_StatusTypeDef setTelescopeOrientation(void) {
    if(BNO_TareNow(TARE_ALL, RotationVector) == HAL_OK) {
        // -X North, Y East, Z Up
        if(BNO_TareSetReorientation(Q_FLIP, 0.0, 0.0, -Q_FLIP) == HAL_OK) {
            // Save new orientation and reset
            if(BNO_TarePerist() == HAL_OK) {
                if (BNO_Reset() == HAL_OK) {
                    // Optionally, check calibration completeness
                    return BNO_isCalibrationComplete();
                }
            }
        }
    }
    return HAL_ERROR;
}

// Start the calibration for 20s or until accuracy is 3
HAL_StatusTypeDef BNO_calibrateHighAccuracyAndReset(void) {
    const uint16_t calibrationTime = 26000;  // Adjust this value as needed
    if(BNO_condigureCalibration(CALIBRATE_ACCEL_GYRO_MAG) == HAL_OK) {
        if(BNO_setFeature(MAGNETIC_FIELD_CALIBRATED, 100000, 0) == HAL_OK) {
            if(BNO_setFeature(GAME_ROTATION_VECTOR, 100000, 0) == HAL_OK) {
                uint32_t startTime = HAL_GetTick() + calibrationTime;
                uint8_t magA = 0, grvA = 0;

                while(HAL_GetTick() < startTime) {
                    if ((BNO_dataAvailable() == HAL_OK) && sensorData.sensorId) {
                        if (sensorData.sensorId == MAGNETIC_FIELD_CALIBRATED) {
                            magA = sensorData.status;
                            // printf("MagA=%d\r\n", magA);  // Optional debug logging
                        }
                        if (sensorData.sensorId == GAME_ROTATION_VECTOR) {
                            grvA = sensorData.status;
                            // printf("GrvA=%d\r\n", grvA);  // Optional debug logging
                        }
                        sensorData.sensorId = 0;  // Reset the sensor ID after processing
                    }

                    // If both magnetometer and rotation vector have maximum accuracy, stop calibration
                    if ((magA == 3) && (grvA == 3)) {
                        break;
                    }
                }

                // Save calibration if completed successfully
                if (BNO_saveCalibration() == HAL_OK) {
                    return BNO_Reset();  // Reset after calibration
                }
            }
        }
    }
    return HAL_ERROR;  // Return error if any step fails
}
#endif
// Check if we have new data
HAL_StatusTypeDef BNO_dataAvailable(void) {
	if(waitForPacket() == HAL_OK) {
		return processResponse();
	}
	return HAL_ERROR;
}

// Enable features an set report time in mili seconds
BNO_Feature_t BNO_getFeature(const uint8_t sensorID) {
	resetHeader(REPORT_GET_FEATURE_REQUEST);
	BNO_Feature_t ret = {0};
	bufferIO[5] = sensorID; // Feature Report ID. 0x01 = Accelerometer, 0x05 = Rotation vector
	if(waitForCommandResponse() == HAL_OK) {
		ret = sensorFeartures;
	}
	return ret;
}

// Enable features an set report time in mili seconds
HAL_StatusTypeDef BNO_setFeature(const uint8_t sensorID, const uint32_t microsBetweenReports, const uint32_t specificConfig) {
	resetHeader(REPORT_SET_FEATURE_COMMAND); // Set feature command. Reference page 55
//	sensorFeartures.sensorID = sensorID;
//	sensorFeartures.flags = 0;
//	sensorFeartures.changeSensitivity = 0;
//	sensorFeartures.reportInterval_uS = microsBetweenReports;
//	sensorFeartures.batchInterval_uS = 0;
//	sensorFeartures.sensorSpecific = specificConfig;
//	*(BNO_Feature_t *)&bufferIO[5] = sensorFeartures;
	bufferIO[5] = sensorID; // Feature Report ID. 0x01 = Accelerometer, 0x05 = Rotation vector
	//bufferIO[6] = 0; // Feature flags
	//*(uint16_t *)&bufferIO[7] = 0; // Change sensitivity [absolute | relative] 
	*(uint32_t *)&bufferIO[9] = microsBetweenReports; // Report interval (LSB) in microseconds
	//*(uint32_t *)&bufferIO[13] = 0; // Batch Interval
	*(uint32_t *)&bufferIO[17] = specificConfig; // Sensor-specific config
	//Transmit packet on channel 2, 17 bytes
	return sendPacket(CHANNEL_CONTROL);
}

//
BNO_RollPitchYaw_t BNO_getRollPitchYaw(void) {
	return rpy;
}

#ifdef RAW_ACCELEROMETER
BNO_RawAccelerometer_t getRawAccelerometer(void) {
	return sensorData.SenVal.RawAccelerometer;
}
#endif
#ifdef ACCELEROMETER
BNO_Accelerometer_t getaccelerometer(void) {
	return sensorData.SenVal.Accelerometer;
}
#endif
#ifdef LINEAR_ACCELERATION
BNO_Accelerometer_t getLinearAcceleration(void) {
	return sensorData.SenVal.LinearAcceleration;
}
#endif
#ifdef GRAVITY
BNO_Accelerometer_t getGravity(void) {
	return sensorData.SenVal.Gravity;
}
#endif
#ifdef RAW_GYROSCOPE
BNO_RawGyroscope_t getRawGyroscope(void) {
	return sensorData.SenVal.RawGyroscope;
}
#endif
#ifdef GYROSCOPE_CALIBRATED
BNO_Gyroscope_t getGyroscope(void) {
	return sensorData.SenVal.Gyroscope;
}
#endif
#ifdef GYROSCOPE_UNCALIBRATED
BNO_GyroscopeUncalibrated_t getGyroscopeUncal(void) {
	return sensorData.SenVal.GyroscopeUncal;
}
#endif
#ifdef RAW_MAGNETOMETER
BNO_RawMagnetometer_t getRawMagnetometer(void) {
	return sensorData.SenVal.RawMagnetometer;
}
#endif
#ifdef MAGNETIC_FIELD_CALIBRATED
BNO_MagneticField_t getMagneticField(void) {
	return sensorData.SenVal.MagneticField;
}
#endif
#ifdef MAGNETIC_FIELD_UNCALIBRATED
BNO_MagneticFieldUncalibrated_t getMagneticFieldUncal(void) {
	return sensorData.SenVal.MagneticFieldUncal;
}
#endif
#ifdef ROTATION_VECTOR
BNO_RotationVectorWAcc_t getRotationVector(void) {
	return sensorData.SenVal.RotationVector;
}
#endif
#ifdef GAME_ROTATION_VECTOR
BNO_RotationVector_t getGameRotationVector(void) {
	return sensorData.SenVal.GameRotationVector;
}
#endif
#ifdef GEOMAGNETIC_ROTATION_VECTOR
BNO_RotationVectorWAcc_t getGeoMagRotationVector(void) {
	return sensorData.SenVal.GeoMagRotationVector;
}
#endif
#ifdef PRESSURE
float getPressure(void) {
	return sensorData.SenVal.Pressure; // Atmospheric Pressure.  [hectopascals]
}
#endif
#ifdef AMBIENT_LIGHT
float getAmbientLight(void) {
	return sensorData.SenVal.AmbientLight; // Ambient Light.  [lux]
}
#endif
#ifdef HUMIDITY
float getHumidity(void) {
	return sensorData.SenVal.Humidity; // Relative Humidity.  [percent]
}
#endif
#ifdef PROXIMITY
float getProximity(void) {
	return sensorData.SenVal.Proximity; // Proximity.  [cm]
}
#endif
#ifdef TEMPERATURE
float getTemperature(void) {
	return sensorData.SenVal.Temperature; // Temperature.  [C]
}
#endif
#ifdef RESERVED
float getReserved(void) {
	return sensorData.SenVal.Reserved;  // Reserved
}
#endif
#ifdef TAP_DETECTOR
BNO_Tap_t getTapDetectorFlag(void) {
	return sensorData.SenVal.TapDetectorFlag;
}
#endif
#ifdef STEP_DETECTOR
uint32_t getStepDetectorLatency(void) {
	return sensorData.SenVal.StepDetectorLatency; // Step detect latency [uS]
}
#endif
#ifdef STEP_COUNTER
BNO_StepCounter_t getStepCounter(void) {
	return sensorData.SenVal.StepCounter;
}
#endif
#ifdef SIGNIFICANT_MOTION
uint16_t getSignificantMotion(void) {
	return sensorData.SenVal.SignificantMotion;
}
#endif
#ifdef STABILITY_CLASSIFIER
BNO_Stability_t getStabilityClassifier(void) {
	return sensorData.SenVal.StabilityClassifier;
}
#endif
#ifdef SHAKE_DETECTOR
BNO_Shake_t getShakeDetector(void) {
	return sensorData.SenVal.ShakeDetector;
}
#endif
#ifdef FLIP_DETECTOR
uint16_t getFlipDetector(void) {
	return sensorData.SenVal.FlipDetector;
}
#endif
#ifdef PICKUP_DETECTOR
BNO_Pickup_t getPickupDetector(void) {
	return sensorData.SenVal.PickupDetector;
}
#endif
#ifdef STABILITY_DETECTOR
BNO_StabilityDetector_t getStabilityDetector(void) {
	return sensorData.SenVal.StabilityDetector;
}
#endif
#ifdef PERSONAL_ACTIVITY_CLASSIFIER
BNO_PersonalActivityClassifier_t getPersonalActivityClassifier(void) {
	return sensorData.SenVal.PersonalActivityClassifier;
}
#endif
#ifdef SLEEP_DETECTOR
uint8_t getSleepDetector(void) {
	return sensorData.SenVal.SleepDetector;
}
#endif
#ifdef TILT_DETECTOR
uint16_t getTiltDetector(void) {
	return sensorData.SenVal.TiltDetector;
}
#endif
#ifdef POCKET_DETECTOR
uint16_t getPocketDetector(void) {
	return sensorData.SenVal.PocketDetector;
}
#endif
#ifdef CIRCLE_DETECTOR
uint16_t getCircleDetector(void) {
	return sensorData.SenVal.CircleDetector;
}
#endif
#ifdef HEART_RATE_MONITOR
uint16_t getHeartRateMonitor(void) {
	return sensorData.SenVal.HeartRateMonitor; // Heart rate in beats per minute.
}
#endif
#ifdef ARVR_STABILIZED_RV
BNO_RotationVectorWAcc_t BNO_getArvrStabilizedRV(void) {
	return sensorData.SenVal.ArVrStabilizedRV;
}
#endif
#ifdef ARVR_STABILIZED_GRV
BNO_RotationVector_t BNO_getArVrStabilizedGRV(void) {
	return sensorData.SenVal.ArVrStabilizedGRV;
}
#endif
#ifdef GYRO_INTEGRATED_RV
BNO_GyroIntegratedRV_t BNO_getGyroIntegratedRV(void) {
	return sensorData.SenVal.GyroIntegratedRV;
}
#endif
#ifdef IZRO_MOTION_REQUEST
BNO_IZroRequest_t getIzroRequest(void) {
	return sensorData.SenVal.IzroRequest;
}
#endif
#ifdef RAW_OPTICAL_FLOW
BNO_RawOptFlow_t getRawOptFlow(void) {
	return sensorData.SenVal.RawOptFlow;
}
#endif
#ifdef DEAD_RECKONING_POSE
BNO_DeadReckoningPose_t getDeadReckoningPose(void) {
	return sensorData.SenVal.DeadReckoningPose;
}
#endif
#ifdef WHEEL_ENCODER
BNO_WheelEncoder_t getWheelEncoder(void) {
	return sensorData.SenVal.WheelEncoder;
}
#endif

Comments powered by CComment