MicroPython的午睡(33) ラズパイPico、S9705フォトICで照度を測る

Joseph Halfmoon

前々回、Raspberry Pi PicoのPWMカウンタ入力を制御して周波数を測定できるようになったので、今回はその応用であります。浜松ホトニクスS9705を使って照度(ルクス)を測ってみます。しかし、過去の投稿を読み返し、大間違いを発見、まずはそのお詫びと訂正から。

※「MicroPython的午睡」投稿順 Indexはこちら

大変申し訳ありません、昨年(2020年)の以下の投稿

部品屋根性(13) S9705、フォトICの中を覗く

にてS9705フォトICを取り上げさせていただいた折に、周波数と照度の簡易版の変換式など書いていたのですが、大間違い です。今回、Raspberry Pi PicoのMicroPython用の測定プログラム(末尾に全文あり)を書いていて気づきました。今となってはどのような理由で大嘘の式を算出したのか経緯は定かでありません。今回提示する以下のPython式の方がマシだと考えております(ちゃんと測定したい人は年寄りの戯言を鵜呑みにせず、自分で評価してくだされや。)

10 ** (math.log10(frq) * 0.967 – 2.699)

なお、上記式には換算可能な周波数範囲に制約あり、手元のデバイスでは10Hzから563.5kHzです。上限についてはデバイス固有のバラツキなどあるやと考えます。

S9705

まず、使用するセンサについてのメーカ・ページは以下です。”PHOTON IS OUR BUSINESS”、日本どころか世界のフォトンを背負ってらっしゃる浜松ホトニクス社の製品であります(会社名はホトニクスだけれどもフォトICです。)

浜松ホトニクス 照度-周波数変換フォトIC S9705シリーズ

上記の要訂正の投稿にて既に一度取り上げさせていただいておりますので、細かい事はパスしてラズパイPicoとの接続図から行きます。前々回、周波数測定スクリプトを作ったので、そのスクリプトでそのまま測定できるように回路を構成してみました。現物回路の写真はアイキャッチ画像に。

RPiPico_S9705 Schematic

スクリプト

末尾に動作試験で使用したスクリプト全文を掲げました。周波数測定については前々回のコードそのまま流用です。前々回書いたとおりでコードに改良の余地はあるのですが何もしておらず。結局、今回追加したのは、問題の周波数から照度への変換ルーチンの部分だけです。前々回作成のコードが「測ったように」このICの測定可能範囲に適合しているので(実際、あまり気にせず成り行きで作成したらばほぼほぼOK)、換算不能な上下の限界外をエラーではねたのちは単純な換算式で一発です(その割に過去回は換算間違えているな。)ラズパイPicoのRP2040は浮動小数点命令を持ちませんが、この程度であればソフトフロートでも処理できるのでいいかな。。。

測定例

まずは、デスクライトを徐々に近づけて測定してみました。こんな感じ。手元のデバイスの場合、563kHz付近でサチってしまいそれ以上に明るいところでは測れません。比較に使える正確な照度計があればこの値が雰囲気でているか、否かぐらいは確認できるのですが、照度計が手元にないので、数値が出る、ということだけ。

Frequency measured: 173260 [Hz]
Illuminance: 232.7136 [lux]

Frequency measured: 562720 [Hz]
Illuminance: 726.9979 [lux]

Frequency measured: 563910 [Hz]
Illuminance measurement overflow.

次に、黒のフェルトの布をブレッドボードに被せて測定してみました。ラズパイPicoのオンボードLEDが光っているので、完全に真暗にはできないですが、こんな感じ。

Frequency measured: 870 [Hz]
Illuminance: 1.391603 [lux]

Frequency measured: 100 [Hz]
Illuminance: 0.1717909 [lux]

まあ、見かけはよさげ。また、間違いでしたなどと言うなよ自分。そうだ、以前の投稿に訂正の追記をしなければ。。。

MicroPython的午睡(32) ラズパイPicoの処理系をようやくv1.15に更新 へ戻る

MicroPython的午睡(34) ラズパイPico、MEMSマイクのPDM生波形を取得 へ進む

いい加減な測定だが、とりあえず周波数を測って照度に変換するMicroPythonスクリプト

from machine import Pin, Timer, PWM, mem32
import time
import math

counter0 = 0

def readModifyWrite(adr, mask, dat):
    temp = mem32[adr] & mask
    mem32[adr] = temp | dat

def startPWMCtr(ch):
    PWM_BASE = 0x40050000
    if (ch < 0) or (ch > 7):
        return None
    ch_adr = ch * 0x14 + PWM_BASE
    readModifyWrite(ch_adr + 0x10, 0xFFFF0000, 0xFFFF)
    mem32[ch_adr + 0xC] = 0
    readModifyWrite(ch_adr + 8, 0xFFFF0000, 0)
    readModifyWrite(ch_adr + 4, 0xFFFFF000, 0x10)
    readModifyWrite(ch_adr + 0, 0xFFFFFF00, 0x21)
    
def stopPWMCtr(ch):
    PWM_BASE = 0x40050000
    if (ch < 0) or (ch > 7):
        return None
    ch_adr = ch * 0x14 + PWM_BASE
    work = mem32[ch_adr + 8]
    readModifyWrite(ch_adr + 0, 0xFFFFFF00, 0)
    return work & 0xFFFF

def timCB(timer):
    global counter0
    counter0 = stopPWMCtr(0)

def setGPIO1CTRL():
    IO_BANK0_BASE = 0x40014000
    gpio1_ctrl = IO_BANK0_BASE + 0x00c
    temp = mem32[gpio1_ctrl]
    mem32[gpio1_ctrl] = (temp & 0xFFFFFFE0) | 0x4

def s9705convFreq2Lux(frq):
    return 10 ** (math.log10(frq) * 0.967 - 2.699)
    

setGPIO1CTRL()

tim = Timer()
tim.init(mode=Timer.ONE_SHOT, period=100, callback=timCB) # measurement period = 100ms, *10 for Hz
startPWMCtr(0)
time.sleep(0.11) # wait 110ms
tim.deinit()
if counter0 is not None:
    print("Frequency measured: {0} [Hz]".format(counter0 * 10))
    if counter0 < 1:
        print("Illuminance measurement underflow.")
    elif counter0 >= 56350:
        print("Illuminance measurement overflow.")
    else:    
        print("Illuminance:        {0} [lux]".format(s9705convFreq2Lux(counter0 * 10)))
else:
    print("ERROR: Measurement.")