Python
WebAPI
組み込み
IoT
ESP-WROOM-32

日没頃に自動で100均イルミネーションをつけてみる

はじめに

以前に日没頃に自動で照明をつけてみるという記事を投稿したのですが、
会社勤めなどしていると、日没頃に家の照明がついてもあまりうれしくない。
じゃあ、使い道のあるものを、、、寒くなってきたし、、、:bulb:
これってクリスマスイルミネーションにぴったりじゃね。

やりたいこと

日没頃に自動でイルミネーションをつけて、自己満足する。
夜明けには自動で消える。

環境

-100均のイルミネーションはある(本当は200円)
-照度センサーはない
-GPSはない
-WiFi環境はある
-WiFiにつながるマイコンはある

方針

やっぱりインターネットで日の入/日の出の時間を調べて、
その時間に、イルミネーションの点灯・点滅を行う。
前回と同じでは面白くないので、RaspberryPiは使わず、ESP-WROOM-32を使う。
すべてESP-WROOM-32で事足りるはず。

やり方

WebAPIで住所の緯度経度を調べる。
WebAPIでその緯度経度の日の入/日の出時刻を調べる。
日の入時刻頃にイルミネーションをつける。
日の出時刻頃にイルミネーションを消す。

完成イメージ

Skitch から.png

用意するもの

・ESP-WROOM-32(ESP32-DevKitC)
・100均のクリスマスイルミネーション
・モバイルバッテリーなどの電源(一応外を想定)
100円のものもあったがこれは200円
package.png
常時点灯タイプだから点滅させることにする。

作る

接続する

電池部分は切ってしまって、イルミネーションとGPIO19をつなぐ。
ch_led.png
光ったので接続は大丈夫そうだ。(電流値を測定していないので、チップ寿命を縮めてるかも)

プログラムを作る

Webアクセスが簡単にできるMicroPythonでプログラムを作成。
ソースコード全体はgithubにおいた。

時刻管理

時刻設定時の注意

標準ではMicroPythonからESP-WROOM-32に対して時刻を設定することができない。
いっぺーちゃんの いろいろやってみよ~では時刻設定のためにMicroPythonにPatchを当てている。
しかし、私の環境では取得できる時間が大きすぎるせいで、Overflowとなった。
そのため、Patchで行なっている1970/01/01/00:00からの経過秒数を
2000/01/01/00:00からの経過秒数となるように変更した。
MicroPythonに付属しているntptimeは2000年からの経過秒数が取得できるため問題なかった。
とりあえず、その方法でOverflowは発生しなくなった。

点灯時刻と消灯時刻の管理

時刻管理はTime_ctrlクラスにまとめた。
このクラスは毎日の点灯時刻と消灯時刻を管理している。

class Time_ctrl:
    def __init__(self):
        utime.set_time(ntptime.time())
        self.year = 2000 
        self.month = 1 
        self.day = 1 
        self.time_led_on = 0
        self.time_led_off = 0

    def update_date(self):
        now = utime.localtime()
        self.year = now[0]
        self.month = now[1]
        self.day = now[2]

    def get_date(self):
        return (self.year, self.month, self.day)

    def is_time_update(self):
        if utime.time() > utime.mktime((self.year, self.month, self.day+1, 0, 0, 0, 0, 0)): 
            return True
        else:
            return False

    def update_time_led_on(self, str_time_sunset):
        hour_led_on = float(str_time_sunset)
        sec_led_on = int(hour_led_on * 60 * 60)
        self.time_led_on = utime.mktime((year, month, day, 0, 0, 0, 0, 0)) + sec_led_on

    def update_time_led_off(self, str_time_sunrise):
        hour_led_off = float(str_time_sunrise)
        sec_led_off = int(hour_led_off * 60 * 60)
        self.time_led_off = utime.mktime((year, month, day, 0, 0, 0, 0, 0)) + sec_led_off

    def get_time_led_on(self):
        return self.time_led_on

    def get_time_led_off(self):
        return self.time_led_off

WebAPIで緯度/経度を取得

これは前回同様Geocoding APIを使用した。
郵便番号から緯度/経度を取得する。

def get_geocode(zipcode):
    url = 'https://maps.googleapis.com/maps/api/geocode/json?'
    key = 'set_your_token'
    url2 = url + 'address=' + str(zipcode) + '&key=' + key
    response = urequests.get(url2)
    str_json = response.json()
    item_list = str_json['results'][0]
    location = item_list['geometry']['location']
    lat = location['lat']
    lng = location['lng']
    return (lat,lng)

WebAPIで日の入/日の出時間を取得

これも前回同様おはこん番地は⁈APIを使用している。
取得した緯度/経度から日の入りと日の出時間を取得する。

def get_time_rise_set(year, month, day, lat,lng):
    str_addr = 'http://labs.bitmeister.jp/ohakon/api/?mode=sun_moon_rise_set&year'
    str_mode = 'mode=sun_rise_set'
    str_year = '&year=' + str(year)
    str_month = '&month=' + str(month)
    str_day = '&day=' + str(day)
    str_lat = '&lat=' + str(lat)
    str_lng = '&lng=' + str(lng)
    url = str_addr + str_mode + str_year + str_month + str_day + str_lat + str_lng 
    response = urequests.get(url)

    pattern = r'<sunrise>[0-9]+.[0-9]+'
    match_sunrise = ure.search(pattern, response.text)

    if not match_sunrise:
        sys.exit(0)

    sunrise_str = str(match_sunrise.group(0))
    sunrise_str = sunrise_str.split('>')

    pattern = r'<sunset>[0-9]+.[0-9]+'
    match_sunset = ure.search(pattern, response.text)

    if not match_sunset:
        sys.exit(0)

    sunset_str = str(match_sunset.group(0))
    sunset_str = sunset_str.split('>')
    return (sunrise_str[1], sunset_str[1]) 

イルミネーションの制御

GPIOにつなげたイルミネーションを制御するクラスを作成。
常時点灯タイプなので、あえて点滅制御を行った。

class Led_ctrl:
    def __init__(self, num_pin):
        self.num_pin = num_pin
        self.t0 = machine.Timer(0)
        self.pin = machine.Pin(self.num_pin, machine.Pin.OUT)
        self.pin.value(0)
        self.is_working = False

    def pin_toggle(self, t):
        cur_data = self.pin.value()
        self.pin.value(not cur_data)

    def set_led_on(self): 
        if self.is_working == False:
            self.t0.init(period=1300, mode = machine.Timer.PERIODIC, callback = self.pin_toggle)
        self.is_working = True

    def set_led_off(self): 
        if self.is_working == True:
            self.t0.deinit()
            self.pin.value(0)

        self.is_working = False

日の入りに点灯、日の出に消灯

毎日、午前0時に日の出時刻と、日の入り時刻を更新する。
時刻が日の入り時刻を過ぎるとイルミネーション開始、日の出で終了する。

led = Led_ctrl(19)
wifi = connect_wifi(SSID_NAME, SSID_PASS)

if not wifi:
    sys.exit(0)

time = Time_ctrl()

lat, lng = get_geocode('set_your_zipcode')

#confirm initialize result
print('test start')
led.set_led_on()
utime.sleep(15)
print('test end')
led.set_led_off()

while True:
    if time.is_time_update(): 
        time.update_date()
        year, month, day = time.get_date()
        print(str(year) + '/' + str(month) + '/' + str(day))

        time_sunrise, time_sunset = get_time_rise_set(year, month, day, lat, lng)

        time.update_time_led_off(time_sunrise) 
        print(time.get_time_led_off())

        time.update_time_led_on(time_sunset)
        print(time.get_time_led_on())

    if utime.time() < time.get_time_led_off():
        led.set_led_on()

    if time.get_time_led_on() > utime.time() and utime.time() >= time.get_time_led_off():
        led.set_led_off()

    if utime.time() >= time.get_time_led_on():
        led.set_led_on()

    utime.sleep(60)

テスト

16時36分(11/12の日の入り時刻)に点灯!!
一応、外のWiFiが届く範囲
3A7F8FB0-B0F4-4C92-AF21-C31C42D2F4C5.png
実は点滅している。

最後に

最近売ってるやつはセンサで自動ON/OFFするみたい。
イルミネーションは200円だけど、ESP-WROOM-32(DevKit)は1480円だから別にお得ではない。

参考サイト

いっぺーちゃんの いろいろやってみよ~