Purpose
This document describes the steps that need to be undertaken to setup a running python environment (at the moment limited to Windows systems), with which sensors from Bosch Sensortec can be read out using the Application Board APP2.0 and the shuttle boards.
Overview
The Application Board APP2.0 runs a firmware that allows connecting to it via USB and interface to a host PC. APP2.0 mainly serves as a protocol translator (like usb to i2c/spi) and has a dedicated streaming functionality for accurate sensor readout using the real-time capabilities of the MCU on the APP2.0.
On PC side, Bosch Sensortec provides a library for convenient access of the board using C# or python.
Requirements and material
PC or Laptop
Tested hardware / software: Windows 7 (64bit)
Software
Development Desktop 2.0 software from Bosch Sensortec web page: https://www.bosch-sensortec.com/bst/support_tools/downloads/overview_downloads
python environment (python 3 is recommended, but should also work with python 2), e.g. https://www.anaconda.com/download/#windows
python module: pythonnet (see details in instructions section)
Installation
Windows Platform
Assumption: the computer has a recent firefox web browser installed and has direct access to the internet. Furthermore, admin rights are required.
Upgrade pip
Install pip: conda install pip
After installing the environment, it needs to be activated: activate my35env
Create virtual environment (this is to prevent any issues with anaconda python and non-conda installed packages. For example name your virtual environment "my35env" with the command: conda create -n my35env anaconda python=3.5 Note: the script should also work with other 3.x versions of python. However, using version 3.5 enables to add guiqwt to a later moment, which may be a required module for displaying sensor data in a GUI format.
Run cmd.exe as admin
Install the Anaconda python environment
Install the Development Desktop 2.0 on the PC. The library UserApplicationBoard.dll that is required to access the APP2.0 can be found in this folder: C:\Program Files\Bosch Sensortec\Development Desktop 2.0\UserpApplicationBoard
direct internet access: pip install --upgrade pip
direct internet access: pip --proxy https://proxy.xyz.com:8080 install --upgrade pip Note: use the proxy settings for the rest of the commands as well if required Note: in some cases the proxy needs authentication, e.g. https://username:password@proxy.xyz.com:8080. In such a case, if the user uses special characters in his password, these characters must be written in html format, e.g. "%23" corresponds to the ASCII escape character of "#", details: https://www.ascii.cl/htmlcodes.htm
Install pythonnet: pip install --pre pythonnet
Copy the python script (see below) so some place
Linux
The board can also be interfaced using libusb, the required software is currently under development.
Operation
Based on the instructions and if the anaconda package is used, the following steps should be taken to run the script:
Connect the APP2.0 to the PC, mount the shuttle board, switch the board on
open cmd.exe
Activate the environment, e.g. activate my35env
Open the IDE spyder: spyder
Open the script using the IDE
Press the green triangle button (i.e. run the file)
Code example
# -*- coding: utf-8 -*-
"""
****************************************************************************
* Copyright (C) 2018 Bosch Sensortec GmbH
*
* Created 15.12.2017
*
* This script is intended to be used to check BMI085 using GenericAPI and
* shuttle board on APP2.0.
*
* Version changelog
* =================
* 0.9 (15.12.2017)
* - Initial version based on previous sample scripts
* 1.0 (18.12.2017)
* - Released after testing, small code modifications
* - Test signal function does not work, reason unknown
*
**************************************************************************** * BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE.
**************************************************************************/
"""
import sys
import clr
from time import sleep
sys.path.append(r"C:\Program Files\Bosch Sensortec\Development Desktop 2.0\UserpApplicationBoard") # path of dll
clr.AddReference("UserApplicationBoard")
from System import Byte
from System import UInt16
clr.AddReference('System.Collections')
import numpy as np
import BST
# Pin descriptions and other constants
PS_Gyro = 9
SCX = 6
SDX = 5
SDO = 4
BMI085_ShuttleID = 70
ACCEL_RANGE = 2 # BMI085: smallest Accel range is 2G (for BMI088 here would be 3G)
GYRO_RANGE = 2000 # BMI08x: "standard" gyro range
board = ''
# This class contains the relevant information about the sensor.
# In case of BMI08x, there are 2 class initializations neeeded, one
# for accelerometer, the other one for the gyroscope.
class Sensor:
def __init__(self, sensor_ID, i2c_addr, interface, CSB, asic):
self.sensor_ID = sensor_ID
self.i2c_addr = i2c_addr
self.interface = interface
self.CSB = CSB
self.asic = asic
self.firstdatareg = 0
self.rangereg = 0
self.fullrange = 0
if asic == 'baa450':
self.firstdatareg = 0x12
self.rangereg = 0x41
self.fullrange = ACCEL_RANGE
if asic == 'bag160':
self.firstdatareg = 0x02
self.rangereg = 0x0F
self.fullrange = GYRO_RANGE
def get_if_detail(self):
if self.interface == 'spi':
return self.CSB
elif self.interface == 'i2c':
return self.i2c_addr
else:
return -1
def set_if(self, interface):
self.interface = interface
# create single board instance over USB connection
def bst_init_brd_app20():
global board
board = BST.UserApplicationBoard()
board.PCInterfaceConfig( BST.PCINTERFACE.USB )
# print board info
bi = board.GetBoardInfo()
print('BoardInfo: HW/SW ID: ' + str(bi.HardwareId) + '/' + \
str(bi.SoftwareId) + ', ShuttleID: ' + str(bi.ShuttleID))
if bi.HardwareId == 0:
print('Seems like there is no board commincation. Stop')
board.ClosePCInterface()
sys.exit()
# if bi.ShuttleID != BMI085_ShuttleID:
# print('Seems like you are not using BMI085 shuttle board. Stop')
# board.ClosePCInterface()
# sys.exit()
# Set SPI bus
def brd_set_spi_if(sensor):
board.PinConfig(SCX, BST.EONOFF.ON, BST.PINMODE.OUTPUT, BST.PINLEVEL.HIGH)
board.PinConfig(SDX, BST.EONOFF.ON, BST.PINMODE.OUTPUT, BST.PINLEVEL.HIGH)
board.PinConfig(SDO, BST.EONOFF.ON, BST.PINMODE.INPUT, BST.PINLEVEL.HIGH)
board.SensorSPIConfig( Byte(sensor.sensor_ID), sensor.CSB, \
BST.SPISPEED.SPI1000KBIT, BST.SPIMODE.MODE0 )
board.PinConfig(sensor.CSB, BST.EONOFF.ON, BST.PINMODE.OUTPUT, \
BST.PINLEVEL.HIGH)
if sensor.asic == 'bag160':
board.PinConfig(PS_Gyro, BST.EONOFF.ON, BST.PINMODE.OUTPUT, \
BST.PINLEVEL.LOW) # ic2 select
# Set I2C bus
def brd_set_i2c_if(sensor):
# selecz ic2 addr
board.PinConfig(SDO, BST.EONOFF.ON, BST.PINMODE.OUTPUT, BST.PINLEVEL.LOW)
board.PinConfig(sensor.CSB, BST.EONOFF.OFF, BST.PINMODE.INPUT, \
BST.PINLEVEL.LOW)
if sensor.asic == 'bag160':
# selecz ic2 mode
board.PinConfig(PS_Gyro, BST.EONOFF.ON, BST.PINMODE.OUTPUT, \
BST.PINLEVEL.HIGH)
board.SensorI2CConfig( Byte(sensor.sensor_ID), UInt16(sensor.i2c_addr), \
BST.I2CSPEED.STANDARDMODE)
# Power up sequence fo the sensors
def power_up(sensor):
if sensor.asic == 'baa450':
# Wait times according to datasheet
sleep(.001)
board.Write(0x7D, 4, sensor.get_if_detail()) # write 4 to ACC_PWR_CTRL
board.Write(0x7C, 0, sensor.get_if_detail()) # Clear ACC_PWR_CONF
sleep(.05)
if sensor.asic == 'bag160':
brd_write(sensor, 0x11, [0])
pwr = brd_read(sensor, 0x11, 1)
print(pwr)
# Function to read from the previous selected bus
def brd_read(sensor, reg, range):
# Consider the dummy byte
if (sensor.asic == 'baa450') and (sensor.get_if_detail() == sensor.CSB):
range += 1
data = board.Read(reg & 0x7F, UInt16(range), sensor.get_if_detail())
data = [int(i) for i in data] # data is in Int32[] format
if (sensor.asic == 'baa450') and (sensor.get_if_detail() == sensor.CSB):
data.pop(0)
return data
# Function to write on the previous selected bus
def brd_write(sensor, reg, data):
return board.Write(reg & 0x7F, data, sensor.get_if_detail())
# Calculate 2's complement out of given uint value
def twos_comp(val, bits):
"""compute the 2's complement of int value val"""
if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255
val = val - (1 << bits) # compute negative value
return val # return positive value as is
# This function reads a number of samples, calculates the average and the
# standard deviation and outputs the result to the user
def read_sensor_values_3D(sensor, num_of_samples = 25, delay_in_ms = 0):
x = []
y = []
z = []
# Read in number of samples
while(num_of_samples > 0):
data = brd_read(sensor, sensor.firstdatareg, 6)
x.append(twos_comp(data[1] * 256 + data[0], 16))
y.append(twos_comp(data[3] * 256 + data[2], 16))
z.append(twos_comp(data[5] * 256 + data[4], 16))
sleep(delay_in_ms)
num_of_samples -= 1
return ([np.mean(x), np.mean(y), np.mean(z), np.std(x), np.std(y), np.std(z)])
# This function converts LSB to pyhsical measure (e.g G for accel or
# dps for gyro)
def convert_lsb_2_phys(sensor, data):
sensrange = brd_read(sensor, sensor.rangereg, 1)
print('Range is set to : ' + str(sensrange[0]))
result = []
for d in data:
result.append(d/32768. * sensor.fullrange * 2 ** (sensrange[0]))
return result
def read_temp_sensor_baa450(sensor):
if sensor.asic == 'baa450':
data = brd_read(sensor, 0x22, 2)
return(twos_comp(data[0] * 8 + (data[1] >> 5), 11) * 0.125 + 23)
# This function is not yet working reliably at the moment! (refers to V1.0)
def trigger_selftest(sensor):
if sensor.asic == 'baa450':
# Set 16G measurement range
brd_write(sensor, 0x41, 3)
# Set ODR 1.6kHz, normal mode:
brd_write(sensor, 0x40, 0xA7)
# Wait 2ms (see datasheet)
sleep(.003)
print('-> Enabling positive selftest signal')
brd_write(sensor, 0x6D, 0x0D)
# Wait >50ms (see datasheet)
sleep(0.051)
data = read_sensor_values_3D(bmi08x_accel)
data = convert_lsb_2_phys(bmi08x_accel, data)
print(data)
print('-> Enabling negative selftest signal')
brd_write(sensor, 0x6D, 0x09)
sleep(.1)
data = read_sensor_values_3D(bmi08x_accel)
data = convert_lsb_2_phys(bmi08x_accel, data)
print(data)
brd_write(sensor, 0x6D, 0x00)
sleep(.1)
# ############################################################################
# Main routine.
if __name__ == "__main__":
bst_init_brd_app20()
try:
# ##### SPI section #####
myif = 'spi'
print('\n +++ Interface: ' + myif + ' +++')
bmi08x_accel = Sensor(0x1F, 0x18, myif, 8, 'baa450')
bmi08x_gyro = Sensor(0x0F, 0x68, myif, 14, 'bag160')
board.SetVDD(0)
board.SetVDDIO(0)
brd_set_spi_if(bmi08x_gyro)
brd_set_spi_if(bmi08x_accel)
board.SetVDD(3.3)
board.SetVDDIO(3.3)
# Read chip-ID 2x for accelerometer to switch to spi mode
chipid = brd_read(bmi08x_accel, 0, 1)
# Read chip id
chipid = brd_read(bmi08x_accel, 0, 1)
print ('Chip ID accel: ' + str(chipid[0]))
chipid = brd_read(bmi08x_gyro, 0, 1)
print ('Chip ID gyro: ' + str(chipid[0]))
board.SetVDD(0)
board.SetVDDIO(0)
# ##### I2C section #####
myif = 'i2c'
print('\n +++ Interface: ' + myif + ' +++')
bmi08x_accel = Sensor(0x1F, 0x18, myif, 8, 'baa450')
bmi08x_gyro = Sensor(0x0F, 0x68, myif, 14, 'bag160')
brd_set_i2c_if(bmi08x_accel)
brd_set_i2c_if(bmi08x_gyro)
board.SetVDD(3.3)
board.SetVDDIO(3.3)
# Read chip id
chipid = brd_read(bmi08x_accel, 0, 1)
print ('Chip ID accel: ' + str(chipid[0]))
chipid = brd_read(bmi08x_gyro, 0, 1)
print ('Chip ID gyro: ' + str(chipid[0]))
# Read accel data
power_up(bmi08x_accel)
data = read_sensor_values_3D(bmi08x_accel)
data = convert_lsb_2_phys(bmi08x_accel, data)
print ('Accel x [mg]: %.2f\t(Noise RMS: %.2f)' % \
(data[0] * 1000, data[3] * 1000))
print ('Accel y [mg]: %.2f\t(Noise RMS: %.2f)' % \
(data[1] * 1000, data[4] * 1000))
print ('Accel z [mg]: %.2f\t(Noise RMS: %.2f)' % \
(data[2] * 1000, data[5] * 1000))
# Read gyro data
data = read_sensor_values_3D(bmi08x_gyro)
data = convert_lsb_2_phys(bmi08x_gyro, data)
print ('Gyro x [mg]: %.2f\t(Noise RMS: %.2f)' % \
(data[0], data[3]))
print ('Gyro y [mg]: %.2f\t(Noise RMS: %.2f)' % \
(data[1], data[4]))
print ('Gyro z [mg]: %.2f\t(Noise RMS: %.2f)' % \
(data[2], data[5]))
# Read temperature data
data = read_temp_sensor_baa450(bmi08x_accel)
print ('Temperature [degC]: %.2f' % data)
# Trigger selftest not yet working for unknown reason, not a sensor,
# but obvious a python/toolchain issue
#trigger_selftest(bmi08x_accel)
except Exception as e:
print('Fehler: ' + str(e))
board.SetVDD(0)
board.SetVDDIO(0)
board.ClosePCInterface(
... View more