Raspberry Pi Picoで変数データを本体フォルダにファイルとして保存し、次回起動時に読込んでプログラム内で使用する方法を詳しく紹介します。
マイコンでは「EEPROM」を使用するのが一般的ですが、「ラズパイPico」には「EEPROM」がありません。
その代わりに、実行プログラムを保存している「メモリ領域」にアクセスすることができるため、そこに自由にデータをファイルとして保存できます。
電源をOFFしてもデータは保持されるため、起動時にデータファイルを読み込んで使用することができます。
プログラム言語は「Python(MicroPython)」開発環境は「Thonny」を使用します。
「MicroPython」や「Thonny」については以下のリンクで詳しく紹介しています。
1.本体データの確認方法(Thonnyの設定)
2.open 関数で本体ファイル操作
・新規または、上書き保存
・新規または、追記で保存
・読み込み
・読み込み時、ファイルの有無確認
3.サンプルプログラム(計数カウンター)
4.動作確認用配線図
5.動作確認
6.実用的なカウンター(割込処理、OLED表示)
・サンプルプログラム(OLED表示付カウンター)
・配線例と動作確認
7.まとめ
1.本体データの確認方法(Thonnyの設定)
まずは開発環境「Thonny」を使用して、「ラズパイPico」本体のデータを表示できるようにしておきます。
以下のように「Thonny」のツールバー[表示]→[ファイル]をクリックします。
以下のように「Raspberry Pi Pico」と表示されているウインドウに本体に保存されているファイルが表示されます。
まずはここに、実行するプログラムファイルを保存しましょう。
以下のように[保存]ボタンをクリックします。
以下のようなウインドウが表示されたら[Raspberry Pi Pico]をクリックします。
以下のウインドウが表示されるので[ファイル名:]の欄に「main.py」と入力して[OK]ボタンをクリックします。
保存すると、以下のように「main.py」ファイルが追加され、コード入力欄のタブ名も「無題」から「main.py」に変わります。
これでコード入力欄に、以降で紹介するサンプルプログラムを貼り付けて実行する準備が整いました。
本体に保存したデータはその都度、本体のデータを参照して確認していきます。
2.open 関数で本体ファイル操作
「ラズパイPico」本体のフォルダにファイルを保存するには「Python」の「open関数」を使用します。
「open関数」は「Python」の標準関数で、ファイルの保存(新規、上書き、追記)や読み込みを行うことができます。
基本の構文は以下のようになります。
path = ‘/data.txt’ # Picoではこの場合、本体フォルダの data.txt を指す。
# ファイルを書き込みモード(‘w’)で開く
withopen(path,‘w’ ) as f:
f.write( ‘ 文字列 ‘ ) # 書き込み実行(上書き)
# ファイルを追記モード(‘a’)で開く
with open( path, ‘a’ ) as f:
f.write( ‘ 文字列\n ‘) # 書き込み実行(追記)
# ファイルを読み込みモード(‘r’)で開く
with open( path, ‘r’ ) as f:
data = f.read() # 読み込み実行
print( data ) # 読み込みデータ表示
「open関数」でファイルを操作する時は、毎回ファイルを開いてから「書き込み」や「読み込み」を実行し、完了後には「ファイルを閉じる」必要がありますが「with」を使用することで、処理ブロックを抜けるだけで自動でファイルを閉じてくれます。
・新規または、上書き保存
「Pico」本体にファイルを新規または、上書き保存するサンプルプログラムは以下になります。
まずは、変化する数値データとして、電源ONしてからの経過時間(ms)をファイルに保存してみましょう。
「Thonny」等の開発画面にコピペで貼り付けて実行してください。
※プログラムのコピーは下の黒塗り部の右上アイコンクリックでもできます。
import utime # 時間を扱うモジュールを準備
path = '/data_int.txt' # ファイルパスの指定
time = utime.ticks_ms() # 電源ONしてからの経過時間を格納(ms)
# ファイルに上書きで書き込み
with open(path, 'w') as f: # ファイルを書き込みモードで開く
f.write(str(time)) # 書き込み実行(文字列で)
print("Write:", time) # 書き込みデータ表示
「Thonny」に貼り付けて[実行]ボタンを押すと、以下のように[シェル]部に「Write: 382968」と表示されました。(経過時間 382968は実行時のタイミングにより異なります。)
[ストップ]ボタンを押すと以下のように「Pico」本体フォルダに「data_int.txt」が追加(ファイルが無ければ新規作成、あれば上書き保存)されていることが確認できます。
このファイルを開くと、経過時間が保存されていることが確認できます。
・新規または、追記で保存
「Pico」本体にファイルを新規または、既存のデータに追記して保存するサンプルプログラムは以下になります。
今回は文字列データとして「Hello!」をファイルに保存してみましょう。
サンプルプログラムは以下になります。「Thonny」等の開発画面にコピペで貼り付けて実行してください。
※プログラムのコピーは下の黒塗り部の右上アイコンクリックでもできます。
path = '/data_str.txt' # ファイルパスの指定
data = 'Hello!' # 文字列データ
# ファイルに追記で書き込み
with open(path, 'a') as f: # ファイルを追記モードで開く
f.write(data + '\n') # 追記実行(文字列で)※改行付
print("Append:", data) # 追記データ表示
「Thonny」に貼り付けて[実行]ボタンを押すと、以下のように[シェル]部に「Append: Hello!」と表示されます。
[停止]ボタンを押すと以下のように「Pico」本体フォルダに「data_str.txt」が追加(ファイルが無ければ新規作成、あれば追記で保存)されていることが確認できます。
このファイルを開くと、「Hello!」が保存されていることが確認できます。
次に「main.py」コードの内容を以下のように「Hello!」から「Hello world!」に書き換えて[実行]ボタンをクリックしてみましょう。
「シェル」部に「Append: Hello world!」が表示されます。
[ストップ]ボタンを押して「data_str.txt」を開くと、最初に保存した「Hello!」の下に「Hello world!」が追記保存されているのが確認できます。
・読み込み
「Pico」本体に保存されているファイルを読込む方法は以下になります。
保存されているデータは基本的に「文字列」です。文字データはそのまま扱えますが、数値の場合は文字列で読み込まれるため、数値へ変換して使用する必要があります。
上で保存した文字データファイル「data_str.txt」と、数値データファイル「data_int.txt」を読込む方法を、以下からそれぞれ確認してみましょう。
文字列の読み込み
上で保存した文字データファイル「data_str.txt」を読込むサンプルプログラムは以下になります。
「Thonny」等の開発画面にコピペで貼り付けて実行してください。
※プログラムのコピーは下の黒塗り部の右上アイコンクリックでもできます。
path = '/data_str.txt' # ファイルパスの指定
# 文字列ファイルを読み込む
with open(path, 'r') as f: # ファイルを読み込みモードで開く
data = f.read() # ファイルの内容を読み込み(文字列)
print("Deta_str:")
print(data) # 読み込みデータ表示
「Thonny」に貼り付けて[実行]ボタンを押すと、以下のように[シェル]部に「data_str.txt」に保存されている内容が表示されます。
数値の読み込み
上で保存した数値データファイル「data_int.txt」を読込むサンプルプログラムは以下になります。
「Thonny」等の開発画面にコピペで貼り付けて実行してください。
※プログラムのコピーは下の黒塗り部の右上アイコンクリックでもできます。
path = '/data_int.txt' # ファイルパスの指定
# 文字列ファイルを読み込む
with open(path, 'r') as f: # ファイルを読み込みモードで開く
data = f.read().strip() # ファイルの内容を読み込み、前後の不要なデータ(空白、改行文字)を削除
data = int(data) # 読み込んだデータは文字列のため、整数に変換
print("Deta_int:")
print(data) # 読み込みデータ表示
「Thonny」に貼り付けて[実行]ボタンを押すと、以下のように[シェル]部に「data_int.txt」に保存されている内容が表示されます。
読み込み時の注意点
「書き込み」時にファイルが存在しなければ「新規作成」されますが、「読み込み」時にファイルが存在しない場合は「エラー」になります。
このため、ファイルが存在しない場合は「新規ファイルを作成」する等の適切な処理が必要となります。
試しにファイルを削除して読み込みを実行してみましょう。
以下のように「Pico」本体フォルダに保存されているファイル「data_int.txt」を選択して「右クリック」で表示されるメニューの中から[削除]をクリックします。
以下のように、削除していいか聞かれるため[はい(Y)]をクリックします。
「data_int.txt」が存在しない状態で、以下のように再度「数値を読込むサンプルプログラム」を実行するとエラーが発生し、「シェル」部に「OSError」が表示されます。
・読み込み時、ファイルの有無確認
読み込み時にファイルが存在しない場合はエラーが発生するため、このエラーを検出することでファイルの有無を確認します。
エラーが発生した場合は適切な処理を行う必要があるため、今回は新規ファイルを作成する方法を紹介します。
文字列ファイルの有無確認
「Pico」で電源ON時にファイルの有無を検出し、ファイルが有ればデータを読み込み、無ければ初期値(文字列)を指定して新規ファイルを作成するサンプルプログラムは以下になります。
「Thonny」等の開発画面にコピペで貼り付けて実行してください。
※プログラムのコピーは下の黒塗り部の右上アイコンクリックでもできます。
# 文字列データ読み出し(ファイルが無ければ新規作成)
path = '/data_str.txt' # パスの指定
try:
with open(path, 'r') as f: # ファイルを読み込みモードで開く
data_str = f.read() # ファイルの内容を読み込み
print("Read file str:", data_str) # 文字列表示
except OSError: # ファイルが存在しない場合のエラーハンドリング
with open(path, 'w') as f: # ファイルを書き込みモードで開く(新規作成)
f.write('Hello!') # 初期値「Hello!」を書き込む
print("Create new file:", path) # パス表示
動作確認のため本体に保存されているファイル「data_str.txt」を以下のように削除しましょう。
ファイルを削除してから、サンプルプログラムを実行するとエラーは発生せず、ファイル「data_str.txt」が初期値「Hello!」の内容で新規作成されます。
「シェル」部には以下のようにファイルのパス「/data_str.txt」が表示されます。
再度[実行]ボタンを押すと、ファイル「data_str.txt」が存在しているため、「シェル」部にファイルの内容「Hello!」が表示されます。
数値ファイルの有無確認
「Pico」で電源ON時にファイルの有無を検出し、ファイルが有ればデータを読み込み、無ければ初期値(数値)を指定して新規ファイルを作成するサンプルプログラムは以下になります。
「Thonny」等の開発画面にコピペで貼り付けて実行してください。
※プログラムのコピーは下の黒塗り部の右上アイコンクリックでもできます。
# 数値(文字列を整数に変換)読み出し(ファイルが無ければ新規作成)
path = '/data_int.txt' # パスの指定
try:
with open(path, 'r') as f: # ファイルを読み込みモードで開く
data_int = f.read().strip() # ファイルの内容を読み込み、前後の不要なデータ(空白、改行文字)を削除
data_int = int(data_int) # 読み込んだデータは文字列のため、整数に変換
print("Read file int:", data_int) # 数値表示
except OSError: # ファイルが存在しない場合のエラーハンドリング
with open(path, 'w') as f: # ファイルを書き込みモードで開く(新規作成)
f.write(str(0)) # 初期値0を書き込む(文字列のみ書き込み可)
print("Create new file:", path) # パス表示
ファイルが無い状態で、サンプルプログラムを実行してもエラーは発生せず、ファイル「data_int.txt」が初期値「0」の内容で新規作成されます。
「シェル」部には以下のようにファイルのパス「/data_int.txt」が表示されます。
再度[実行]ボタンを押すと、ファイル「data_int.txt」が存在しているため、「シェル」部にファイルの内容「0」が表示されます。
ファイル「data_int.txt」を開いて確認すると「0」が書き込まれていることが確認できます。
3.サンプルプログラム(計数カウンター)
使用例としてスイッチのON回数をカウントする「計数カウンター」のサンプルプログラムで動作確認していきます。
サンプルプログラムは以下になります。「Thonny」等の開発画面にコピペで貼り付けて実行してください。
※プログラムのコピーは下の黒塗り部の右上アイコンクリックでもできます。
from machine import Pin # 入出力モジュールを準備
# 入力ピン設定
sw0 = Pin(0, Pin.IN, Pin.PULL_UP) # スイッチ0入力設定:カウント+1用
sw1 = Pin(7, Pin.IN, Pin.PULL_UP) # スイッチ1入力設定:カウントリセット用
# 出力ピン設定
led = Pin('LED', Pin.OUT) # 本体LED出力設定
# 変数宣言
state0 = False # スイッチ0状態格納用
state1 = False # スイッチ1状態格納用
count = 0 # カウント値格納用
path = '/count.txt' # ファイルパス
# カウント初期値の読み出し(ファイルが無ければ新規作成)
try:
with open(path, 'r') as f: # ファイルを読み込みモードで開く
count = f.read().strip() # ファイルの内容を読み込み、前後の不要なデータ(空白、改行文字)を削除
count = int(count) # 読み込んだデータは文字列のため、整数に変換
print("Count:", count)
except OSError: # ファイルが存在しない場合のエラーハンドリング
with open(path, 'w') as f: # ファイルを書き込みモードで開く(新規作成)
f.write(str(0)) # 初期値0を書き込む(文字列で)
print("Create new file:", path) # パス表示
# 関数 -------------------------------------------------------------
# カウント値をファイルに書き込む(上書き)
def count_write(count):
with open(path, 'w') as f: # ファイルを書き込みモードで開く
f.write(str(count)) # 書き込み実行(文字列で)
# メイン処理 ---------------------------------------------------------
while True: # ずっと繰り返し
# カウントアップ
if sw0.value() == 0 and state0 == False: # スイッチ0が押されていてスイッチ状態がFalseなら
state0 = True # スイッチ状態をTrueへ
count += 1 # カウント+1
count_write(count) # カウント値書き込み関数
print(count)
led.value(1) # LEDを点灯
if sw0.value() == 1: # スイッチ0が離されたらLEDを消灯
state0 = False # スイッチ状態をFalseへ
led.value(0) # LEDを消灯
# カウントリセット
if sw1.value() == 0 and state1 == False: # スイッチ1が押されていてスイッチ状態がFalseなら
state1 = True # スイッチ状態をTrueへ
count = 0 # カウントリセット
count_write(count) # カウント値書き込み関数
print(count)
if sw1.value() == 1: # スイッチ1が離されたらLEDを消灯
state1 = False # スイッチ状態をFalseへ
4.動作確認用配線図
動作確認は以下の配線図のように「Pico」本体に「スイッチ」を「ブレッドボード」で接続して行います。
動作は以下のようになります。
青ボタン(スイッチ0)を押すとカウント値が加算されます。
押している間は本体LED緑が点灯します。
赤ボタン(スイッチ1)を押すとカウント値がリセットされて「0」になります。
5.動作確認
計数カウンターの動作確認を行っていきます。
初回の電源起動時には、以下のようにカウント値を保存するためのファイル「count.txt」が初期値「0」で本体フォルダ内に作成されます。
青ボタン(スイッチ0)を押すと、以下のように「シェル」部にカウント値が加算されて表示されます。
[ストップ]ボタンを押して、本体フォルダの「count.txt」ファイルを確認すると、最終のカウント値が保存されているのが確認できます。
再度[実行]ボタンを押すと以下のように「シェル」部に「Count: 5」が表示され、保存されたデータが読み込まれたことが確認できます。
青ボタン(スイッチ0)を押すと読み込まれた数値「5」からカウント値が加算されていくことが確認できます。
赤ボタン(スイッチ1)を押すと以下のように、カウント値はリセットされ「0」になります。
[ストップ]ボタンを押して、本体フォルダの「count.txt」ファイルを確認すると、リセットされたカウント値の「0」保存されていることが確認できます。
6.実用的なカウンター(割込処理、OLED表示)
カウンターとしての動作を紹介しましたが、カウント値の確認がパソコン上でしかできないため、単体で動作できて、カウント値を「OLED表示器」で確認できるようにした、実用的なカウンターについて最後に紹介します。
外観は下写真のようになります。
OLED表示器は「SSD1306」を使用しています。表示を行うには「ライブラリ」のインストールが必要です。OLED表示器の使い方については、以下のリンクで詳しく紹介しています。
・サンプルプログラム(OLED表示付カウンター)
サンプルプログラムは以下になります。「Thonny」等の開発画面にコピペで貼り付けて実行してください。
※プログラムのコピーは下の黒塗り部の右上アイコンクリックでもできます。
from machine import Pin, I2C # 入出力、I2Cモジュールを準備
import ssd1306 # 液晶表示器用ライブラリを使用
# OLED表示設定(SSD1306)
i2c1 = I2C(1, sda=2, scl=3) # I2C設定 (I2C識別ID 1, SDA, SCL)
i2c1_addr = i2c1.scan()[0] # 使用するSSD1306のアドレスを取得して表示(通常は0x3C)
print("OLED I2C1 Address :" + hex(i2c1_addr))
display = ssd1306.SSD1306_I2C(128, 64, i2c1) # ディスプレイ設定(幅, 高さ, 通信仕様)
# 入力ピン設定
sw0 = Pin(0, Pin.IN, Pin.PULL_UP) # スイッチ0入力設定:カウント+1用
sw1 = Pin(7, Pin.IN, Pin.PULL_UP) # スイッチ1入力設定:カウントリセット用
# 出力ピン設定
led = Pin('LED', Pin.OUT) # 本体LED出力設定
# 変数宣言
count = [0] # カウント値格納用(割り込み処理内で使用する変数は配列にする)
path = '/count.txt' # ファイルパスの指定
# カウント初期値の読み出し
try:
with open(path, 'r') as f: # ファイルを読み込みモードで開く
count[0] = f.read().strip() # ファイルの内容を読み込み、前後の不要なデータ(空白、改行文字)を削除
count[0] = int(count[0]) # 読み込んだデータは文字列のため、整数に変換
print("Count:", count[0]) # カウント表示
except OSError: # ファイルが存在しない場合のエラーハンドリング
with open(path, 'w') as f: # ファイルを書き込みモードで開く(新規作成)
f.write(str(0)) # 初期値0を書き込む(文字列で)
# 割り込み処理関数 -------------------------------------------------------------
# カウント+1、ファイルに書き込み(上書き)
def count_up(count):
count[0] += 1 # カウント+1
print(count[0]) # カウント数表示
led.value(1) # LEDを点灯
with open(path, 'w') as f: # ファイルを書き込みモードで開く
f.write(str(count[0])) # 書き込み実行(文字列で)
# カウント0リセット、ファイルに書き込み(上書き)
def count_reset(count):
count[0] = 0 # カウント0リセット
print(count[0]) # カウント数表示
with open(path, 'w') as f: # ファイルを書き込みモードで開く
f.write(str(count[0])) # 書き込み実行(文字列で)
# スイッチの割り込みを設定
sw0.irq(trigger=Pin.IRQ_FALLING, handler=lambda pin: count_up(count))
sw1.irq(trigger=Pin.IRQ_FALLING, handler=lambda pin: count_reset(count))
# メイン処理 ---------------------------------------------------------------
while True: # ずっと繰り返し
# 液晶画面表示内容設定
display.fill(0) # 表示内容消去
display.hline(0, 1, 128, True) # 指定座標から横線
display.text('UP COUNTER', 25, 5, True) # タイトル表示
display.hline(0, 15, 128, True) # 指定座標から横線
display.rect(5, 25, 118, 30, True) # (x, y, 幅, 高さ, 色)指定座標に四角
display.text('COUNT ={:6d}'.format(count[0]), 12, 35, True) # カウント数表示
# 設定した内容で表示実行
display.show()
# 本体LED消灯
if sw0.value() == True: # スイッチ0 OFFなら
led.value(0) # LEDを消灯
・配線例と動作確認
動作確認は以下の配線例のように「Pico」本体に「スイッチ」「OLED表示器(SSD1306)」を「ブレッドボード」で接続して行いました。
動作は以下のようになります。
青ボタン(スイッチ0)を押すとカウント値が加算されます。
押している間は本体LED緑が点灯します。
赤ボタン(スイッチ1)を押すとカウント値がリセットされて「0」になります。
7.まとめ
Raspberry Pi Picoで変数データを本体に保存して、起動時に読込んで使用する方法を紹介しました。
マイコンデバイスでは一般的な「EEPROM」が「ラズパイPico」にはありませんが、代わりに実行プログラムを保存している「メモリ領域」を利用してデータを保存することができます。
データを保存するには「Python」の「open関数」が使用できるため、比較的簡単にデータの保存、読み込みができます。
電源をOFFしてもデータは保持されるため、起動時にデータを読み込んで、前回の設定のままプログラムを再開することができます。
今回は実用例として、電源をOFFしてもカウント値を保持する「計数カウンター」を紹介しました。
以下リンクで紹介している「ラズパイ Pico シーケンサ」でもこのカウンターは欠かせないため、データ保持可能なカウンタも組み合わせた応用的な使用方法もまた紹介していきたいと思います。
コメント