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

#はじめに
以前に[日没頃に自動で照明をつけてみる](https://qiita.com/mitazet/items/00754f62ba089ea29353)という記事を投稿したのですが、
会社勤めなどしていると、日没頃に家の照明がついてもあまりうれしくない。
じゃあ、使い道のあるものを、、、寒くなってきたし、、、:bulb:
__これってクリスマスイルミネーションにぴったりじゃね。__
#やりたいこと
日没頃に自動でイルミネーションをつけて、自己満足する。
夜明けには自動で消える。
#環境
-100均のイルミネーションはある(本当は200円)
-照度センサーはない
-GPSはない
-WiFi環境はある
-WiFiにつながるマイコンはある
#方針
やっぱりインターネットで日の入/日の出の時間を調べて、
その時間に、イルミネーションの点灯・点滅を行う。
前回と同じでは面白くないので、RaspberryPiは使わず、ESP-WROOM-32を使う。
すべてESP-WROOM-32で事足りるはず。

#やり方
WebAPIで住所の緯度経度を調べる。
WebAPIでその緯度経度の日の入/日の出時刻を調べる。
日の入時刻頃にイルミネーションをつける。
日の出時刻頃にイルミネーションを消す。
##完成イメージ
![Skitch から.png](https://qiita-image-store.s3.amazonaws.com/0/140055/d2254786-ad4a-bc4e-3144-a866bd74a93d.png)

#用意するもの
・ESP-WROOM-32(ESP32-DevKitC)
・100均のクリスマスイルミネーション
・モバイルバッテリーなどの電源(一応外を想定)
100円のものもあったがこれは200円
![package.png](https://qiita-image-store.s3.amazonaws.com/0/140055/59208d8f-7072-ed98-6ee7-91c4e7e7771c.png)
常時点灯タイプだから点滅させることにする。
#作る
##接続する
電池部分は切ってしまって、イルミネーションとGPIO19をつなぐ。
![ch_led.png](https://qiita-image-store.s3.amazonaws.com/0/140055/563081ac-7524-ee9f-6d4d-0bf8efbc8551.png)
光ったので接続は大丈夫そうだ。(電流値を測定していないので、チップ寿命を縮めてるかも)

##プログラムを作る
Webアクセスが簡単にできる[MicroPython](https://github.com/micropython/micropython-esp32)でプログラムを作成。
ソースコード全体は[github](https://github.com/mitazet/esp/blob/master/micropython/auto_led/main.py)においた。
###時刻管理
####時刻設定時の注意
標準ではMicroPythonからESP-WROOM-32に対して時刻を設定することができない。
[いっぺーちゃんの いろいろやってみよ～](http://ippei8jp.hatenablog.jp/entry/2017/08/18/135755)では時刻設定のためにMicroPythonにPatchを当てている。
しかし、私の環境では取得できる時間が大きすぎるせいで、Overflowとなった。
そのため、Patchで行なっている1970/01/01/00:00からの経過秒数を
2000/01/01/00:00からの経過秒数となるように変更した。
MicroPythonに付属しているntptimeは2000年からの経過秒数が取得できるため問題なかった。
とりあえず、その方法でOverflowは発生しなくなった。
####点灯時刻と消灯時刻の管理
時刻管理は`Time_ctrl`クラスにまとめた。
このクラスは毎日の点灯時刻と消灯時刻を管理している。

```python
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](https://developers.google.com/maps/documentation/geocoding/start?hl=ja)を使用した。
郵便番号から緯度/経度を取得する。

```python
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](http://labs.bitmeister.jp/ohakon/index.cgi)を使用している。
取得した緯度/経度から日の入りと日の出時間を取得する。

```python
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につなげたイルミネーションを制御するクラスを作成。
常時点灯タイプなので、あえて点滅制御を行った。

```python
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時に日の出時刻と、日の入り時刻を更新する。
時刻が日の入り時刻を過ぎるとイルミネーション開始、日の出で終了する。

```python
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](https://qiita-image-store.s3.amazonaws.com/0/140055/932a3634-87a7-662e-53b5-e2f49f3d073c.png)
__実は点滅している。__

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

#参考サイト
[いっぺーちゃんの いろいろやってみよ～](http://ippei8jp.hatenablog.jp/entry/2017/08/18/135755)