- install smbus2 with pip3
- in milkv duo s use pins pin b20 and b21 (that is i2c bus 4)
bmp sensor example
from smbus2 import SMBus
import time
DEVICE_ADDR = 0x77
def read_signed_16(bus, addr):
high = bus.read_byte_data(DEVICE_ADDR, addr)
low = bus.read_byte_data(DEVICE_ADDR, addr+1)
val = (high << 8) + low
if val >= 32768:
val = val - 65536
return val
def read_unsigned_16(bus, addr):
high = bus.read_byte_data(DEVICE_ADDR, addr)
low = bus.read_byte_data(DEVICE_ADDR, addr+1)
return (high << 8) + low
with SMBus(4) as bus:
# Read calibration data from EEPROM
AC1 = read_signed_16(bus, 0xAA)
AC2 = read_signed_16(bus, 0xAC)
AC3 = read_signed_16(bus, 0xAE)
AC4 = read_unsigned_16(bus, 0xB0)
AC5 = read_unsigned_16(bus, 0xB2)
AC6 = read_unsigned_16(bus, 0xB4)
B1 = read_signed_16(bus, 0xB6)
B2 = read_signed_16(bus, 0xB8)
MB = read_signed_16(bus, 0xBA)
MC = read_signed_16(bus, 0xBC)
MD = read_signed_16(bus, 0xBE)
# Read raw temperature
bus.write_byte_data(DEVICE_ADDR, 0xF4, 0x2E)
time.sleep(0.005)
UT = (bus.read_byte_data(DEVICE_ADDR, 0xF6) << 8) + bus.read_byte_data(DEVICE_ADDR, 0xF7)
# Read raw pressure (oss = 0)
bus.write_byte_data(DEVICE_ADDR, 0xF4, 0x34)
time.sleep(0.005)
UP = (bus.read_byte_data(DEVICE_ADDR, 0xF6) << 8) + bus.read_byte_data(DEVICE_ADDR, 0xF7)
# Calculate true temperature
X1 = ((UT - AC6) * AC5) >> 15
X2 = (MC << 11) // (X1 + MD)
B5 = X1 + X2
temp = ((B5 + 8) >> 4) / 10.0
# Calculate true pressure
B6 = B5 - 4000
X1 = (B2 * (B6 * B6 >> 12)) >> 11
X2 = (AC2 * B6) >> 11
X3 = X1 + X2
B3 = (((AC1 * 4 + X3) << 0) + 2) >> 2
X1 = (AC3 * B6) >> 13
X2 = (B1 * ((B6 * B6) >> 12)) >> 16
X3 = ((X1 + X2) + 2) >> 2
B4 = (AC4 * (X3 + 32768)) >> 15
B7 = (UP - B3) * 50000
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
pressure = p + ((X1 + X2 + 3791) >> 4)
print(f"Temperature: {temp:.1f} °C")
print(f"Pressure: {pressure} Pa")
scan i2c
from smbus2 import SMBus
def scan_i2c(bus_number):
print(f"Scanning I2C bus {bus_number}...")
devices = []
with SMBus(bus_number) as bus:
for address in range(0x03, 0x78):
try:
bus.read_byte(address)
devices.append(address)
except OSError:
continue
if devices:
print("Found I2C device(s) at address(es):")
for d in devices:
print(f" 0x{d:02X}")
else:
print("No I2C devices found.")
scan_i2c(4)
use an oled screen (ssd1306)
from smbus2 import SMBus
import time
# OLED I2C config
I2C_ADDR = 0x3C
I2C_BUS = 4
CMD = 0x00
DATA = 0x40
# Minimal 5x7 font (vertical bytes, bit 0 = top)
font5x7 = {
' ': [0x00, 0x00, 0x00, 0x00, 0x00],
'!': [0x00, 0x00, 0x5F, 0x00, 0x00],
'.': [0x00, 0x60, 0x60, 0x00, 0x00],
':': [0x00, 0x36, 0x36, 0x00, 0x00],
'-': [0x08, 0x08, 0x08, 0x08, 0x08],
'0': [0x3E, 0x51, 0x49, 0x45, 0x3E],
'1': [0x00, 0x42, 0x7F, 0x40, 0x00],
'2': [0x42, 0x61, 0x51, 0x49, 0x46],
'3': [0x21, 0x41, 0x45, 0x4B, 0x31],
'4': [0x18, 0x14, 0x12, 0x7F, 0x10],
'5': [0x27, 0x45, 0x45, 0x45, 0x39],
'6': [0x3C, 0x4A, 0x49, 0x49, 0x30],
'7': [0x01, 0x71, 0x09, 0x05, 0x03],
'8': [0x36, 0x49, 0x49, 0x49, 0x36],
'9': [0x06, 0x49, 0x49, 0x29, 0x1E],
'A': [0x7E, 0x11, 0x11, 0x11, 0x7E],
'B': [0x7F, 0x49, 0x49, 0x49, 0x36],
'C': [0x3E, 0x41, 0x41, 0x41, 0x22],
'D': [0x7F, 0x41, 0x41, 0x22, 0x1C],
'E': [0x7F, 0x49, 0x49, 0x49, 0x41],
'F': [0x7F, 0x09, 0x09, 0x09, 0x01],
'G': [0x3E, 0x41, 0x49, 0x49, 0x7A],
'H': [0x7F, 0x08, 0x08, 0x08, 0x7F],
'I': [0x00, 0x41, 0x7F, 0x41, 0x00],
'J': [0x20, 0x40, 0x41, 0x3F, 0x01],
'K': [0x7F, 0x08, 0x14, 0x22, 0x41],
'L': [0x7F, 0x40, 0x40, 0x40, 0x40],
'M': [0x7F, 0x02, 0x04, 0x02, 0x7F],
'N': [0x7F, 0x04, 0x08, 0x10, 0x7F],
'O': [0x3E, 0x41, 0x41, 0x41, 0x3E],
'P': [0x7F, 0x09, 0x09, 0x09, 0x06],
'Q': [0x3E, 0x41, 0x51, 0x21, 0x5E],
'R': [0x7F, 0x09, 0x19, 0x29, 0x46],
'S': [0x46, 0x49, 0x49, 0x49, 0x31],
'T': [0x01, 0x01, 0x7F, 0x01, 0x01],
'U': [0x3F, 0x40, 0x40, 0x40, 0x3F],
'V': [0x1F, 0x20, 0x40, 0x20, 0x1F],
'W': [0x3F, 0x40, 0x38, 0x40, 0x3F],
'X': [0x63, 0x14, 0x08, 0x14, 0x63],
'Y': [0x07, 0x08, 0x70, 0x08, 0x07],
'Z': [0x61, 0x51, 0x49, 0x45, 0x43],
}
def send_cmd(bus, cmd):
bus.write_byte_data(I2C_ADDR, CMD, cmd)
def send_data(bus, data):
for i in range(0, len(data), 16):
bus.write_i2c_block_data(I2C_ADDR, DATA, data[i:i+16])
def init_display(bus):
cmds = [
0xAE, 0xD5, 0x80, 0xA8, 0x3F,
0xD3, 0x00, 0x40, 0x8D, 0x14,
0x20, 0x00, 0xA1, 0xC8, 0xDA,
0x12, 0x81, 0xCF, 0xD9, 0xF1,
0xDB, 0x40, 0xA4, 0xA6, 0xAF
]
for c in cmds:
send_cmd(bus, c)
def set_cursor(bus, page, column):
send_cmd(bus, 0xB0 + page) # Set page (row)
send_cmd(bus, 0x00 + (column & 0x0F)) # Lower col nibble
send_cmd(bus, 0x10 + (column >> 4)) # Upper col nibble
def draw_char(bus, char):
data = font5x7.get(char.upper(), font5x7[' ']) + [0x00] # spacing
send_data(bus, data)
def draw_text(bus, page, col, text):
set_cursor(bus, page, col)
for c in text:
draw_char(bus, c)
def clear_display(bus):
for page in range(8):
set_cursor(bus, page, 0)
send_data(bus, [0x00] * 128)
def main():
with SMBus(I2C_BUS) as bus:
init_display(bus)
clear_display(bus)
draw_text(bus, page=0, col=0, text="HELLO")
draw_text(bus, page=1, col=0, text="DUO S")
time.sleep(10)
clear_display(bus)
if __name__ == "__main__":
main()