pinewell's farmer blog 農業と電子工作、ソフトウェア、バイク、車

2023年2月16日

RasberryPi Picoでの水温計作成 その5

Filed under: その他 — pinewell @ 11:24 PM

前回アップしたソースではGC9A01に表示させるフォントが気に入らなかった。

さらにbitmapの表示がどのようにしたらよいものかわからなかったので新たに探してみた。

https://github.com/russhughes/gc9a01_mpy

結果上記のサイトに行きつき、jpgファイル、.pyファイルへ変換してのbitmapファイルの表示方法が分かった。

とりあえず動作はする。実際に車載したときにどうなるか。

from machine import Pin,SPI,I2C,PWM
import gc9a01 # 内包されているfirmwareを使うこと。
import onewire, ds18x20
import time,utime,math

from ds1307 import DS1307 #libフォルダ

from fonts import chango_16 as fontA
from fonts import chango_32 as fontB

import hondaWing #bitmapのpythonスクリプト

def main():
    #iniファイルの保存
    def SaveIniFile(filename, dictionary):
        with open(filename, "w") as f:
            for key in dictionary:
                f.write("{},{}\n".format(key, dictionary[key]))
    #iniファイルの読み取り
    def LoadIniFile(filename):
        dictionary = {}
        with open(filename, "r") as f:
            for s in f:
                lst = s.strip().split(",")
                dictionary[lst[0]] = lst[1]
        return dictionary
    #使い方
    #data = {}
    #data["Name"] = "Hippy"
    #data["Year"] = "2021"
    #SaveIniFile("/MyData.ini", data)
    #data = LoadIniFile("/MyData.ini")
    #print(data)
      
  
       
    #arduinoのMAP関数
    def convert(x, in_min, in_max, out_min, out_max):
        return (x - in_min) * (out_max - out_min) // (in_max - in_min) + out_min

    #円を書いて塗りつぶす
    def circle(x,y,r,c):
       tft.hline(x-r,y,r*2,c)
       for i in range(1,r):
           a = int(math.sqrt(r*r-i*i))#平方根だって
           tft.hline(x-a,y+i,a*2,c)
           tft.hline(x-a,y-i,a*2,c)
    #円を書く
    def ring(x,y,r,c):
        tft.pixel(x-r,y,c)
        tft.pixel(x+r,y,c)
        tft.pixel(x,y-r,c)
        tft.pixel(x,y+r,c)
        #lcd.display()
        #utime.sleep(0.1)
        for i in range(1,r):
            a = int(math.sqrt(r*r-i*i)) # Pythagoras
            tft.pixel(x-a,y-i,c)
            tft.pixel(x+a,y-i,c)
            tft.pixel(x-a,y+i,c)
            tft.pixel(x+a,y+i,c)
            tft.pixel(x-i,y-a,c)
            tft.pixel(x+i,y-a,c)
            tft.pixel(x-i,y+a,c)
            tft.pixel(x+i,y+a,c)
            
    data = LoadIniFile("ini.ini")
    dspMode=data["dspMode"]
    face=("FaceA.jpg","FaceD.jpg")
   
    modePin=21  #時刻合わせモードに移行スイッチ
    upPin=20    #日付・時計あわせのアップスイッチ

    cds = machine.ADC(0) #明るさセンサー GP26がADC0
    # 16bitの数値一単位での電圧値を設定します
    unit = 0.00005035477
    
    kurai = 20000  #暗いとき
    akarui = 65535 #明るいとき
    
    dspMode = 0  #0:アナログ 1:デジタル
    mode    = 0  #0:通常 1:時計合わせ 
    sec     = 0  #時計合わせの時のセクション 0:Year 1:month 2 day 3 hour 4 minute 
    uYear   = 0  #時計あわせようの値
    uMonth  = 0
    uDate   = 0
    uHour   = 0
    uMinute = 0

    epSec = 0

    swMode = machine.Pin(modePin,machine.Pin.IN,machine.Pin.PULL_DOWN) #スイッチの定義
    swUp   = machine.Pin(upPin,machine.Pin.IN,machine.Pin.PULL_DOWN)   #スイッチの定義

    pushStart =0 #押し始めた時間
    flgPush = False #押しているか
    pushInt = 0 #押されていた時間

    DSPIN = 19 #温度計のPin。onewire
    dsCnt = 0  #温度計の数
    
    ds_pin = machine.Pin(DSPIN)
    ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin)) #温度計を定義
    
    pw33 = machine.Pin(18,machine.Pin.OUT)   #RTCの3.3Vを得るため
    pw33.value(1)
    
    tokei=True #RTCが取得できたかフラグ


    try:   #RTCから現在時刻の取得
      i2c_rtc = I2C(0,scl = Pin(17),sda = Pin(16),freq = 100000) #RTCの定義
      result = I2C.scan(i2c_rtc)

      rtc = DS1307(i2c_rtc)
      (year,month,date,day,hour,minute,second,p1)=rtc.datetime()

      #RTCから取得した時間を元に 2021/1/1 00:00:00(RP2040のリセット時間)との差分を作成
      epSec = utime.mktime((year,month,date,hour,minute,second,0,0)) #1970/1/1 00:00:00からの経過秒
      epSec = epSec-1609459200
      
      if year==2000:#RTCからの初取得は2000年となるのでその場合に 2023/2/2 12:00:00にRTCを設定 
          year = 2023
          month = 2 
          date = 2 
          day = 4 
          hour = 12
          minute = 0
          second = 0
        
          now = (year,month,date,day,hour,minute,second,0)
          rtc.datetime(now)
    except:
        print("not Tokei")  #RTCから取得できなかったら tokei非表示
        tokei=False
        
    o_ds = bytearray(b'(\x07\x83t/!\x01(') #温度計の個体を特定 外気温
    w_ds = bytearray(b'(?\x9b8/!\x01\xd4') #                水温
    
    try:
        roms = ds_sensor.scan()  #センサーを取得
        dsCnt=len(roms)          #センサー数を得る
        for rom in roms:
            print('Found DS devices: ', rom)
    except:
        dsCnt = 0
    
    #液晶ディスプレイの定義
    spi = SPI(1, baudrate=60000000, sck=Pin(10), mosi=Pin(11))
        
    tft = gc9a01.GC9A01(#バックライトはPWMで調整するので指定しない。
        spi,
        240,
        240,
        reset=Pin(12, Pin.OUT),
        cs=Pin(9, Pin.OUT),
        dc=Pin(8, Pin.OUT),
        rotation=0)

    #バックライトはPWMで調整
    pwm = PWM(Pin(25))
    pwm.freq(1000)
    pwm.duty_u16(akarui)
    tft.init()
    
    tft.jpg(face[dspMode], 0, 0, gc9a01.SLOW)
    
    oldx =120
    oldy = 120
    
    
    while True:
        for i in range(100): #バックライトの明るさ調整
            # ADCの値を読み込みます(16bitの生の数値)
            voltRaw = cds.read_u16()
            #print("voltRaw:" + str(voltRaw))
            volt = voltRaw * unit
            #print( "volt:" + "{:.3f}".format(volt))
        if volt > 2:
            pwm.duty_u16(kurai)
        else:
            pwm.duty_u16(akarui)
 
        #モードスイッチが押されているか?
        if swMode.value() == 1:
            if flgPush : #長押中
                pushInt = utime.time()-pushStart
            else:
                flgPush = True #長押し計測スタート
                pushStart = utime.time()
                pushInt = 0
        else:
            if flgPush :# すでに押されていて離した
                flgPush=False
                pushInt=utime.time()-pushStart #押していた時間を取得
                if pushInt > 2: #長押しされた
                    #print("long") 
                    if mode ==0: #時計合わせモードに移行
                        tft.fill(gc9a01.WHITE)
                        (uYear,uMonth,uDate,day,uHour,uMinute,second,p1)=rtc.datetime() #
                        mode=1
                        sec=0
                    else:   #時計合わせモードで長押し
                        if sec == 4:#分まで設定してたら時計合わせ終了
                            now = (uYear,uMonth,uDate,0,uHour,uMinute,0,0)
                            rtc.datetime(now)
                            sec=0
                            mode=0
                            tft.fill(gc9a01.WHITE)
                            tft.jpg(face[dspMode], 0, 0, gc9a01.SLOW)
                            oldx=120
                            oldy=120
                        else:#次のセクションへ移行
                            sec=sec+1
                else:
                    print("short") #短押しは表示モード切り替え
        #調整用UPボタンの処理
        if swUp.value() == 1:
            #print("up")
            if tokei : #時計表示ができる。 
                if mode == 1: #時刻調整モード
                    utime.sleep_ms(200)        
                    if sec ==0:
                        uYear=uYear+1
                        if uYear>2100:
                            uYear=2023
                    elif sec ==1:
                        uMonth=uMonth+1
                        if uMonth>12:
                            uMonth=1
                    elif sec ==2:
                        uDate=uDate+1
                        if uDate>31:
                            uDate=1
                    elif sec ==3:
                        uHour=uHour+1
                        if uHour>23:
                            uHour=0
                    elif sec ==4:
                        uMinute=uMinute+1
                        if uMinute>59:
                            uMinute=0
                else :
                    #時刻設定モードじゃ無い場合は表示モード切り替え
                    if dspMode==0:
                        dspMode=1
                        tft.jpg(face[dspMode], 0, 0, gc9a01.SLOW)
                        data["dspMode"] = dspMode
                        SaveIniFile("/ini.ini", data)
                    else:
                        dspMode=0
                        data["dspMode"] = dspMode
                        SaveIniFile("/ini.ini", data)
                        tft.jpg(face[dspMode], 0, 0, gc9a01.SLOW)
                        oldx=120
                        oldy=120
                        
                #print(str(uYear)+"/"+str(uMonth)+"/"+str(uDate) + "  " + str(uHour)+":"+str(uMinute)+":00")

        
        if mode ==0: #通常表示
            #温度の取得
            oTemp = 999.99
            wTemp = 999.99
            oReadErr=""
            wReadErr=""
            if dsCnt > 0 :#温度計の数が0以上だったら
                flgSuc=True
                try:
                    ds_sensor.convert_temp()
                    utime.sleep_ms(750)
                except:
                    flgSuc=False
                if flgSuc:
                    for rom in roms:
                        if rom == o_ds :
                            buf = oTemp
                            try:
                                oTemp = ds_sensor.read_temp(rom)
                                oReadErr="O"
                            except:
                                oReadErr="X"
                                oTemp=buf
                        else :
                            buf = wTemp
                            try:
                                wTemp = ds_sensor.read_temp(rom)
                                wReadErr="O"
                            except:
                                wReadErr="X"
                                wTemp=buf
            
            if dspMode==0:
                wwTemp = 20 #最小温度            
                if wTemp <=120 or wTemp > 20: #読み取れている?
                    wwTemp = wTemp
        
                kakudo = convert(wwTemp,20,120,40,320) #角度を取得
                rad = 3.14*kakudo/180 #ラジアン値に変換
                r=90  #針の長さ
                x = int(r*(math.cos(rad))) + 120 #座標を求める
                y = int(r*(math.sin(rad))) + 120 

                c=gc9a01.BLACK
                if wTemp >80:
                    c=gc9a01.RED
                elif wTemp<40:
                    c=gc9a01.BLUE
            
                tft.write(fontA, "w={:.1f}  ".format(wTemp),150,105,c,gc9a01.WHITE)
                tft.write(fontA, "o={:.1f}  ".format(oTemp),155,120,gc9a01.BLACK,gc9a01.WHITE)
        
                if (x != oldx) or (y !=oldy) :
                  tft.line(122, 120 ,oldx ,oldy, gc9a01.WHITE)
                  tft.line(121, 121 ,oldx ,oldy, gc9a01.WHITE)
                  tft.line(120, 120, oldx, oldy, gc9a01.WHITE)
                  tft.line(120, 119 ,oldx ,oldy, gc9a01.WHITE)
                  tft.line(120, 121, oldx, oldy, gc9a01.WHITE)
                  tft.line(119, 119 ,oldx ,oldy, gc9a01.WHITE)
                  tft.line(119, 120 ,oldx ,oldy, gc9a01.WHITE)
                  tft.line(118, 120 ,oldx ,oldy, gc9a01.WHITE)
                  tft.line(120, 118 ,oldx ,oldy, gc9a01.WHITE)

                  tft.write(fontA, "20",175, 162,gc9a01.BLACK,gc9a01.WHITE)
                  tft.write(fontA, "40",94, 190,gc9a01.BLACK,gc9a01.WHITE)
                  tft.write(fontA, "60",40, 148,gc9a01.BLACK,gc9a01.WHITE)
                  tft.write(fontA, "80",40, 77,gc9a01.BLACK,gc9a01.WHITE)
                  tft.write(fontA, "100",94, 31,gc9a01.BLACK,gc9a01.WHITE)
                  tft.write(fontA, "120",175, 61,gc9a01.BLACK,gc9a01.WHITE)
                  tft.bitmap(hondaWing,120-int(hondaWing.WIDTH/2),50)
          
                  tft.line(122, 120 ,x ,y, gc9a01.RED)
                  tft.line(121, 121 ,x ,y, gc9a01.RED)
                  tft.line(120, 120, x, y, gc9a01.RED)
                  tft.line(120, 119 ,x ,y, gc9a01.RED)
                  tft.line(120, 121, x, y, gc9a01.RED)
                  tft.line(119, 119 ,x ,y, gc9a01.RED)
                  tft.line(119, 120 ,x ,y, gc9a01.RED)
                  tft.line(118, 120 ,x ,y, gc9a01.RED)
                  tft.line(120, 118 ,x ,y, gc9a01.RED)
        
                  oldx = x
                  oldy = y

                circle(120,120,10,gc9a01.BLACK)
            else :
                # 温度の表示 右寄せ
                tft.write(fontA, "WaterTemp",40,68,gc9a01.BLACK,gc9a01.WHITE)
                w = tft.write_len(fontB,"    {:.2f}".format(wTemp))
                tft.write(fontB, "    {:.2f}".format(wTemp),210-w,86,gc9a01.BLACK,gc9a01.WHITE)
                tft.write(fontA,wReadErr,40,90,gc9a01.BLACK,gc9a01.WHITE)
                tft.write(fontA, "OutTemp",40,128,gc9a01.BLACK,gc9a01.WHITE)
                w = tft.write_len(fontB,"    {:.2f}".format(oTemp))
                tft.write(fontB, "    {:.2f}".format(oTemp),210-w,146,gc9a01.BLACK,gc9a01.WHITE)
                tft.write(fontA,oReadErr,40,150,gc9a01.BLACK,gc9a01.WHITE)

            if tokei :
                it = utime.mktime(utime.localtime()) #ローカルでは 2021/1/1 00:00:00として起動した1970/1/1 00:00:00からの経過秒
                it = it+epSec                        #RTC差分の値とあわせて
                time =   utime.localtime(it)         #本当の現在時刻を生成
                year=time[0]
                month=time[1]
                date=time[2]
                hour=time[3]
                minute=time[4]
                second=time[5]
                if dspMode==1:
                    dateStr = "{0:04d}/ {1:02d}/ {2:02d}  ".format(year,month,date)
                    tft.write(fontA, dateStr,56, 195,gc9a01.BLACK,gc9a01.WHITE)
                    tft.write(fontA, "                ",85, 178,gc9a01.BLACK,gc9a01.WHITE)
                timeStr = "{0:02d}:{1:02d}:{2:02d}  ".format(hour,minute,second)
                tft.write(fontA, timeStr,80, 213,gc9a01.BLACK,gc9a01.WHITE)
                
                
        else: #時計合わせ
            
            dateStr = "{0:04d}/ {1:02d}/ {2:02d}  ".format(uYear,uMonth,uDate)
            tft.write(fontA, dateStr,60, 195,gc9a01.BLACK,gc9a01.WHITE)
            timeStr = "{0:02d}:{1:02d}:00      ".format(uHour,uMinute)
            tft.write(fontA, timeStr,82, 213,gc9a01.BLACK,gc9a01.WHITE)
            if sec ==0:
                tft.write(fontA,"year       ",85, 178,gc9a01.BLACK,gc9a01.WHITE)
            elif sec ==1:
                tft.write(fontA,"month      ",85, 178,gc9a01.BLACK,gc9a01.WHITE)
            elif sec ==2:
                tft.write(fontA,"date       ",85, 178,gc9a01.BLACK,gc9a01.WHITE)
            elif sec ==3:
                tft.write(fontA,"hour       ",85, 178,gc9a01.BLACK,gc9a01.WHITE)
            elif sec ==4:
                tft.write(fontA,"minute ",85, 178,gc9a01.BLACK,gc9a01.WHITE)
        utime.sleep_ms(10)        
main()

2023年2月15日

RasberryPi Picoでの水温計作成 その4

Filed under: その他 — pinewell @ 11:38 PM

実際にはPicoでは無いのだが。

画面

さてなんやかんやあって上記画像通り、アナログでの表示に成功。

sinとかcosとかラジアンとか高校のころ以来。

w=で水温 o=外気温となる。

水温は40℃以下は青色表示、80℃以上は赤表示とする。

ただ問題ががが。実際にエンジンをかけるとノイズなのかまともに動かない。

とりあえずRTCからの時間取得で失敗していたのでRTCから取得はエンジン始動前の1回にしてみた。

その後寒くてエンジン始動も倉庫内の作業も困難で試してない。

2023年2月7日

RasberryPi Picoでの水温計作成 その3

Filed under: その他 — pinewell @ 10:44 PM

Fritzingを改めて使ってみる。

パーツを作るのが面倒・・・。

必要な情報を貼っていく。

RP2040-LCD-1.28

2つあるヘッダのうち、H2のみを使う。

PICO

本来のPicoとはすこーし違うよう。

PiRTC

Pi用のRTC。DS1307は5V駆動なので3.3Vへのロジックレベル変換が内蔵されている。が、3.3Vも繋ぐ必要があるのは誤算だった。

ブレッドボードで再現

これを収めるケースを作らねば。

2023年2月5日

RasberryPi Picoでの水温計作成 その2

Filed under: その他 — pinewell @ 9:44 PM

DS18B20の取得はOneWireを使うと1つのGPIOで取得できる。

周りの明るさで輝度を調整したかったので、いつ買ったのかもわからないセンサーを接続した。

I2CでRTC。DS1307を用いた。

from machine import Pin, SPI,I2C,PWM
import gc9a01py as gc9a01
import onewire, ds18x20
import time,utime

from ds1307 import DS1307

# from fonts import vga1_8x8 as font
# from fonts import vga2_8x8 as font
# from fonts import vga1_8x16 as font
# from fonts import vga2_8x16 as font
# from fonts import vga1_16x16 as font
# from fonts import vga1_bold_16x16 as font
# from fonts import vga2_16x16 as font
# from fonts import vga2_bold_16x16 as font
# from fonts import vga1_16x32 as font
# from fonts import vga1_bold_16x32 as font
# from fonts import vga2_16x32 as font
# from fonts import vga2_bold_16x32 as font
# from fonts import vga2_bold_16x32 as fontB
# from truetype import NotoSerif_32 as fontC

from fonts import vga2_8x16 as fontA
from fonts import vga2_16x32 as fontB


def main():
    modePin=21  #時刻合わせモードに移行スイッチ
    upPin=20    #日付・時計あわせのアップスイッチ

    cds = machine.ADC(0) #明るさセンサー
    # 16bitの数値一単位での電圧値を設定します
    unit = 0.00005035477
    
    kurai = 20000  #暗いとき
    akarui = 65535 #明るいとき
    
    mode    = 0   #0:通常 1:時計合わせ 
    sec     = 0  #時計合わせの時のセクション 0:Year 1:month 2 day 3 hour 4 minute 
    uYear   = 0  #時計あわせようの値
    uMonth  = 0
    uDate   = 0
    uHour   = 0
    uMinute = 0

    swMode = machine.Pin(modePin,machine.Pin.IN,machine.Pin.PULL_DOWN) #スイッチの定義
    swUp   = machine.Pin(upPin,machine.Pin.IN,machine.Pin.PULL_DOWN)   #スイッチの定義

    pushStart =0 #押し始めた時間
    flgPush = False #押しているか
    pushInt = 0 #押されていた時間

    DSPIN = 19 #温度計のPin。onewire
    dsCnt = 0  #温度計の数
    ds_pin = machine.Pin(DSPIN)
    ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin)) #温度計を定義
    
    pw33 = machine.Pin(18,machine.Pin.OUT)   #RTCの3.3Vを得るため
    pw33.value(1)
    
    tokei=True #RTCが取得できたか?
    
    try:
      i2c_rtc = I2C(0,scl = Pin(17),sda = Pin(16),freq = 100000) #RTCの定義
      result = I2C.scan(i2c_rtc)
      #print(result)

      rtc = DS1307(i2c_rtc)
      #print(rtc.datetime())
      (year,month,date,day,hour,minute,second,p1)=rtc.datetime()
      
      if year==2000:#初期時は2000年となるのでその場合に 2023/2/2 12:00:00に設定
          year = 2023
          month = 2 
          date = 2 
          day = 4 
          hour = 12
          minute = 0
          second = 0
        
          now = (year,month,date,day,hour,minute,second,0)
          rtc.datetime(now)
    except:
        #print("not Tokei")  #RTCが取得できなかったら tokei非表示
        tokei=False
        
    o_ds = bytearray(b'(?\x9b8/!\x01\xd4') #温度計の個体を特定 外気温
    w_ds = bytearray(b'(\x07\x83t/!\x01(') #                 水温

    try:
        roms = ds_sensor.scan()  #センサーを取得
        dsCnt=len(roms)          #センサー数を得る
        for rom in roms:
            print('Found DS devices: ', rom)
    except:
        dsCnt = 0
    
    #液晶ディスプレイの定義
    spi = SPI(1, baudrate=60000000, sck=Pin(10), mosi=Pin(11))
    #tft = gc9a01.GC9A01( 
    #    spi,
    #    dc=Pin(8, Pin.OUT),
    #    cs=Pin(9, Pin.OUT),
    #    reset=Pin(12, Pin.OUT),
    #    backlight=Pin(25, Pin.OUT),
    #    rotation=0)
    tft = gc9a01.GC9A01(#バックライトはPWMで調整するので指定しない。
        spi,
        dc=Pin(8, Pin.OUT),
        cs=Pin(9, Pin.OUT),
        reset=Pin(12, Pin.OUT),
        rotation=0)
    
    
    #バックライトはPWMで調整
    pwm = PWM(Pin(25))
    pwm.freq(1000)
    pwm.duty_u16(akarui)
    #pwm.duty_u16(20000)

    tft.fill(gc9a01.WHITE)
    tft.fill_rect(0,0,240,60,gc9a01.RED)
    tft.text(fontB,"HONDA",80,20,gc9a01.WHITE,gc9a01.RED)
              
    while True:
        for i in range(100): #バックライトの明るさ調整
            # ADCの値を読み込みます(16bitの生の数値)
            voltRaw = cds.read_u16()
            #print("voltRaw:" + str(voltRaw))
            volt = voltRaw * unit
            #print( "volt:" + "{:.3f}".format(volt))
        if volt > 2:
            pwm.duty_u16(kurai)
        else:
            pwm.duty_u16(akarui)
 
        #モードスイッチが押されているか?
        if swMode.value() == 1:
            if flgPush :
                pushInt = time.time()-pushStart
            else:
                flgPush = True
                pushStart = time.time()
                pushInt = 0
        else:
            if flgPush :# すでに押されていて離した
                flgPush=False
                pushInt=time.time()-pushStart
                if pushInt > 2: #長押しされた
                    #print("long") 
                    if mode ==0: #時計合わせモードに移行
                        (uYear,uMonth,uDate,day,uHour,uMinute,second,p1)=rtc.datetime() #
                        mode=1
                        sec=0
                    else:   #時計合わせモードで長押し
                        if sec == 4:#分まで設定してたら時計合わせ終了
                            now = (uYear,uMonth,uDate,0,uHour,uMinute,0,0)
                            rtc.datetime(now)
                            sec=0
                            mode=0
                        else:#次のセクションへ移行
                            sec=sec+1
                else:
                    print("short") #短押しはなにもしない

        if swUp.value() == 1:
            if mode == 0: 
                (year,month,date,day,hour,minute,second,p1)=rtc.datetime()
                print(str(year)+"/"+str(month)+"/"+str(date) + "  " + str(hour)+":"+str(minute)+":"+str(second))
            else:
                if sec ==0:
                    uYear=uYear+1
                    if uYear>2100:
                        uYear=2023
                elif sec ==1:
                    uMonth=uMonth+1
                    if uMonth>12:
                        uMonth=1
                elif sec ==2:
                    uDate=uDate+1
                    if uDate>31:
                        uDate=1
                elif sec ==3:
                    uHour=uHour+1
                    if uHour>23:
                        uHour=0
                elif sec ==4:
                    uMinute=uMinute+1
                    if uMinute>59:
                        uMinute=0
                #print(str(uYear)+"/"+str(uMonth)+"/"+str(uDate) + "  " + str(uHour)+":"+str(uMinute)+":00")

        #温度の取得
        oTemp = 999.99
        wTemp = 999.99
        if dsCnt > 0 :
            ds_sensor.convert_temp()
            for rom in roms:
                if rom == o_ds :
                    oTemp = ds_sensor.read_temp(rom)
                else :
                    wTemp = ds_sensor.read_temp(rom)
        
        # 温度の表示
        tft.text(fontA,"WaterTemp",50,62,gc9a01.BLACK,gc9a01.WHITE)
        tft.text(fontB,"{:.2f}".format(wTemp),100,80,gc9a01.BLACK,gc9a01.WHITE)
        tft.text(fontA,"OutTemp",50,122,gc9a01.BLACK,gc9a01.WHITE)
        tft.text(fontB,"{:.2f}".format(oTemp),100,140,gc9a01.BLACK,gc9a01.WHITE)

        
        if mode ==0: #通常表示
            if tokei :
                (year,month,date,day,hour,minute,second,p1)=rtc.datetime()
        
                dateStr = "{0:04d}/ {1:02d}/ {2:02d}".format(year,month,date)
                tft.text(fontA,dateStr,85,190,gc9a01.BLACK,gc9a01.WHITE)
                timeStr = "{0:02d}:{1:02d}:{2:02d}".format(hour,minute,second)
                tft.text(fontA,timeStr,92 ,208,gc9a01.BLACK,gc9a01.WHITE)
                tft.text(fontA,"      ",85 ,174,gc9a01.BLACK,gc9a01.WHITE)
                
        else: #時計合わせ
            dateStr = "{0:04d}/ {1:02d}/ {2:02d}".format(uYear,uMonth,uDate)
            tft.text(fontA,dateStr,85,190,gc9a01.BLACK,gc9a01.WHITE)
            timeStr = "{0:02d}:{1:02d}:00".format(uHour,uMinute)
            tft.text(fontA,timeStr,92 ,208,gc9a01.BLACK,gc9a01.WHITE)
            if sec ==0:
                tft.text(fontA,"year  ",85 ,174,gc9a01.BLACK,gc9a01.WHITE)
            elif sec ==1:
                tft.text(fontA,"month ",85 ,174,gc9a01.BLACK,gc9a01.WHITE)
            elif sec ==2:
                tft.text(fontA,"date  ",85 ,174,gc9a01.BLACK,gc9a01.WHITE)
            elif sec ==3:
                tft.text(fontA,"hour  ",85 ,174,gc9a01.BLACK,gc9a01.WHITE)
            elif sec ==4:
                tft.text(fontA,"minute",85 ,174,gc9a01.BLACK,gc9a01.WHITE)
main()

これでスイッチ2個で外気温、水温、時計と照度計を用いた水温計が実現できる。

2023年1月29日

OrangePi zero2を用いた基準局設備

Filed under: GNSS — pinewell @ 9:31 PM

本来ならRaspberryPiを使いたいのだが入手困難な上、高額となってしまったため試行錯誤。

機能的には充分なOrangePi zero2にしてみた。お値段も手頃。

OSにはarmbianを選択。これとPX1122R-EVBをTX/RXで接続して基準局データを送信することができることを確認。

で、ケースをRaspberryPiと同じサイズにで作成すると上部に余ったスペースが出来た。

まぁケースを小くすることも考えたが、折角なのでOLEDの液晶とタクトスイッチをユニバーサル基板に半田付けして、と。

OLEDの液晶はSSD1306を用いたi2c接続で128×64の解像度。アマゾンでも買える。

まぁ、難儀しましたよ。Raspiと違って圧倒的に情報量が少ない。しかもOrangepi は種類が多くてサンプルがあってもzero2では動かないとか。

pythonのOPi.GPIOも結局まともに動作させられず、pythonからsubprocessを呼び出すなんて美しくない方法でタクトスイッチのON/OFFを取得せざるを得ず。

LCDにはIPアドレスと現在時刻。タクトスイッチの状態を表示。スイッチを5秒以上長押しでシャットダウンできるようにしてみた。

キモはWiringOPでのGPIOコマンドとタクトスイッチの状態を確定させるための10kΩプルダウン抵抗。

#!/usr/bin/env python                                             

import subprocess                                                 
import datetime                                                   
import time                                                       
from oled.device import ssd1306                                   
from oled.render import canvas                                    
from PIL import ImageFont                                         
                                                                  
import ipget                                                      
                                                                  
while True:                                                       
  try:                                                            
    target_ip = ipget.ipget()                                     
    ip = target_ip.ipaddr("eth0")                                 
    break;                                                        
  except:                                                         
    time.sleep(5)                                                 
                                                                                                                                   
device = ssd1306(port=3, address=0x3C)  # rev.1 users set port=0  
                                                                  
font = ImageFont.load_default()                                   

flgPush = False #押されているか                                      
pushStart =0 #押された時間                                           
pushInt = 0                                                          
onStr="OFF"                                                          
                                                                     
flgShutdown = False                                                  
                                                                     
result = subprocess.run(                                             
        "/usr/local/bin/gpio mode 2 in",shell=True,                  
        stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True)     

while True:                                                                              
    result = subprocess.run(                                                             
            "/usr/local/bin/gpio read 2",shell=True,                                     
            stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True)                     
    val=result.stdout[0:1]                                                               
    if val == "1" :                                                                      
        if flgPush : #既におされているか?                                               
          pushInt = time.time()-pushStart                                                
        else :                                                                           
          flgPush=True                                                                   
          pushStart = time.time()                                                        
          pushInt = 0                                                                    
        onStr="ON"                                                                       
    else:                                                                                
        if flgPush :#既におされていて離した                                              
          flgPush=False                                                                  
          pushInt = time.time()-pushStart                                                
          onStr="OFF"                                                                    
          if pushInt > 5 :                                                               
              flgShutdown = True                                                         
              onStr="Shutdown..."                                                        
        else :                                                                           
          flgPush=False
          pushInt = 0
          onStr="OFF"

    with canvas(device) as draw:
       font = ImageFont.load_default()
       draw.text((0, 0), ip   ,  font=font, fill=255)
       t_delta = datetime.timedelta(hours=9)
       JST = datetime.timezone(t_delta,'JST')
       now = datetime.datetime.now(JST)
       d=now.date().strftime('%Y/%m/%d')
       draw.text((0, 12),d  ,  font=font, fill=255)
       t=now.time().strftime('%X')
       draw.text((0, 24),t  ,  font=font, fill=255)

       draw.text((0, 40),onStr  ,  font=font, fill=255)
       if not flgShutdown :
         if pushInt>0:
           draw.text((40,40),'{:.2f}'.format(pushInt),font=font,fill=255)
    if flgShutdown :
        break
    time.sleep(0.3)

time.sleep(3)
with canvas(device) as draw:
  draw.rectangle((0, 0, device.width-1, device.height-1), outline=0, fill=0)

print("shutdown")
result = subprocess.run(
         "/usr/sbin/shutdown -h now",shell=True,
         stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True)

2023年1月26日

RasberryPi Picoでのメーター作成

Filed under: その他 — pinewell @ 2:01 PM

バイクの水温計を作成したい。

ついては以下の要件

1.水温

2.外気温

3.時計

4.振動センサーによる盗難防止ブザー

Waveshare RP2040-LCD-1.28 というものを購入した。

温度計のために GPIO 2つ

時計合わせのためのボタンに2つ。

RTCのI2Cに2つ。

6つの空きGPIOが必要。

2022年7月16日

NTRIP Caster SNIPの購入

Filed under: GNSS — pinewell @ 10:32 PM

基準局が3つになったのでLite版ではいろいろと使い辛く、BASICを購入。

実は2021/4/1に研究用として購入していたライセンスがある。この時は$851で購入。

2つのサーバーに使用するため今回追加で1ライセンスと思ったら$950に値上げされていた。円安も伴い、実際の請求はいくらになることやら。

PX1122Rでの5波対応基準局は上手く動いている。みちびき対応トリンブルで確認してもらったところ、QZSSを使用して補正されているとのこと。

GPS7500を使用した基準局をPX1122Rに置き換えるために「NS-HP-GN2 : PX1122R MULTI-BAND QUAD-GNSS RTK BREAKOUT BOARD」を発注。

こちらは$99。必要十分。

2022年6月29日

PX1122R を使用してのRTK基準局

Filed under: GNSS — pinewell @ 4:47 PM

NavSpark Store で購入したPX1122R-EVB が届いた。

早速自局の位置をRTKでFIX。そしてBaseモードにして基準局が設定できた。

ZED-F9PではできなかったGPS,GLONASS,QZSS,Galileo,Beidouの5波対応基準局となる。

AgLeaderにてFIXできることを確認。悲しい事にAgLeaderではGPSとGLONASSのみなので恩恵はまったくなし。

後日TrimbleなりのQZSSに対応したROVERで確認してみないと。

2021年1月18日

NtripCasterのロードバランサー

Filed under: GNSS — pinewell @ 11:01 PM

NtipCasterにSNIPを使用している。これはさくらのVPSでWindowsServerを借りての運用。耐障害性を考えて2台での運用していた。

2020年はそれぞれrtk1 とか rtk2 として接続先を変えていたのだが、本年より接続ユーザー数が30、クライアントとしては50台くらいにはなるのだろうか?

負荷分散を考えるに手動で設定するのは面倒。なのでロードバランサーの検討/導入となった。

単純にDNSラウンドロビンでも良いのだが、サーバーダウン時には対応が出来ないとなると二の足を踏んだ。

CentOSにてLVSとKeepalivedでの構築も考えたのだが(全て石狩データセンターなのでそれぞれがローカルでも繋っている)、ntripcasterはただのhttpサーバーとは違い、NtripServerからの基準局データを受け付けなければならず、これも断念。(実サーバーのdefaultgatewayがロードバランサーにしなければならず、globalIPでのntripserverの受付ができない。)

そこでさくらのクラウドにあるGSLB(広域負荷分散)を用いることとした。

NtripServerからの基準局データ送信は caster1.example.com と caster2.example.comとしてDNSにて登録されたglobalIPへ送信する。

NtripClientからは rtk.example.com としてDNSにてCNAMEされた GSLBのFQDNへ変換。さくらのGSLBに登録されている実サーバーのうち、適切なサーバーのglobalIPをクライアントに返すように設定ができた。

料金は月額550円。これは安い!

2021年1月16日

NTRIP 接続

Filed under: GNSS — pinewell @ 2:24 PM

ntripcaster 0.1.5 を使用してlinux鯖によるrtk配信サービスを構築するつもり。

”Lefebure NTRIP Client”での接続でローカル環境でのテストではOKなのにインターネット上のサーバーに接続すると認証エラーでつながらない。

ソースから main.c のinfo.logfiledebuglevel = 0 を 9にして全デバッグ情報を確認すると どういうわけか接続時の文字列の分解に失敗しているよう。

[Authorization] == [Basic XXXXXXXXXXXXXXXX] が取得できていない。

[Connectiotion] == [Basic XXXXXXXXXXXXXXXX]になってしまっているので client.c をいじって [Authorization]の取得に失敗していたら [Connectiotion]の値を使って認証させるように(場当たり的ではあるが)修正した。

AgLeaderではどうかと思い確認すると[Authorization] == [Basic XXXXXXXXXXXXXXXX]が無事取得できているのでまぁよし。

GET /AGS HTTP/1.0
User-Agent: NTRIP AgLeader/1.0 (InCommand)
Host: 192.168.1.254
Authorization: Basic XXXXXXXXXXXX

[16/Jan/2021:13:57:58] [4:Connection Handler] DEBUG: Building request out of [GET /AGS HTTP/1.0]
[16/Jan/2021:13:57:58] [4:Connection Handler] DEBUG: Building clean request from [/AGS]
[16/Jan/2021:13:57:58] [4:Connection Handler] DEBUG: Adding varpair [User-Agent] == [NTRIP AgLeader/1.0 (InCommand)]
[16/Jan/2021:13:57:58] [4:Connection Handler] DEBUG: Building request out of [Host: 192.168.1.254]
[16/Jan/2021:13:57:58] [4:Connection Handler] DEBUG: Adding varpair [Authorization] == [Basic XXXXXXXXXXXX]
[16/Jan/2021:13:57:58] [4:Connection Handler] DEBUG: Checking need for authentication on mount /AGS

 

GET /AGS HTTP/1.0
User-Agent: NTRIP LefebureAndroidIntNTRIPClient/20200406
Accept: */*
Connection: close
Authorization: Basic XXXXXXXXXXXX

[15/Jan/2021:14:48:04] [3:Connection Handler] DEBUG: Building request out of [GET /AGS HTTP/1.0]
[15/Jan/2021:14:48:04] [3:Connection Handler] DEBUG: Building clean request from [/AGS]
[15/Jan/2021:14:48:04] [3:Connection Handler] DEBUG: Adding varpair [User-Agent] == [NTRIP LefebureAndroidIntNTRIPClient/20200406]
[15/Jan/2021:14:48:04] [3:Connection Handler] DEBUG: Adding varpair [Accept] == [*/*]
[15/Jan/2021:14:48:04] [3:Connection Handler] DEBUG: Adding varpair [Connectiotion] == [Basic XXXXXXXXXXXX]
[15/Jan/2021:14:48:04] [3:Connection Handler] WARNING: Invalid header line [XXXXXXXXXXXX] without colon
[15/Jan/2021:14:48:04] [3:Connection Handler] DEBUG: Checking need for authentication on mount /AGS

 

簡単に修正できたように書いているが辿り着くまでは困難だった。

なにせローカルでは問題ないので苦もなく動くものと思いこんでいました。

それにしても[Connectiotion]って?再現性はあるのでもう少しスマートな修正もできるのでしょうが、C言語は不得手で。

« Newer PostsOlder Posts »

Powered by WordPress