• sales

    +86-0755-88291180

RPi Tutorial Series: I2C Programming User Guide

I believe you have a certain understanding about the I2C on RPi. Here We continue to control BMP180 via I2C. First the program reads calibration data (such as AC1, AC2, AC3, AC4, AC5, AC6, B1, B2, MB, MC and MD) from the EEPROM of the BMP180. Then reads uncompensated temperature value and pressure value and calculates the true value with the calibration data.

There are many libraries for Raspberry Pi I2C programming. We recommend BCM2835, WiringPi and smbus Python.


BMP180 Example

These examples require a certain library, see: Libraries Installation for RPi

in Python (import smbus)

Before using this program, you should install the smbus library for Python. Get it by apt-get command:

sudo apt-get install python-smbus

Save the following program and run with:

sudo python BMP180_example.py


This is a BMP180 driver written in Python.

Save the file as "BMP180.py"

import time
import smbus
 
# BMP085 default address.
BMP180_I2CADDR           = 0x77
 
# Operating Modes
BMP180_ULTRALOWPOWER     = 0
BMP180_STANDARD          = 1
BMP180_HIGHRES           = 2
BMP180_ULTRAHIGHRES      = 3
 
# BMP085 Registers
BMP180_CAL_AC1           = 0xAA  # R   Calibration data (16 bits)
BMP180_CAL_AC2           = 0xAC  # R   Calibration data (16 bits)
BMP180_CAL_AC3           = 0xAE  # R   Calibration data (16 bits)
BMP180_CAL_AC4           = 0xB0  # R   Calibration data (16 bits)
BMP180_CAL_AC5           = 0xB2  # R   Calibration data (16 bits)
BMP180_CAL_AC6           = 0xB4  # R   Calibration data (16 bits)
BMP180_CAL_B1            = 0xB6  # R   Calibration data (16 bits)
BMP180_CAL_B2            = 0xB8  # R   Calibration data (16 bits)
BMP180_CAL_MB            = 0xBA  # R   Calibration data (16 bits)
BMP180_CAL_MC            = 0xBC  # R   Calibration data (16 bits)
BMP180_CAL_MD            = 0xBE  # R   Calibration data (16 bits)
BMP180_CONTROL           = 0xF4
BMP180_TEMPDATA          = 0xF6
BMP180_PRESSUREDATA      = 0xF6
 
# Commands
BMP180_READTEMPCMD       = 0x2E
BMP180_READPRESSURECMD   = 0x34
 
 
class BMP180(object):
    def __init__(self, address=BMP180_I2CADDR, mode=BMP180_STANDARD):
        self._mode = mode
        self._address = address
        self._bus = smbus.SMBus(1)
        # Load calibration values.
        self._load_calibration()
    def _read_byte(self,cmd):
        return self._bus.read_byte_data(self._address,cmd)
 
    def _read_u16(self,cmd):
        MSB = self._bus.read_byte_data(self._address,cmd)
        LSB = self._bus.read_byte_data(self._address,cmd+1)
        return (MSB << 8) + LSB
 
    def _read_s16(self,cmd):
        result = self._read_u16(cmd)
        if result > 32767:result -= 65536
        return result
 
    def _write_byte(self,cmd,val):
        self._bus.write_byte_data(self._address,cmd,val)
 
    def _load_calibration(self):
        "load calibration"
        self.cal_AC1 = self._read_s16(BMP180_CAL_AC1)   # INT16
        self.cal_AC2 = self._read_s16(BMP180_CAL_AC2)   # INT16
        self.cal_AC3 = self._read_s16(BMP180_CAL_AC3)   # INT16
        self.cal_AC4 = self._read_u16(BMP180_CAL_AC4)   # UINT16
        self.cal_AC5 = self._read_u16(BMP180_CAL_AC5)   # UINT16
        self.cal_AC6 = self._read_u16(BMP180_CAL_AC6)   # UINT16
        self.cal_B1  = self._read_s16(BMP180_CAL_B1)     # INT16
        self.cal_B2  = self._read_s16(BMP180_CAL_B2)     # INT16
        self.cal_MB  = self._read_s16(BMP180_CAL_MB)     # INT16
        self.cal_MC  = self._read_s16(BMP180_CAL_MC)     # INT16
        self.cal_MD  = self._read_s16(BMP180_CAL_MD)     # INT16
 
    def read_raw_temp(self):
        """Reads the raw (uncompensated) temperature from the sensor."""
        self._write_byte(BMP180_CONTROL, BMP180_READTEMPCMD)
        time.sleep(0.005)  # Wait 5ms
        MSB = self._read_byte(BMP180_TEMPDATA)
        LSB = self._read_byte(BMP180_TEMPDATA+1)
        raw = (MSB << 8) + LSB
        return raw
 
    def read_raw_pressure(self):
        """Reads the raw (uncompensated) pressure level from the sensor."""
        self._write_byte(BMP180_CONTROL, BMP180_READPRESSURECMD + (self._mode << 6))
        if self._mode == BMP180_ULTRALOWPOWER:
            time.sleep(0.005)
        elif self._mode == BMP180_HIGHRES:
            time.sleep(0.014)
        elif self._mode == BMP180_ULTRAHIGHRES:
            time.sleep(0.026)
        else:
            time.sleep(0.008)
        MSB = self._read_byte(BMP180_PRESSUREDATA)
        LSB = self._read_byte(BMP180_PRESSUREDATA+1)
        XLSB = self._read_byte(BMP180_PRESSUREDATA+2)
        raw = ((MSB << 16) + (LSB << 8) + XLSB) >> (8 - self._mode)
        return raw
 
    def read_temperature(self):
        """Gets the compensated temperature in degrees celsius."""
        UT = self.read_raw_temp()
 
        X1 = ((UT - self.cal_AC6) * self.cal_AC5) >> 15
        X2 = (self.cal_MC << 11) / (X1 + self.cal_MD)
        B5 = X1 + X2
        temp = ((B5 + 8) >> 4) / 10.0
        return temp
 
    def read_pressure(self):
        """Gets the compensated pressure in Pascals."""
        UT = self.read_raw_temp()
        UP = self.read_raw_pressure()
 
 
        X1 = ((UT - self.cal_AC6) * self.cal_AC5) >> 15
        X2 = (self.cal_MC << 11) / (X1 + self.cal_MD)
        B5 = X1 + X2
 
        # Pressure Calculations
        B6 = B5 - 4000
        X1 = (self.cal_B2 * (B6 * B6) >> 12) >> 11
        X2 = (self.cal_AC2 * B6) >> 11
        X3 = X1 + X2
        B3 = (((self.cal_AC1 * 4 + X3) << self._mode) + 2) / 4
 
        X1 = (self.cal_AC3 * B6) >> 13
        X2 = (self.cal_B1 * ((B6 * B6) >> 12)) >> 16
        X3 = ((X1 + X2) + 2) >> 2
        B4 = (self.cal_AC4 * (X3 + 32768)) >> 15
        B7 = (UP - B3) * (50000 >> self._mode)
 
        if B7 < 0x80000000:
            p = (B7 * 2) / B4
        else:
            p = (B7 / B4) * 2
        X1 = (p >> 8) * (p >> 8)
        X1 = (X1 * 3038) >> 16
        X2 = (-7357 * p) >> 16
 
        p = p + ((X1 + X2 + 3791) >> 4)
        return p
 
    def read_altitude(self, sealevel_pa=101325.0):
        """Calculates the altitude in meters."""
        # Calculation taken straight from section 3.6 of the datasheet.
        pressure = float(self.read_pressure())
        altitude = 44330.0 * (1.0 - pow(pressure / sealevel_pa, (1.0/5.255)))
        return altitude
 
    def read_sealevel_pressure(self, altitude_m=0.0):
        """Calculates the pressure at sealevel when given a known altitude in
        meters. Returns a value in Pascals."""
        pressure = float(self.read_pressure())
        p0 = pressure / pow(1.0 - altitude_m/44330.0, 5.255)
        return p0


This is an example working with the BMP180 driver (written in Python).

Save the file as "BMP180_example.py"

#!/usr/bin/python
 
import time
from BMP180 import BMP180
 
# Initialise the BMP085 and use STANDARD mode (default value)
# bmp = BMP085(0x77, debug=True)
bmp = BMP180()
 
# To specify a different operating mode, uncomment one of the following:
# bmp = BMP085(0x77, 0)  # ULTRALOWPOWER Mode
# bmp = BMP085(0x77, 1)  # STANDARD Mode
# bmp = BMP085(0x77, 2)  # HIRES Mode
# bmp = BMP085(0x77, 3)  # ULTRAHIRES Mode
while True:
    temp = bmp.read_temperature()
 
# Read the current barometric pressure level
    pressure = bmp.read_pressure()
 
# To calculate altitude based on an estimated mean sea level pressure
# (1013.25 hPa) call the function as follows, but this won't be very accurate
    altitude = bmp.read_altitude()
 
# To specify a more accurate altitude, enter the correct mean sea level
# pressure level.  For example, if the current pressure level is 1023.50 hPa
# enter 102350 since we include two decimal places in the integer value
# altitude = bmp.readAltitude(102350)
 
    print "Temperature: %.2f C" % temp
    print "Pressure:    %.2f hPa" % (pressure / 100.0)
    print "Altitude:     %.2f\n" % altitude
time.sleep(1)

in C (include bcm2835.h)

You can compile the C program with:

gcc -Wall bmp180.c -o bmp180 -lbcm2835 -lm

and run with:

sudo ./bmp180


This is a BMP180 example written with bcm2835.h

Save the file as "bmp180.c"

#include <bcm2835.h>
#include <stdio.h>
#include <math.h>
#include "bmp180.h"

#define OSS BMP180_STANDARD
short AC1,AC2,AC3,B1,B2,MB,MC,MD;
unsigned short AC4,AC5,AC6;

char I2C_readByte(char reg)
{
    char buf[] = {reg};
    bcm2835_i2c_read_register_rs(buf,buf,1);
    return buf[0];
}
unsigned short I2C_readU16(char reg)
{
    char buf[] = {reg,0};
    bcm2835_i2c_read_register_rs(buf,buf,2);
    int value =  buf[0]*0x100+buf[1];
    return value;
}
short I2C_readS16(char reg)
{
    int result;
    result = I2C_readU16(reg);
    if (result > 32767)result -= 65536;
    return (short)result;
}
void I2C_writeByte(char reg,char val)
{
    char buf[] = {reg,val};
    bcm2835_i2c_write(buf,2);
}

void load_calibration()
{
    AC1 = I2C_readS16(BMP180_CAL_AC1);
    AC2 = I2C_readS16(BMP180_CAL_AC2);
    AC3 = I2C_readS16(BMP180_CAL_AC3);
    AC4 = I2C_readU16(BMP180_CAL_AC4);
    AC5 = I2C_readU16(BMP180_CAL_AC5);
    AC6 = I2C_readU16(BMP180_CAL_AC6);
    B1  = I2C_readS16(BMP180_CAL_B1);
    B2  = I2C_readS16(BMP180_CAL_B2);
    MB  = I2C_readS16(BMP180_CAL_MB);
    MC  = I2C_readS16(BMP180_CAL_MC);
    MD  = I2C_readS16(BMP180_CAL_MD);
}

int read_raw_temp()
{
    int raw;
    I2C_writeByte(BMP180_CONTROL,BMP180_READTEMPCMD);
    bcm2835_delay(5); //5ms
    raw = I2C_readByte(BMP180_TEMPDATA) << 8;
    raw += I2C_readByte(BMP180_TEMPDATA+1);
    return raw;
}

int read_raw_pressure()
{
    int MSB,LSB,XLSB,raw;
    I2C_writeByte(BMP180_CONTROL,BMP180_READPRESSURECMD +(OSS << 6));
    switch(OSS)
    {
        case BMP180_ULTRALOWPOWER:
            bcm2835_delay(5);break;
        case BMP180_HIGHRES:
            bcm2835_delay(14);break;
        case BMP180_ULTRAHIGHRES:
            bcm2835_delay(26);break;
        default :
            bcm2835_delay(8);
    }
    MSB  = I2C_readByte(BMP180_PRESSUREDATA);
    LSB  = I2C_readByte(BMP180_PRESSUREDATA + 1);
    XLSB = I2C_readByte(BMP180_PRESSUREDATA + 2);
    raw = ((MSB << 16) + (LSB << 8) + XLSB) >> (8 - OSS);
    return raw;
}
float read_temperature()
{
    float T;
    int UT,X1,X2,B5;
    UT = read_raw_temp();
    X1 = ((UT - AC6)*AC5) >> 15;
    X2 = (MC << 11) / (X1 + MD);
    B5 = X1 + X2;
    T = ((B5 + 8) >> 4) /10.0;
    return T;
}

int read_pressure()
{
    int P;
    int UT,UP,X1,X2,X3,B3,B5,B6;
    unsigned int B4;
    int B7;
    UT = read_raw_temp();
    UP = read_raw_pressure();

    X1 = ((UT - AC6)*AC5) >> 15;
    X2 = (MC << 11) / (X1 + MD);
    B5 = X1 + X2;

    //Pressure Calculations
    B6 = B5 - 4000;
    X1 = (B2 * (B6 * B6) >> 12) >> 11;
    X2 = (AC2 * B6) >> 11;
    X3 = X1 + X2;
    B3 = (((AC1 * 4 + X3) << OSS) + 2) / 4;
    X1 = (AC3 * B6) >> 13;
    X2 = (B1 * ((B6 * B6) >> 12)) >> 16;
    X3 = ((X1 + X2) + 2) >> 2;
    B4 = (AC4 * (X3 + 32768)) >> 15;
    B7 = (UP - B3) * (50000 >> OSS);
    if (B7 < 0x80000000){P = (B7 * 2) / B4;}
    else {P = (B7 / B4) * 2;}
    X1 = (P >> 8) * (P >> 8);
    X1 = (X1 * 3038) >> 16;
    X2 = (-7357 * P) >> 16;
    P = P + ((X1 + X2 + 3791) >> 4);
    return P;

}
float read_altitude()
{
    float pressure,altitude;
    float sealevel_pa = 101325.0;
    pressure = (float)read_pressure();
    altitude = 44330.0 * (1.0 - pow(pressure / sealevel_pa,(1.0/5.255)));
    return altitude;
}
float read_sealevel_pressure()
{
    float altitude_m = 0.0;
    float pressure,p0;
    pressure =(float)read_pressure();
    p0 = pressure / pow(1.0 - altitude_m/44330.0,5.255);
    return p0;
}
int main(int argc,char **argv)
{
    printf("BMP180 Test Program ...\n");
    if(!bcm2835_init()) return 1;
    bcm2835_i2c_begin();
    bcm2835_i2c_setSlaveAddress(BMP180_Address);
    bcm2835_i2c_set_baudrate(10000);
    load_calibration();
    while(1)
    {
        printf("\nTemperature : %.2f C\n",read_temperature());
        printf("Pressure :    %.2f Pa\n",read_pressure()/100.0);
        printf("Altitude :    %.2f h\n",read_altitude());
        bcm2835_delay(1000);
    }
    return 0;
}


                The head file.

Save the file as "bmp180.h"

#ifndef _BMP180_
#define _BMP180_

//i2c address
#define BMP180_Address 0x77

//Operating Modes
#define BMP180_ULTRALOWPOWER    0
#define BMP180_STANDARD         1
#define BMP180_HIGHRES          2
#define BMP180_ULTRAHIGHRES     3

//BMP185 Registers
#define BMP180_CAL_AC1          0xAA  //Calibration data (16 bits)
#define BMP180_CAL_AC2          0xAC  //Calibration data (16 bits)
#define BMP180_CAL_AC3          0xAE  //Calibration data (16 bits)
#define BMP180_CAL_AC4          0xB0  //Calibration data (16 bits)
#define BMP180_CAL_AC5          0xB2  //Calibration data (16 bits)
#define BMP180_CAL_AC6          0xB4  //Calibration data (16 bits)
#define BMP180_CAL_B1           0xB6  //Calibration data (16 bits)
#define BMP180_CAL_B2           0xB8  //Calibration data (16 bits)
#define BMP180_CAL_MB           0xBA  //Calibration data (16 bits)
#define BMP180_CAL_MC           0xBC  //Calibration data (16 bits)
#define BMP180_CAL_MD           0xBE  //Calibration data (16 bits)
#define BMP180_CONTROL          0xF4
#define BMP180_TEMPDATA         0xF6
#define BMP180_PRESSUREDATA     0xF6

//Commands
#define BMP180_READTEMPCMD      0x2E
#define BMP180_READPRESSURECMD  0x34

#endif


in C (include wiringPi.h and wiringPiI2C.h)

You can compile the C program with:

gcc -Wall bmp180.c -o bmp180 -lwiringPi -lm

and run with:

sudo ./bmp180


This is a BMP180 example written with wiringPi.h and wiringPiI2C.h

Save the file as "bmp180.c"

#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <stdio.h>
#include <math.h>
#include "bmp180.h"

#define OSS BMP180_STANDARD
short AC1,AC2,AC3,B1,B2,MB,MC,MD;
unsigned short AC4,AC5,AC6;
int fd;
char I2C_readByte(int reg)
{
    return (char)wiringPiI2CReadReg8(fd,reg);
}

unsigned short I2C_readU16(int reg)
{
    int MSB,LSB;
    MSB = I2C_readByte(reg);
    LSB = I2C_readByte(reg + 1);
    int value = (MSB << 8) +LSB;
    return (unsigned short)value;
}

short I2C_readS16(int reg)
{
    int result;
    result = I2C_readU16(reg);
    if (result > 32767)result -= 65536;
    return (short)result;
}
void I2C_writeByte(int reg,int val)
{
    wiringPiI2CWriteReg8(fd,reg,val);
}

void load_calibration()
{
    AC1 = I2C_readS16(BMP180_CAL_AC1);
    AC2 = I2C_readS16(BMP180_CAL_AC2);
    AC3 = I2C_readS16(BMP180_CAL_AC3);
    AC4 = I2C_readU16(BMP180_CAL_AC4);
    AC5 = I2C_readU16(BMP180_CAL_AC5);
    AC6 = I2C_readU16(BMP180_CAL_AC6);
    B1  = I2C_readS16(BMP180_CAL_B1);
    B2  = I2C_readS16(BMP180_CAL_B2);
    MB  = I2C_readS16(BMP180_CAL_MB);
    MC  = I2C_readS16(BMP180_CAL_MC);
    MD  = I2C_readS16(BMP180_CAL_MD);
}
int read_raw_temp()
{
    int raw;
    I2C_writeByte(BMP180_CONTROL,BMP180_READTEMPCMD);
    delay(5);  //5ms;
    raw = I2C_readByte(BMP180_TEMPDATA) << 8;
    raw += I2C_readByte(BMP180_TEMPDATA+1);
    return raw;

}
int read_raw_pressure()
{
    int MSB,LSB,XLSB,raw;
    I2C_writeByte(BMP180_CONTROL,BMP180_READPRESSURECMD +(OSS << 6));
    switch(OSS)
    {
        case BMP180_ULTRALOWPOWER:
            delay(5);break;
        case BMP180_HIGHRES:
            delay(14);break;
        case BMP180_ULTRAHIGHRES:
            delay(26);break;
        default :
            delay(8);
    }
    MSB  = I2C_readByte(BMP180_PRESSUREDATA);
    LSB  = I2C_readByte(BMP180_PRESSUREDATA + 1);
    XLSB = I2C_readByte(BMP180_PRESSUREDATA + 2);
    raw = ((MSB << 16) + (LSB << 8) + XLSB) >> (8 - OSS);
    return raw;
}
float read_temperature()
{
    float T;
    int UT,X1,X2,B5;
    UT = read_raw_temp();
    X1 = ((UT - AC6)*AC5) >> 15;
    X2 = (MC << 11) / (X1 + MD);
    B5 = X1 + X2;
    T = ((B5 + 8) >> 4) /10.0;
    return T;
}

int read_pressure()
{
    int P;
    int UT,UP,X1,X2,X3,B3,B5,B6;
    unsigned int B4;
    int B7;
    UT = read_raw_temp();
    UP = read_raw_pressure();

    X1 = ((UT - AC6)*AC5) >> 15;
    X2 = (MC << 11) / (X1 + MD);
    B5 = X1 + X2;

    //Pressure Calculations
    B6 = B5 - 4000;
    X1 = (B2 * (B6 * B6) >> 12) >> 11;
    X2 = (AC2 * B6) >> 11;
    X3 = X1 + X2;
    B3 = (((AC1 * 4 + X3) << OSS) + 2) / 4;
    X1 = (AC3 * B6) >> 13;
    X2 = (B1 * ((B6 * B6) >> 12)) >> 16;
    X3 = ((X1 + X2) + 2) >> 2;
    B4 = (AC4 * (X3 + 32768)) >> 15;
    B7 = (UP - B3) * (50000 >> OSS);
    if (B7 < 0x80000000){P = (B7 * 2) / B4;}
    else {P = (B7 / B4) * 2;}
    X1 = (P >> 8) * (P >> 8);
    X1 = (X1 * 3038) >> 16;
    X2 = (-7357 * P) >> 16;
    P = P + ((X1 + X2 + 3791) >> 4);
    return P;

}
float read_altitude()
{
    float pressure,altitude;
    float sealevel_pa = 101325.0;
    pressure = (float)read_pressure();
    altitude = 44330.0 * (1.0 - pow(pressure / sealevel_pa,(1.0/5.255)));
    return altitude;
}
float read_sealevel_pressure()
{
    float altitude_m = 0.0;
    float pressure,p0;
    pressure =(float)read_pressure();
    p0 = pressure / pow(1.0 - altitude_m/44330.0,5.255);
    return p0;
}
int main(int argc,char **argv)
{
    printf("BMP180 Test Program ...\n");
    if(wiringPiSetup() < 0) return 1;
    fd = wiringPiI2CSetup(BMP180_Address);
    load_calibration();
    while(1)
    {
        printf("\nTemperature : %.2f C\n",read_temperature());
        printf("Pressure :    %.2f Pa\n",read_pressure()/100.0);
        printf("Altitude :    %.2f h\n",read_altitude());
        delay(1000);
    }
    return 0;
}


            The head file.

Save the file as "bmp180.h"

#ifndef _BMP180_
#define _BMP180_

//i2c address
#define BMP180_Address 0x77

//Operating Modes
#define BMP180_ULTRALOWPOWER    0
#define BMP180_STANDARD         1
#define BMP180_HIGHRES          2
#define BMP180_ULTRAHIGHRES     3

//BMP185 Registers
#define BMP180_CAL_AC1          0xAA  //Calibration data (16 bits)
#define BMP180_CAL_AC2          0xAC  //Calibration data (16 bits)
#define BMP180_CAL_AC3          0xAE  //Calibration data (16 bits)
#define BMP180_CAL_AC4          0xB0  //Calibration data (16 bits)
#define BMP180_CAL_AC5          0xB2  //Calibration data (16 bits)
#define BMP180_CAL_AC6          0xB4  //Calibration data (16 bits)
#define BMP180_CAL_B1           0xB6  //Calibration data (16 bits)
#define BMP180_CAL_B2           0xB8  //Calibration data (16 bits)
#define BMP180_CAL_MB           0xBA  //Calibration data (16 bits)
#define BMP180_CAL_MC           0xBC  //Calibration data (16 bits)
#define BMP180_CAL_MD           0xBE  //Calibration data (16 bits)
#define BMP180_CONTROL          0xF4
#define BMP180_TEMPDATA         0xF6
#define BMP180_PRESSUREDATA     0xF6

//Commands
#define BMP180_READTEMPCMD      0x2E
#define BMP180_READPRESSURECMD  0x34

#endif
TAG: X1203 Raspberry Pi 5 UPS Board Uninterruptible Power Supply For Li-Battery (NOT includ) Sipeed Lichee Tang Nano 9K FPGA MINI Development Board GW1NR-9 GOAI Gowin Power Manager Module 2.66inch e-Paper e-link Module 360x184 4-Color Red Yellow Black White For Arduino Raspberry Pi STM32 Jetson Tutorial Information Wiki Program Code User Guide SpotPear UART To WiFi MPS2280D Raspberry Pi 5 PCIe to M.2 NVMe Dual SSD Adapter Board HAT Pi5 Double 2280 Pi5 Active Cooler Luckfox Pico Max LCD Screen Driver Board STM32 0.85inch LCD Sipeed Lichee Tang Nano1K FPGA MINI Development Board GW1NZ-1 Raspberry Pi 5 / 4B UPS With PD Trigger Activation Uninterruptible Power Bank Supply 9600mAh 5V/5A 25W Also For Phone/Mini Computer PC Raspberry Pi RP2040 Long-Rang Raspberry Pi Screen Jetson Orin NX Industrial USB to RS485 Converter 1.83inch LCD TouchScreen Display 240&amp;amp;amp;times;280 NV3030B Arduino Raspberry Pi ESP32S3 Pico STM32 Raspberry Pi DSI 800×480 CSI-to-HDMI X1004