M5Stackで自動ペット給餌器

Automatic pet feeder ・ 自动喂宠

Core2コントローラー、2チャンネルリレーでモーター駆動、RTCで時刻管理する自動給餌器。

この構成をエディタで開く・編集する →

部品リスト(BOM)

部品役割ポートリンク・価格
M5Stack Core2Controllergrove_i2c, grove_analog, grove_uart, mbusofficial page — $46.9
Unit PaHub2I2C Hubgrove_i2cofficial page — $7.95
Unit 2Relay (2-Channel Relay Unit)Motor Driver (2-Channel Relay)grove_i2cofficial page — $6.5
Unit RTCReal-Time Clockgrove_i2cofficial page — $4.95

配線

作り方

M5Stack Core2とPaHub2、2チャンネルリレーユニット、RTCユニットを使って作る、自動ペットフーダーです。決まった時間にリレーでモーターを動かし、フードを自動で出します。Core2がRTCで現在時刻を取得し、設定した給餌時刻になるとリレーをONにしてモーターを回します。

  1. 部品を用意する — 用意するもの:M5Stack Core2本体、Unit PaHub2(I2Cハブ)、Unit 2Relay(2チャンネルリレーユニット)、Unit RTC(リアルタイムクロック)、Groveケーブル3本(I2C用、赤色)、USB-Cケーブル(Core2とパソコン接続用)、モーターと電源(リレーで制御する外部機器)。
  2. PaHub2をCore2に接続する — Groveケーブル(赤色)の一端をPaHub2の入力ポート(IN)に、もう一端をCore2のPort.A(赤色ポート)に差し込みます。コネクタは一方向にしか入らないので、向きを確認して軽く押し込んでください。
  3. リレーユニットとRTCユニットをPaHub2に接続する — 2本目のGroveケーブルで、PaHub2のチャンネル0(CH0)とリレーユニットのGroveポートをつなぎます。3本目のGroveケーブルで、PaHub2のチャンネル1(CH1)とRTCユニットのGroveポートをつなぎます。これでCore2はI2C経由で両方のユニットを制御できます。
  4. Core2をパソコンに接続し、MicroPythonを書き込む — USB-CケーブルでCore2をパソコンにつなぎます。UIFlowまたはThonnyなどのツールでCore2にMicroPythonファームウェアが入っていることを確認し、下のコードをデバイスにコピーして実行します。
  5. 時刻を設定し、給餌スケジュールを調整する — コード内のFEED_TIMESリストに給餌したい時刻を追加します(例:'08:00'、'18:00')。RTCはバッテリーでバックアップされるので、一度設定すれば電源を切っても時刻は保持されます。初回実行時にシリアルモニタで現在時刻を確認し、必要に応じてコード内のset_time変数を編集して時刻を合わせてください。
  6. 動作をテストする — コードを実行し、Core2の画面に現在時刻と次の給餌時刻が表示されることを確認します。給餌時刻になると、リレーが約3秒間ONになり、モーターが回転します。実際のペットフーダーに組み込む前に、モーターとフードの動作を確認してください。

サンプルコード(micropython)

# M5Stack Core2 + PaHub2 + 2Relay + RTC (Automatic Pet Feeder)
# Wiring:
#   Core2 Port.A (red) -> PaHub2 IN
#   PaHub2 CH0 -> 2Relay (I2C address 0x26)
#   PaHub2 CH1 -> RTC (I2C address 0x51)
import time
from machine import Pin, I2C
from m5stack import lcd
from m5stack_ui import M5Screen

# Initialize I2C on Core2 Port.A
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=100000)

# PaHub2 (TCA9548A) I2C address: 0x70
PAHUB_ADDR = 0x70

def select_channel(ch):
    """Select a channel on PaHub2 (0-5)."""
    i2c.writeto(PAHUB_ADDR, bytes([1 << ch]))

# 2Relay I2C address: 0x26
RELAY_ADDR = 0x26

def relay_on(channel):
    """Turn on a relay channel (0 or 1)."""
    select_channel(0)  # 2Relay is on CH0
    if channel == 0:
        i2c.writeto(RELAY_ADDR, bytes([0x01]))  # Relay1 ON
    else:
        i2c.writeto(RELAY_ADDR, bytes([0x02]))  # Relay2 ON

def relay_off(channel):
    """Turn off a relay channel."""
    select_channel(0)
    if channel == 0:
        i2c.writeto(RELAY_ADDR, bytes([0x00]))  # Relay1 OFF
    else:
        i2c.writeto(RELAY_ADDR, bytes([0x00]))  # Relay2 OFF (same command)

# RTC (HYM8563) I2C address: 0x51
RTC_ADDR = 0x51

def read_rtc():
    """Read time from RTC, return (hour, minute, second)."""
    select_channel(1)  # RTC is on CH1
    # Read 7 bytes from register 0x02 (seconds, minutes, hours, ...)
    data = i2c.readfrom_mem(RTC_ADDR, 0x02, 7)
    sec = (data[0] & 0x7F) + ((data[0] >> 4) & 0x07) * 10  # BCD to decimal
    minute = (data[1] & 0x7F) + ((data[1] >> 4) & 0x07) * 10
    hour = (data[2] & 0x3F) + ((data[2] >> 4) & 0x03) * 10
    return hour, minute, sec

def set_rtc(hour, minute, second):
    """Set RTC time (24h format)."""
    select_channel(1)
    # Convert decimal to BCD
    sec_bcd = (second // 10) << 4 | (second % 10)
    min_bcd = (minute // 10) << 4 | (minute % 10)
    hour_bcd = (hour // 10) << 4 | (hour % 10)
    # Write to registers 0x02 (seconds), 0x03 (minutes), 0x04 (hours)
    i2c.writeto_mem(RTC_ADDR, 0x02, bytes([sec_bcd, min_bcd, hour_bcd]))

# Set initial time (adjust as needed)
# set_rtc(12, 0, 0)  # Uncomment and set to current time once

# Feeding schedule: list of times in "HH:MM" format
FEED_TIMES = ["08:00", "18:00"]
FEED_DURATION = 3  # seconds

def parse_time(t_str):
    h, m = map(int, t_str.split(':'))
    return h, m

last_feed_day = -1  # to avoid multiple feeds per day

while True:
    hour, minute, second = read_rtc()
    current_time = "{:02d}:{:02d}:{:02d}".format(hour, minute, second)
    lcd.clear()
    lcd.print("Time: " + current_time, 10, 20)
    
    # Check if current time matches any feed time
    for t in FEED_TIMES:
        feed_h, feed_m = parse_time(t)
        if hour == feed_h and minute == feed_m and second < 5:  # within 5 sec window
            # Check if already fed today (using day of month, simplified)
            # For simplicity, we use a flag that resets at midnight
            if last_feed_day != hour * 60 + minute:  # crude unique check
                lcd.print("Feeding...", 10, 60)
                relay_on(0)  # Relay1 controls motor
                time.sleep(FEED_DURATION)
                relay_off(0)
                last_feed_day = hour * 60 + minute
                break
    
    time.sleep(1)

コツ・安全上の注意

この作り方はAIが構成から生成したものです(未検証)。通電前に配線とコードをご確認ください。

この構成はAIが実在するM5Stack部品から設計し、ポート互換をサーバで検証したものです。編集・共有できます。

エディタで開く →

UnitKit は M5Stack® 製品向けの非公式ツール。作りたいものを書くと、部品リストと配線図が出ます。