Package: coinespy-test-only
coinespyallows users to access the Bosch Sensortec Application Board using PythonControl VDD and VDDIO of sensorConfigure SPI and I2C bus parametersRead and write into registers of sensors from Bosch Sensortec via SPI and I2CRead and write digital pins of the Application Board.Example for read data
RequirementsHW: https://www.bosch-sensortec.com/software-tools/tools/application-board-3-0/

SW: Windows/Linux/MacOS
Recommended using system 64bitThis is a simple example of BMI085 Interrupt streaming using coinespy:fromenumimportEnumimportcoinespyascpyimportbmi08x_commonasbmi08xfrombmi08x_commonimportBMI08XasBMI08X_CLSimporthelper_functionsashfuncclassBMI085(BMI08X_CLS):""" Child class of BMI08X_CLS with methods for interrupt streaming """def__init__(self,**kwargs):BMI08X_CLS.__init__(self,kwargs["bus"],kwargs["interface"])self.accel_stream_settings=dict(I2C_ADDR_PRIMARY=0x18,NO_OF_BLOCKS=2,REG_X_LSB=[0x12,0x00],NO_OF_DATA_BYTES=[6,1],CHANNEL_ID=1,CS_PIN=cpy.MultiIOPin.SHUTTLE_PIN_8.value,INT_PIN=cpy.MultiIOPin.SHUTTLE_PIN_21.value,INT_TIME_STAMP=1,HW_PIN_STATE=1,# SPI_TYPE = 0,# CLEAR_ON_WRITE = 0,# INTLINE_COUNT =0)self.gyro_stream_settings=dict(I2C_ADDR_PRIMARY=0x68,NO_OF_BLOCKS=2,REG_X_LSB=[0x02,0x00],NO_OF_DATA_BYTES=[6,1],CHANNEL_ID=2,CS_PIN=cpy.MultiIOPin.SHUTTLE_PIN_14.value,INT_PIN=cpy.MultiIOPin.SHUTTLE_PIN_22.value,INT_TIME_STAMP=1,HW_PIN_STATE=0,# SPI_TYPE = 0,# CLEAR_ON_WRITE = 0,# INTLINE_COUNT =0)# ACCEL Range set to 16Gself.accel_full_range=16#  GYRO Range set to 250 dpsself.gyro_full_range=250self.accel_stream_config,self.accel_data_blocks=self.set_stream_settings(self.accel_stream_settings,bmi08x.SensorType.ACCEL)self.gyro_stream_config,self.gyro_data_blocks=self.set_stream_settings(self.gyro_stream_settings,bmi08x.SensorType.GYRO)self.number_of_samples=10self.accel_int_config=self.set_accel_interrupt_cfg()self.gyro_int_config=self.set_gyro_interrupt_cfg()self.packet_counter={"ACCEL":1,"GYRO":1}self.lost_packets=[]self.lost_packet_count=0self.accel_stream_data_packets=[]self.gyro_stream_data_packets=[]defset_stream_settings(self,sensor:dict,sensor_type:bmi08x.SensorType):""" API to configure Stream and Data blocks """stream_config=cpy.StreamingConfig()data_blocks=cpy.StreamingBlocks()ifself.interface==cpy.SensorInterface.I2C:stream_config.Intf=cpy.SensorInterface.I2C.valuestream_config.I2CBus=cpy.I2CBus.BUS_I2C_0.valuestream_config.DevAddr=sensor["I2C_ADDR_PRIMARY"]elifself.interface==cpy.SensorInterface.SPI:stream_config.Intf=cpy.SensorInterface.SPI.valuestream_config.SPIBus=cpy.SPIBus.BUS_SPI_0.valuestream_config.CSPin=sensor["CS_PIN"]ifsensor_type==bmi08x.SensorType.ACCELandself.interface==cpy.SensorInterface.SPI:# extra dummy byte for SPIdummy_byte_offset=1else:dummy_byte_offset=0data_blocks.NoOfBlocks=sensor["NO_OF_BLOCKS"]foriinrange(0,data_blocks.NoOfBlocks):data_blocks.RegStartAddr[i]=sensor["REG_X_LSB"][i]data_blocks.NoOfDataBytes[i]=sensor["NO_OF_DATA_BYTES"][i]+\dummy_byte_offsetstream_config.IntTimeStamp=sensor["INT_TIME_STAMP"]stream_config.IntPin=sensor["INT_PIN"]stream_config.HwPinState=sensor["HW_PIN_STATE"]# stream_config.SPIType = sensor["SPI_TYPE"]# stream_config.ClearOnWrite = sensor["CLEAR_ON_WRITE"]# if stream_config.ClearOnWrite:#     stream_config.ClearOnWriteConfig.DummyByte = 0#     stream_config.ClearOnWriteConfig.StartAddress = 0#     stream_config.ClearOnWriteConfig.NumBytesToClear = 0# stream_config.IntlineCount = sensor["INTLINE_COUNT"]# for i in range(0, stream_config.IntlineCount):#     stream_config.IntlineInfo[i] = sensor["INTLINE_INFO"][i]return(stream_config,data_blocks)defsend_stream_settings(self,sensor:dict,sensor_type:Enum):""" API to send streaming settings to board based on Sensor type"""ifsensor_type==bmi08x.SensorType.ACCEL:ret=self.board.config_streaming(sensor["CHANNEL_ID"],self.accel_stream_config,self.accel_data_blocks)else:ret=self.board.config_streaming(sensor["CHANNEL_ID"],self.gyro_stream_config,self.gyro_data_blocks)returnretdefprint_accel_gyro_data(self,sensor:dict,sensor_type,sensor_data):chip_id=0""" To Display Accel and Gyro data after unit conversion"""ifsensor_data:stream_buffer,valid_sample_count=sensor_datadata=stream_bufferifdata:print(f'\n{"ACCEL"ifsensor_type==bmi08x.SensorType.ACCELelse"GYRO"}DATA\n')buffer_index=0foridxinrange(0,valid_sample_count):if(idx+5)<len(data):# First 4 bytes contain packet count infopacket_count=0shift_value=24forjinrange(4):packet_count|=data[buffer_index]<<shift_valuebuffer_index+=1shift_value-=8ifself.interface==cpy.SensorInterface.SPIandsensor_type==bmi08x.SensorType.ACCEL:# dummy bytebuffer_index+=1# Next 6 bytes contain sensor infox_data=hfunc.twos_comp((data[1+buffer_index]<<8)|data[0+buffer_index],16)y_data=hfunc.twos_comp((data[3+buffer_index]<<8)|data[2+buffer_index],16)z_data=hfunc.twos_comp((data[5+buffer_index]<<8)|data[4+buffer_index],16)buffer_index+=6ifsensor_type==bmi08x.SensorType.ACCEL:unit_converted_x_data=bmi08x.lsb_to_mps2(x_data,self.accel_full_range,16)unit_converted_y_data=bmi08x.lsb_to_mps2(y_data,self.accel_full_range,16)unit_converted_z_data=bmi08x.lsb_to_mps2(z_data,self.accel_full_range,16)elifsensor_type==bmi08x.SensorType.GYRO:unit_converted_x_data=bmi08x.lsb_to_dps(x_data,self.gyro_full_range,16)unit_converted_y_data=bmi08x.lsb_to_dps(y_data,self.gyro_full_range,16)unit_converted_z_data=bmi08x.lsb_to_dps(z_data,self.gyro_full_range,16)# Next 1 byte chip_idchip_id=data[buffer_index]buffer_index+=1# Next 6 bytes contain Time stampifsensor["INT_TIME_STAMP"]==1:time_stamp=0shift_value=40forjinrange(6):time_stamp|=data[buffer_index]<<shift_valuebuffer_index+=1shift_value-=8ifsensor_type==bmi08x.SensorType.ACCEL:print(f"Accel[{packet_count}] Acc_ms2_X :{unit_converted_x_data:+.3f}"f"\tAcc_ms2_Y :{unit_converted_y_data:+.3f}"f"\tAcc_ms2_Z :{unit_converted_z_data:+.3f}"f"\tChip_id :{chip_id}T(us):{time_stamp}")elifsensor_type==bmi08x.SensorType.GYRO:print(f"Gyro[{packet_count}] Gyr_DPS_X :{unit_converted_x_data:+3.2f}"f"\tGyr_DPS_Y :{unit_converted_y_data:+3.2f}"f"\tGyr_DPS_Z :{unit_converted_z_data:+3.2f}"f"\tChip_id :{chip_id}T(us):{time_stamp}")defset_accel_interrupt_cfg(self):""" Set accel interrupt configurations """accel_int_config=dict()# Set accel interrupt pin configurationint_types=bmi08x.Bmi08xAccelIntTypesint_channels=bmi08x.Bmi08xAccelIntChannelaccel_int_config['int_channel']=int_channels.BMI08X_INT_CHANNEL_1accel_int_config['int_type']=int_types.BMI08X_ACCEL_INT_DATA_RDYaccel_int_config['int_pin_cfg']=dict()accel_int_config['int_pin_cfg']['output_mode']=bmi08x.BMI08X_INT_MODE_PUSH_PULLaccel_int_config['int_pin_cfg']['lvl']=bmi08x.BMI08X_INT_ACTIVE_HIGHreturnaccel_int_configdefset_gyro_interrupt_cfg(self):""" Set gyro interrupt configurations """gyro_int_config=dict()# Set gyro interrupt pinint_types=bmi08x.Bmi08xGyroIntTypesint_channels=bmi08x.Bmi08xGyroIntChannelgyro_int_config['int_channel']=int_channels.BMI08X_INT_CHANNEL_3gyro_int_config['int_type']=int_types.BMI08X_GYRO_INT_DATA_RDYgyro_int_config['int_pin_cfg']=dict()gyro_int_config['int_pin_cfg']['output_mode']=bmi08x.BMI08X_INT_MODE_PUSH_PULLgyro_int_config['int_pin_cfg']['lvl']=bmi08x.BMI08X_INT_ACTIVE_HIGHreturngyro_int_configdefenable_disable_bmi08x_interrupt(self,enable_interrupt:bool):"""API to enable interrupt if enable_interrupt is True and disable otherwiseArgs:enable_interrupt (bool):"""self.accel_int_config['int_pin_cfg']['enable_int_pin']=bmi08x.BMI08X_ENABLE\ifenable_interruptelsebmi08x.BMI08X_DISABLE# Enable accel data ready interrupt channelself.api_error_code=bmi08x.bmi08a_set_int_config(self,self.accel_int_config)self.verify_api_error("Accel Interrupt Config")self.gyro_int_config['int_pin_cfg']['enable_int_pin']=bmi08x.BMI08X_ENABLE\ifenable_interruptelsebmi08x.BMI08X_DISABLE# Enable gyro data ready interrupt channelself.api_error_code=bmi08x.bmi08g_set_int_config(self,self.gyro_int_config)self.verify_api_error("Gyro Interrupt Config")definterrupt_streaming(self):""" API to execute interrupt streaming sequence"""accel_read_status=cpy.ErrorCodes.COINES_E_COMM_IO_ERRORgyro_read_status=cpy.ErrorCodes.COINES_E_COMM_IO_ERROR# Send streaming settingsself.board.error_code=self.send_stream_settings(self.accel_stream_settings,bmi08x.SensorType.ACCEL)self.verify_error("Accel Stream settings")self.board.error_code=self.send_stream_settings(self.gyro_stream_settings,bmi08x.SensorType.GYRO)self.verify_error("Gyro Stream settings")# Enables 48-bit system timerself.board.error_code=self.board.trigger_timer(cpy.TimerConfig.TIMER_START.value,cpy.TimerStampConfig.TIMESTAMP_ENABLE.value)self.verify_error("Start timer")# Wait for 10 msself.board.delay_milli_sec(10)# Enable data ready interruptsself.enable_disable_bmi08x_interrupt(enable_interrupt=True)# Start interrupt streamingself.board.error_code=self.board.start_stop_streaming(cpy.StreamingMode.STREAMING_MODE_INTERRUPT.value,cpy.StreamingState.STREAMING_START.value)self.verify_error("Start Interrupt streaming")# Read sensor data via interrupt streaming(self.board.error_code,accel_stream_buffer,accel_valid_sample_count)=self.board.read_stream_sensor_data(self.accel_stream_settings["CHANNEL_ID"],self.number_of_samples)accel_read_status=self.board.error_codeself.verify_error("Read Accel stream data")ifself.board.error_code==cpy.ErrorCodes.COINES_SUCCESS:(self.board.error_code,gyro_stream_buffer,gyro_valid_sample_count)=self.board.read_stream_sensor_data(self.gyro_stream_settings["CHANNEL_ID"],self.number_of_samples)gyro_read_status=self.board.error_codeself.verify_error("Read Gyro stream data")# Stop interrupt streamingself.board.error_code=self.board.start_stop_streaming(cpy.StreamingMode.STREAMING_MODE_INTERRUPT.value,cpy.StreamingState.STREAMING_STOP.value)self.verify_error("Stop Interrupt streaming")# Stop Timerself.board.error_code=self.board.trigger_timer(cpy.TimerConfig.TIMER_STOP.value,cpy.TimerStampConfig.TIMESTAMP_DISABLE.value)self.verify_error("Stop timer")# print the streamed accel & gyro dataifaccel_read_status==cpy.ErrorCodes.COINES_SUCCESS:accel_data=[accel_stream_buffer,accel_valid_sample_count]self.print_accel_gyro_data(self.accel_stream_settings,bmi08x.SensorType.ACCEL,accel_data)ifgyro_read_status==cpy.ErrorCodes.COINES_SUCCESS:gyro_data=[gyro_stream_buffer,gyro_valid_sample_count]self.print_accel_gyro_data(self.gyro_stream_settings,bmi08x.SensorType.GYRO,gyro_data)# flush the bufferself.board.flush_interface()# Wait for 100 msself.board.delay_milli_sec(100)# Disable data ready interruptsself.enable_disable_bmi08x_interrupt(enable_interrupt=False)# Parse sensor stream dataself.accel_stream_data_packets=self.parse_read_data(accel_stream_buffer,accel_valid_sample_count,bmi08x.SensorType.ACCEL)self.gyro_stream_data_packets=self.parse_read_data(gyro_stream_buffer,gyro_valid_sample_count,bmi08x.SensorType.GYRO)# Analyze data packets lossself.analyze_data_loss(self.accel_stream_data_packets,bmi08x.SensorType.ACCEL)self.analyze_data_loss(self.gyro_stream_data_packets,bmi08x.SensorType.GYRO)defparse_read_data(self,read_data,samples_count,sensor_type):"""This function is used to parse the read data to packets"""index=0streamed_data=[]ifself.interface==cpy.SensorInterface.SPIandsensor_type==bmi08x.SensorType.ACCEL:packet_len=19else:packet_len=17forsampleinrange(samples_count):# packet_len computationstreamed_data.append(read_data[index:index+packet_len])index+=packet_lenreturnstreamed_datadefcheck_for_packet_loss(self,sensor_name,packet_cnt):"""This function is to count and track lost packets"""ifpacket_cnt==self.packet_counter[sensor_name]:self.packet_counter[sensor_name]+=1else:self.lost_packet_count=packet_cnt-\self.packet_counter[sensor_name]foriinrange(self.lost_packet_count):self.lost_packets.append(self.packet_counter[sensor_name]+i)# Data loss increment the counter to check next packetself.packet_counter[sensor_name]=packet_cnt+1defprint_data_loss(self,sensor_type):"""Print data loss in streamed packets"""print(f"\n<-----------------{sensor_type.name}Streaming Benchmark Result------------------>")ifself.packet_counter[sensor_type.name]>1:loss_percent=(self.lost_packet_count/self.packet_counter[sensor_type.name])*100print(f'{sensor_type.name}data loss -{loss_percent:.5f}%, 'f'total packets received -{self.packet_counter[sensor_type.name]}, 'f'packets lost -{self.lost_packet_count}')else:print(f"{sensor_type.name}streaming not configured")defanalyze_data_loss(self,streamed_data,sensor_type):"""Analyze data loss for a specific sensor type"""self.lost_packets=[]self.lost_packet_count=0fordatainstreamed_data:packet_cnt=int.from_bytes(bytes(data[0:4]),"big",signed=False)self.check_for_packet_loss(sensor_type.name,packet_cnt)self.print_data_loss(sensor_type)if__name__=="__main__":bmi085=BMI085(bus=cpy.I2CBus.BUS_I2C_0,interface=cpy.SensorInterface.I2C)# bmi085 = BMI085(bus=cpy.SPIBus.BUS_SPI_0,interface=cpy.SensorInterface.SPI)bmi085.init_board()# Change sensor config settings as shown below# bmi085.accel_stream_config.I2CBus = cpy.I2CBus.BUS_I2C_0.value# bmi085.number_of_samples = 30# # Change sensor interrupt config settings as shown below# bmi085.accel_int_config['int_channel'] = bmi08x.Bmi08xAccelIntChannel.BMI08X_INT_CHANNEL_1# Set Accel power, ODR and range settingsbmi085.set_accel_power_mode()acc_range=bmi085.set_accel_meas_conf()# Set Gyro power, ODR and range settingsbmi085.set_gyro_power_mode()gyr_range=bmi085.set_gyro_meas_conf()print(f"Accel range:{acc_range}Gyro range:{gyr_range}")ifbmi085.accel_cfg['POWER_MODE']==bmi08x.BMI08X_ACCEL_PM_SUSPENDand\(bmi085.gyro_cfg['POWER_MODE']==bmi08x.BMI08X_GYRO_PM_SUSPENDorbmi085.gyro_cfg['POWER_MODE']==bmi08x.BMI08X_GYRO_PM_DEEP_SUSPEND):print("Warning: Accel and gyro sensors are in suspend mode. Use them in active/normal mode !!")try:bmi085.interrupt_streaming()exceptExceptionase:print(f"Exception:{e}")bmi085.close_comm()bmi085.close_comm()