Important Cookie Information

This website uses absolutely necessary cookies. If you consent to the use of convenience cookies, please click “Yes, I agree.” By clicking on “Privacy Settings.” you can change this setting at any time and withdraw your given consent. Icon down see more You can find detailled information in our Privacy Policy Icon forward-right
Icon up see less

Bosch Sensortec Community

    cancel
    Showing results for 
    Search instead for 
    Did you mean: 

    Python based sensor evaluation script using APP2.0

    100% helpful (2/2)

    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

    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.

    1. Upgrade pip
    2. Install pip:
      conda install pip
    3. After installing the environment, it needs to be activated:
      activate my35env
    4. 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.

    5. Run cmd.exe as admin
    6. Install the Anaconda python environment
    7. 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
      1. direct internet access: pip install --upgrade pip
      2. 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
    8. Install pythonnet:
      pip install --pre pythonnet
    9. 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:

    1. Connect the APP2.0 to the PC, mount the shuttle board, switch the board on
    2. open cmd.exe
    3. Activate the environment, e.g.
      activate my35env
    4. Open the IDE spyder:
      spyder
    5. Open the script using the IDE
    6. 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
    * 
    ****************************************************************************
    * Disclaimer
    *
    * Common:
    * Bosch Sensortec products are developed for the consumer goods industry.
    * They may only be used within the parameters of the respective valid
    * product data sheet.  Bosch Sensortec products are provided with the
    * express understanding that there is no warranty of fitness for a
    * particular purpose.They are not fit for use in life-sustaining,
    * safety or security sensitive systems or any system or device
    * that may lead to bodily harm or property damage if the system
    * or device malfunctions. In addition,Bosch Sensortec products are
    * not fit for use in products which interact with motor vehicle systems.
    * The resale and or use of products are at the purchasers own risk and
    * his own responsibility. The examination of fitness for the intended use
    * is the sole responsibility of the Purchaser.
    *
    * The purchaser shall indemnify Bosch Sensortec from all third party
    * claims, including any claims for incidental, or consequential damages,
    * arising from any product use not covered by the parameters of
    * the respective valid product data sheet or not approved by
    * Bosch Sensortec and reimburse Bosch Sensortec for all costs in
    * connection with such claims.
    *
    * The purchaser must monitor the market for the purchased products,
    * particularly with regard to product safety and inform Bosch Sensortec
    * without delay of all security relevant incidents.
    *
    * Engineering Samples are marked with an asterisk (*) or (e).
    * Samples may vary from the valid technical specifications of the product
    * series. They are therefore not intended or fit for resale to third
    * parties or for use in end products. Their sole purpose is internal
    * client testing. The testing of an engineering sample may in no way
    * replace the testing of a product series. Bosch Sensortec assumes
    * no liability for the use of engineering samples.
    * By accepting the engineering samples, the Purchaser agrees to indemnify
    * Bosch Sensortec from all claims arising from the use of engineering
    * samples.
    *
    * Special:
    * This software module (hereinafter called "Software") and any information
    * on application-sheets (hereinafter called "Information") is provided
    * free of charge for the sole purpose to support your application work.
    * The Software and Information is subject to the following
    * terms and conditions:
    *
    * The Software is specifically designed for the exclusive use for
    * Bosch Sensortec products by personnel who have special experience
    * and training. Do not use this Software if you do not have the
    * proper experience or training.
    *
    * This Software package is provided `` as is `` and without any expressed
    * or implied warranties,including without limitation, the implied warranties
    * of merchantability and fitness for a particular purpose.
    *
    * Bosch Sensortec and their representatives and agents deny any liability
    * for the functional impairment
    * of this Software in terms of fitness, performance and safety.
    * Bosch Sensortec and their representatives and agents shall not be liable
    * for any direct or indirect damages or injury, except as
    * otherwise stipulated in mandatory applicable law.
    *
    * The Information provided is believed to be accurate and reliable.
    * Bosch Sensortec assumes no responsibility for
     the consequences of use
    * of such Information nor for any infringement of patents or
    * other rights of third parties which may result from its use.
    * No license is granted by implication or otherwise under any patent or
    * patent rights of Bosch. Specifications mentioned in the Information are
    * subject to change without notice.
    **************************************************************************/
    
    """
    
    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(
    Version history
    Revision #:
    2 of 2
    Last update:
    ‎01-08-2019 01:50 PM
    Updated by:
     
    Contributors
    Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist