稼働中

Raspberry Pi Pico(d_05)DS18B20 1wire 温度センサー(2)

DS18B20 1wire 温度センサー

MAXIM Programmable Resolution 1-Wire Digital Thermometer
DS18B20は1-Wireディジタル温度計です。記事d_04ではds18x20モジュールをインポートで簡単に温度測定が出来ました。
その代わりに1-Wireデバイスの基礎的な把握が出来ませんでした。DS18B20のデータシートにあるROMコマンド、FUNCTIONコマンドの使い方も分かりません。ds18x20モジュールをインポートしないでDS18B20の温度測定をしてみようと思います。接続図や実体図は記事d_04を参照ください。
※開発環境はThonnyです。ThonnyでMicroPythonをRaspberry Pi Pico with RP2040にインストールして使っています。

1-Wireの通信

素人なのであまり詳しくは書けません。雰囲気だけです。
通信方式は
「バスリセット>rom-code(64bit)を送ってデバイスを指定する>実行するコマンドを送る」
でワンセットです。

実行コマンドを送るには
「バスリセット>ROMコマンド>DEVICEコマンド」
でセットです。

ROMコマンドは1-Wireデバイスで共通のようです。そのため同じバス上の複数のデバイスをrom-codeで指定して操作するようです。

DS18B20のデータシートからROMコマンドとDEVICEコマンドを以下に列挙します。詳細はデータシートを参照ください。


ROMコマンド
SEARCH ROM [F0h]            バス上デバイスのrom-codeを調べます。
READ ROM [33h]              デバイスが1つのならrom-codeを読出しできます。
MATCH ROM [55h]             rom-codeでデバイスを指定します。
SKIP ROM [CCh]              デバイスが1つならrom-codeを省いてデバイスを指定します。
ALARM SEARCH [ECh]          アラーム フラグが設定されたデバイスのみが応答します。

FUNCTION(DEVICE)コマンド
CONVERT T [44h]             温度データをSCRATCHPADに書込む
WRITE SCRATCHPAD [4Eh]      SCRATCHPADに3bytes書込む 
READ SCRATCHPAD [BEh]       SCRATCHPAD 9bytesを読込む
COPY SCRATCHPAD [48h]       SCRATCHPADの3bytesをEEPROMにコピーする
RECALL E2 [B8h]             EEPROMから3bytesを読込む
READ POWER SUPPLY [B4h]     parasite(0)/externally(1)をbitで返す
※3bytes(TH,TL,Config)データ

onewireモジュールを読込みます。class ‘OneWire’があります。Pin(12)で通信します。
ow = onewire.OneWire(Pin(12))
でオブジェクトを作ります。

OneWireには、ROM コマンドのSKIP_ROM、SEARCH_ROM、MATCH_ROMの定数やreset、scan、select_rom、readbit、readbyte、readinto、writebit、write、writebyteのメソッドがあります。

ROM CODEを取得

ROMコマンドのSEARCH_ROM等を使ってデバイスを検索して、全ての1wireデバイスのrom-codeを収集するのですが、素人の私では荷が重いのでscan()で取得します。

スクリプト

スクリプトは以下のようにしました。


pico_ds18b20_01b_rom.py
from machine import Pin
import onewire
## DS18B20 Programmable Resolution 1-Wire Digital Thermometer 
# 8-BIT FAMILY CODE DS18B20 (28h)
# GPIO 12 で OneWire バスを作成
ow = onewire.OneWire(Pin(12))

# バス上のrom-codeをリストデータで返す
s=ow.scan()             # GPIO 12のデバイスROM 
rom_code = s[0]         # ここではデバイスは1つだけなのでs[0] 2つ目ならs[1]
family_code=s[0][0]     # FAMILY CODEはs[0][0] bytearray(8)の[0]
print('scan data=',s)
print('rom code =',rom_code)
print('family code=', family_code)

実行結果

以下のようになりました。※Thonnyのshellに表示されます。
DS18B20のrom codeは bytearray(b'(\xffe\xf8\x93\x16\x05S’)でした。


pico_ds18b20_01b_rom.py
>>> %Run -c $EDITOR_CONTENT
scan data= [bytearray(b'(\xffe\xf8\x93\x16\x05S')]
rom code = bytearray(b'(\xffe\xf8\x93\x16\x05S')
family code= 40         #hex(40)=0x28
>>> 

ちなみに、1wireデバイスがDS18B20しか接続されてなければ、READ_ROM[33h]でもrom codeを取得できます。


※Thonnyのshellで実行
>>> import onewire
>>> buf = bytearray(8)
>>> ow = onewire.OneWire(Pin(12))
>>> ow.reset(True)
True
>>> ow.writebyte(0x33)      #READ_ROM = 0x33
>>> ow.readinto(buf)
>>> buf
bytearray(b'(\xffe\xf8\x93\x16\x05S')
>>> 

温度測定

DS18B20のSCRATCHPADを読んで温度データから摂氏温度に変換して表示してみます。

スクリプト

スクリプトは以下のようにしました。


pico_ds18b20_02b_temp.py
from machine import Pin
import onewire
import time

#符号計算 
def comp(dat,bit_num):
    if dat >= (2**(bit_num-1)):
        dat=dat-(2**bit_num)
    #print(bin(dat), dat, bin(dat & 0xf))
    return dat

## DS18B20 Programmable Resolution 1-Wire Digital Thermometer 
ow = onewire.OneWire(Pin(12))  # GPIO 12 で OneWire バスを作成
s=ow.scan()      # ROMCODE 
rom_code = s[0]  # デバイスは1つだけなのでs[0]がDS18B20のROMCODE

## ROM COMMANDS 
#SEARCH_ROM = 0xF0   #240 (ow.SEARCH_ROMでも可)
#READ_ROM = 0x33
MATCH_ROM = 0x55     # 85 (ow.MATCH_ROMでも可)
#SKIP_ROM = 0xCC     # 204(ow.SKIP_ROMでも可)

## DEVICE COMMANDS
CONVERT = 0x44      #CONVERT T [44h] 
RD_SCRATCH =0xBE    #READ SCRATCHPAD [BEh]  
#WR_SCRATCH =0x4E   #WRITE SCRATCHPAD [4Eh]

## CONVERT T   rom-codeの整合(MATCH_ROM)>整合したデバイスへ送る
ow.reset(True)
ow.writebyte(MATCH_ROM)     #ow.MATCH_ROMでも可
ow.write(rom_code)          #8bytes
ow.writebyte(CONVERT)       #温度データをSCRATCHPADに書込む
time.sleep_ms(750)

## READ SCRATCHPAD  レジスタデータ読出し 9bytes
buf = bytearray(9)

#ow.select_rom(rom_code) 
ow.reset(True)          #select_romはreset~writeと同じ
ow.writebyte(MATCH_ROM)
ow.write(rom_code)
ow.writebyte(RD_SCRATCH) #RD_SCRATCH=0xBE READ SCRATCHPAD
ow.readinto(buf)

print('bytearray(9)=',buf)

## 温度に変換
# SCRATCHPADのbyte0,byte1が温度データ 2bytesを符号付き10bitにする
t_reg=(buf[1] & 0x0f)<<8 | buf[0]
t_dat=comp(t_reg,12)        # 符号計算(データ,bit数)

temp=t_dat*0.0625    # config 12bitなら 0.0625/bit
print('temp=',temp)

補足です。
rom_codeを送信してデバイスを指定する以下の部分は、select_rom(rom_code)で置き換えができます。


ow.reset(True)
ow.writebyte(MATCH_ROM)
ow.write(rom_code)

ちなみに1wireデバイスがDS18B20だけなので「## CONVERT T」の部分はrom_codeの送信なし(SKIP_ROM)でも可能です。rom_code取得も不要です。


## CONVERT T
# ow.reset(True)
# ow.writebyte(SKIP_ROM)    #ow.SEARCH_ROMでも可
# ow.writebyte(CONVERT)
# time.sleep_ms(750)

実行結果

実行すると以下のようになりました。※Thonnyのshellに表示されます。
SCRATCHPADのデータと温度が表示されています。


pico_ds18b20_02b_temp.py
>>> %Run -c $EDITOR_CONTENT
bytearray(9)= bytearray(b'\xe6\x01 \x15\x7f\xff\x7f\x10\x16')
temp= 30.375
>>> 

その他

他のDEVICE(FUNCTION)コードの使用例です。実行例などは省いています。

WRITE SCRATCHPAD

bufで指定した値をSCRATCHPADのTH,TL, Configurationレジスタに書込みします。※TH,TLレジスタはアラームで使うレジスタのようですが、USERレジスタとしても使えるようです。


##WRITE SCRATCHPAD 3bytes
buf = bytearray(3)
buf[0]=0x20         #TH Register
buf[1]=0x15         #TL Register
buf[2]=0x7f         #Configuration Register

ow.reset(True)
ow.reset(True)
ow.writebyte(MATCH_ROM)
ow.write(rom_code)
ow.writebyte(0x4e)      #WRITE SCRATCHPAD [4Eh]
ow.write(buf)

COPY SCRATCHPAD

SCRATCHPADのTH,TL, Configurationレジスタの値をEEPROMに書込む(保存)します。


## COPY SCRATCHPAD
ow.reset(True)
ow.writebyte(MATCH_ROM)
ow.write(rom_code)      #8bytes
ow.writebyte(0x48)      #COPY SCRATCHPAD [48h]
time.sleep_ms(10)

RECALL E2

EEPROMからSCRATCHPADのTH,TL, Configurationレジスタに書込みます。


## RECALL E2
ow.reset(True)
ow.writebyte(MATCH_ROM)
ow.write(rom_code)     #8bytes
ow.writebyte(0xb8)     #RECALL E2 [B8h]
#time.sleep_ms(10)

READ POWER SUPPLY

電源供給の状態を調べます。PARASITEなら(0)外部電源なら(1)を返します。


## Read Power Supply readbit
ow.reset(True)
ow.writebyte(MATCH_ROM)
ow.write(rom_code)      #8bytes
ow.writebyte(0xb4)      #READ POWER SUPPLY[B4h] 
print(ow.readbit())     #parasite(0)/externally(1)

まとめ

Raspberry Pi PicoでMicroPythonを使って1wireデバイスDS18B20での温度測定が出来ました。ds18x20のモジュールのインポートなしでも温度測定が出来ました。
1-Wireデバイスへのコマンド送信は「リセット>ROMコマンド>DEVICEコマンドの繰返し」であることが大まかに分かりました。