Jak dostat data z I2C zařízení BH1750 přes USB-I2C převodník v Pythonu?

Zdar,

mám tato zařízení:

USB-I2C převodník: https://www.robot-electronics.co.uk/htm/usb_i2c_tech.htm

GY-30 I2C modul: https://5.imimg.com/data5/TY/AK/MY-1833510/gy-30-bh1750-intensity-digital-light-sensor-module.pdf

A modul GY-30 je se snímačem okolního osvětlení BH1750: https://www.mylms.cz/wp-content/uploads/2017/07/bh1750-datasheet.pdf

Potřeboval bych z toho BH1750 vyčítat Luxy. Cílem je, abych si pak napsal SW, který mi bude automaticky regulovat podsvícení monitoru.

S převodníkem komunikovat umím, přes pyserial na něm umím zhasnout tu kontrolní LEDku a nebo si nechat zobrazit jeho verzi.

Co u ale neumím, to je určit správnou sekvenci bytů (bajtů), aby mi ten BH1750 poslal ty Luxy. Mám to testovně napsané nějak takto:

Kód: [Vybrat]
import serial
ser = serial.Serial(port="/dev/ttyUSB0",
                    baudrate=19200,
                    parity=serial.PARITY_NONE,
                    stopbits=serial.STOPBITS_TWO,
                    bytesize=serial.EIGHTBITS,
                    timeout=0.500,
                    )

ser.flushInput()
ser.flushOutput()

ser.write(bytearray([0x55, 0x23, 0x11, 0x01]))
test = ser.read()

Zkoušel jsem různé posloupnosti, samozřejmě nejdřív ten BH1750 přes 0x01 zapnout, resetovat a tak dále. Ale ono se to k němu prostě nějak asi nedostane, vůbec to nereaguje, zkoušel jsem i jiné adresy, na všech adresách ale stejná odezva. Něco jakoby vyčtu, ale je to třeba jen číslo 255 a bohužel to Luxy opravdu nejsou.

Umí s tím někdo a byl by mi ochoten pomoci? Buď samozřejmě takhle přes fórum a nebo klidně sdílení přes ssh a nebo nějaká session.

Díky za případné reakce.

Jiří


Pokud máte možnost, doporučil bych ten senzor připojit k něčemu, kde můžete přímo komunikovat přes I2C (Arduino, RPi...).

Moje chápání I2C je také takové, že pokud chcete vyčíst něco ze senzoru, musíte dopředu vědět, kolik bajtů vám bude posílat (AFAIK I2C nedovoluje slave zařízení nějak říci "poslal jsem všechno, co jsem chtěl").

Zkuste popsat co očekáváte od té sekvence

Kód: [Vybrat]
ser.write(bytearray([0x55, 0x23, 0x11, 0x01]))
Podle dat o tom převodníku je 0x55  příkaz pro čtení/zápis více bytů a měl by být následován adresou i2c obvodu.
A opět dle datasheetu BH1750 ta následující 0x23 není korektní adresou tohoto obvodu.
Spíš to vypadá, že se rovnou pokoušíte zapnout "One Time L-Resolution Mode ".

Takhle jednoduše to asi nepůjde, myslím, že ten obvod se napřed musí správně inicializovat, dále se uspává a musí se probudit atd.

Zkuste mrknout sem:
https://gist.github.com/oskar456/95c66d564c58361ecf9f

Plus nezapomenout, že na výsledek se musí někdy čekat až 200ms ( podle rozlišení )

Zdravím, od té sekvence jsem očekával, že mi to dá hodnotu, kterou bych pak přepočetl na Luxy. Zkoušel jsem to i v různých variantách za sebou, tedy:
ser.write(bytearray([0x55, 0x23, 0x00]))
ser.write(bytearray([0x55, 0x23, 0x01]))
ser.write(bytearray([0x55, 0x23, 0x20, 0x04]))
ser.write(bytearray([0x55, 0x23, 0x00]))
a s nějakou prodlevou pak ser.read(). Ale pořád buď žádná hodnota nebo nějaká ta 255.
Přiznám se na rovinu, že I2C sběrnici vůbec nerozumím a mým účelem je prostě si jen vyrobit ten regulátor podsvícení pro monitor a pak už se k takové věci okolo HW asi nedostanu.
I když to udělám podle toho odkazu, tak jsou výsledky stejné ...

Skier

  • ***
  • 110
    • Zobrazit profil
    • E-mail
Zdar,

přečti si datasheet té BH1750, konkrétně strana 7 - tam máš jasně vidět co jsou zápisy z PC (Master) a co je čtení z IC (Slave). A podle toho si udělej sekvence write - read.

Zajímá tě one time L resolution mode, tím bych začal, bude to pro tebe jednodušší.

Pak máš na straně 5 seznam instrukcí a můžeš si hrát.



Skier

  • ***
  • 110
    • Zobrazit profil
    • E-mail
Zdravím, od té sekvence jsem očekával, že mi to dá hodnotu, kterou bych pak přepočetl na Luxy. Zkoušel jsem to i v různých variantách za sebou, tedy:
ser.write(bytearray([0x55, 0x23, 0x00]))
ser.write(bytearray([0x55, 0x23, 0x01]))
ser.write(bytearray([0x55, 0x23, 0x20, 0x04]))
ser.write(bytearray([0x55, 0x23, 0x00]))
a s nějakou prodlevou pak ser.read(). Ale pořád buď žádná hodnota nebo nějaká ta 255.
Přiznám se na rovinu, že I2C sběrnici vůbec nerozumím a mým účelem je prostě si jen vyrobit ten regulátor podsvícení pro monitor a pak už se k takové věci okolo HW asi nedostanu.
I když to udělám podle toho odkazu, tak jsou výsledky stejné ...

Proč posíláš 0x55 0x23 ? Musíš nejdříve naadresovat device.


Ten převodník jsem nikdy nepoužíval, ale podle dokumentace by to mělo funfovat takto:

Víš jakou máš adresu BH1750?

Podle dokumentace má BH1750 tyto adresy (volí se podle úrovně na ADDR pinu - nastavení modulku):

ADDR = L:
- adresa pro zápis 0x46
- adresa pro čtení 0x47

ADDR = H:
- adresa pro zápis 0xB8
- adresa pro čtení 0xB9

A potom registry:
- Power ON: 0x01
- One Time Low resolution measurement mode (doporučím pro začátek): 0x23

No, a potom je sekvence jednoduchá:

1. Zapíšeš adresu device s nahozeným bitem pro zápis (protože v dalším kroku zapisuješ interní adresu)
2. Zapíšeš interní adresu

Takže pro zapnutí 0x55 0x46 (nebo 0xB9) 0x01

Pro jednorázové změření a čtení : WRITE [0x55 0x46 (nebo 0xB8) 0x23] WAIT 24 ms WRITE [0x55 0x47 (nebo 0xB9)] READ 2 Byty

A to by mělo funfovat.

Ten luxmetr znám, pár věcí tam vidím špatně, ale nebude problém to napravit.

Začneme adresou zařízení. Vidím ve Vašem příkladu 0x23, takže předpokládám, že ADDR pin zařízení máte spojen s GND. Předně 0x23 není celá adresa - je to horních 7 bitů adresy obvodu. Vy ještě musíte dodat nejnižší bit, kterým říkáte, jestli budete číst (1) nebo zapisovat (0). Pro zápis povelu tedy potřebujete volat adresu 0x23 << 1 | 0 = 0x46, pro čtení budete volat 0x23 << 1 | 1 = 0x47.

Převodník, co máte, je dost katastrofa, protože se snaží zjednodušit práci s některými typy I2C zařízení na úkor obecné použitelnosti. Navíc si v "dokumentaci" volně zaměňují pojmy jako adresa, registr, offset. 0x55 je povel pro zařízení s interním datovým pointerem, což tento senzor není.

Tento senzor, stejně jako spousta dalších, se nějakým povelem zapne a pak už jen chrlí data. Pro nastavení senzoru tedy použijete povel převodníku 0x53 "Read/Write single byte for non-registered devices" a potom pro čtení budete volat 0x54 "Read multiple bytes without setting new address".

Takhle by to mělo vypadat nějak celé:

Kód: [Vybrat]
import serial
import time

ser = serial.Serial(port="/dev/ttyUSB0",
                    baudrate=19200,
                    parity=serial.PARITY_NONE,
                    stopbits=serial.STOPBITS_TWO,
                    bytesize=serial.EIGHTBITS,
                    timeout=0.500,
                    )

ser.flushInput()
ser.flushOutput()

ADDR_BASE = 0x23 # nebo 0x5c pokud je ADDR pin HIGH
ADDR_READ = ADDR_BASE << 1 | 1
ADDR_WRITE = ADDR_BASE << 1

CMD_WRITE_1B = 0x53
CMD_READ_MULT = 0x54

LUX_RESOLUTION_1X = 0x10
LUX_RESOLUTION_4X = 0x13

# nastavení senzoru
ser.write(bytearray([CMD_WRITE_1B, ADDR_WRITE, LUX_RESOLUTION_1X]))

while True:
time.sleep(1)

# požádám převodník o 2 B
ser.write(bytearray([CMD_READ_MULT, ADDR_READ, 0x02]))

# přečtu je
raw = ser.read(2)

# interpretace bytů podle datasheetu
lx = (raw[1] << 8 | raw[0]) / 1.2

print("--> %.d lx" %(lx))

RDa

  • *****
  • 2 467
    • Zobrazit profil
    • E-mail
Začneme adresou zařízení. Vidím ve Vašem příkladu 0x23, takže předpokládám, že ADDR pin zařízení máte spojen s GND. Předně 0x23 není celá adresa - je to horních 7 bitů adresy obvodu. Vy ještě musíte dodat nejnižší bit, kterým říkáte, jestli budete číst (1) nebo zapisovat (0). Pro zápis povelu tedy potřebujete volat adresu 0x23 << 1 | 0 = 0x46, pro čtení budete volat 0x23 << 1 | 1 = 0x47.

I2C ma 7 nebo 10 bitovou adresu (SLA - SLave Address)! Nic jako read address a write address neexistuje.
Posledni bit prvniho bajtu je totiz R/W flag. Lidi co povazuji za "adresu" prvni bajt bych nekompromisne strilel.

Ref: https://www.nxp.com/docs/en/user-guide/UM10204.pdf - I2C-bus specification and user manual, rev. 6

Pro puvodniho tazatele a par kolemjdoucich mam jednu radu - nesnazte se spojovat nekolik vrstev dohromady, nema to na PC s "neomezenyma zdroji" smysl, kdyz uz pouzivate vysokourovnovy jazyk ktery ma objekty, tak si udelejte hezky tridy a abstrakce. Zrovna v tomto pripade se to hodi, i pro pochopeni problemu s vrstvenim protokolu.

Pane Křišťane Přijmení :),
to je prostě perfektní vysvětlení a ten Váš script funguje úplně bez chyby na první dobrou. Napsal jste to prostě dokonale. Díky tomu Vašemu komentáři to najednou i chápu ... :). Ještě si tedy musím nastudovat to adresování raději ....
Mám jen jedinou připomínku a to, že prodejce mi říkal, že v tomto případě stačí pouze přímé připojení a nic dalšího, tedy jsem PIN ADDR nespojoval s GND externě, mám jen 4 žíly mezi převodníkem a modulkem. Podle schématu je to přes malý 1k odpor propojeno interně:
http://mbitech.ru/images/userfiles/image/BH1750.jpg
Toto píšu proto, aby to případně dalším čtenářům pomohlo.

Ještě jednou moc děkuji.
PS: Nebál bych se Vás nazvat SW/HW bohem a to i z důvodu, že včera večer jsem měl Skype session s člověkem, který se něčím podobným profesionálně živí a nedal to dohromady i se vzdáleným přistupem k zařízení a Vy jste to dal jen podle datasheetů ...... fakt smekám.
« Poslední změna: 05. 04. 2020, 14:07:15 od Jiří Něměc »

Pro tyhle povídánky s HW z PC se mi osvědčilo obyč. USB arduino Pro za dva dolary s nahranou Firmatou. Knihovny jsou pro vše možné a rozšíření případné chybějící podpory není problém, přidával jsem třeba accelstepper do octave, žádná věda. Funguje velice spolehlivě. I2C protokol to má, akorát jsem jej nenašel v pythonním klientu firmaty, ale moc jsem se nekoukal.