0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Easy-IoTシステム構築 ~(できるだけ)お手軽にIoTシステムを構築してみる~<Google Gemini編>

0
Last updated at Posted at 2025-12-12

Easy-IoTシステム構築

この記事では、(できるだけ)お手軽にIoTシステムを構築してみます。

以下、自分の環境で試す場合には、自己責任でお願いします。
また、Geminiの回答(コード)が異なる場合もあります。
ご留意ください。

  • やってみること
    • (できるだけ)お手軽に
      • はんだ付け→なし
      • サーバの準備(構築)→なし
      • コーディング→できるだけ自分では書かない(生成AI(Gemini)に聞いてみる)
    • IoTシステム
      • センサから読み取った値をロギングするシステム

システム構成

構成図


今回構築するシステムの構成図

使用ハードウェア

<Pico WH + Grove Shield>

<DHT-11 + Groveケーブル>

<マイクロUSBケーブル>

使用AI

IoTデバイス(温湿度センサー)セットアップ

  1. Raspberry Pi Pico WHをGrove Shield fo Pi Picoに装着します
    <Pico WH + Grove Shield>

    Raspberry Pi Pico WHの向きに気を付けて、Grove Shield for Pi Picoにセットアップしてください。

  2. Groveのケーブルで温度センサをデジタル信号(D16)ポートに接続します
    <Pico WH + Grove Shield>

  3. Raspberry Pi Pico WにMicroPython用のファームウェアをインストールしてください

IoTデバイスの動作確認

プログラミング言語:MicroPython

ChatGPTを利用して、プログラムを開発していきます。
まず、デバイスの動作チェックを行うために、以下のプロンプトで、温度と湿度を取得するプログラムを作ってもらいます。

Geminiへのプロンプト

[プロンプト]
次のデバイスを使って温湿度を計測するシステムを構築したいと考えています。
Raspberry Pi PicoW
Grove Shield for Pi Pico v1.0
GROVE - デジタル温度・湿度センサ(DHT11)

言語はMicroPythonです。
温湿度センサはD16ポートに接続します。

温湿度を取得して表示するMicroPythonプログラムを作成してください。

Geminiからの回答

次のコードと補足などが提案されました。

このシステム構成で温湿度を取得するためのMicroPythonプログラムを作成します。

DHT11センサーからデータを取得するには、MicroPython用のライブラリが必要です。特にDHT11はタイミングが重要なので、標準的なdhtモジュール(またはカスタムライブラリ)を使用するのが一般的です。

提供された情報に基づき、D16ポート(Raspberry Pi Pico W上のGPIO16)に接続されたDHT11センサーから温湿度を読み取るMicroPythonコードの例を示します。

🌡️ 温湿度取得MicroPythonプログラム
このプログラムは、MicroPythonの標準ライブラリであるdhtモジュールを使用してDHT11センサーを読み取ります。

import dht
import machine
import time

# DHT11センサーが接続されているGPIOピンを指定します
# Grove Shield for Pi PicoのD16ポートは、PicoのGPIO16に対応しています。
DHT11_PIN = 16

# DHT11デバイスを初期化します
# machine.Pin(ピン番号, machine.Pin.IN) でピンを入力として設定
sensor = dht.DHT11(machine.Pin(DHT11_PIN))

print("DHT11温湿度計測システム起動...")
print("GPIO:", DHT11_PIN)

while True:
    try:
        # センサーからデータを読み取ります
        sensor.measure()
        
        # 湿度と温度を取得します
        temperature = sensor.temperature()
        humidity = sensor.humidity()
        
        # 結果を表示します
        print("------------------------------")
        print(f"🌡️ 温度: {temperature:.1f} °C")
        print(f"💧 湿度: {humidity:.1f} %")
        print("------------------------------")
        
    except OSError as e:
        # センサー読み取りエラーが発生した場合の処理
        print("⚠️ センサーからデータを読み取れませんでした:", e)
        # エラーの原因として一般的なのは、タイミングの問題や配線の接触不良です。
        
    # 次の測定までの待機時間 (DHT11は頻繁な測定を推奨しません)
    time.sleep(5) 

📝 実行時の注意点

  1. ライブラリの確認:
    • このコードは、MicroPythonファームウェアにdhtモジュールが組み込まれていることを前提としています。通常、標準的なMicroPythonビルドには含まれていますが、もしエラーが出る場合は、ファームウェアのビルドオプションを確認する> か、サードパーティのDHTライブラリ(GitHubなどで公開されているもの)をダウンロードしてPico Wにアップロードする必要があります
  2. 接続:
    • Raspberry Pi Pico WにGrove Shield for Pi Pico v1.0を装着します
    • GROVE - デジタル温度・湿度センサ(DHT11)をGrove ShieldのD16ポートに接続します

このプログラムをMicroPythonで動作するRaspberry Pi Pico Wにアップロードし、実行すると、5秒ごとに温湿度の値がターミナルに出力されます。

ThonnyやVS Codeでこのプログラムを実行し、シェルやターミナルに以下のような表示が出力されれば、デバイスは正常に動作しています。

では、提案されたコード(test_sensor_dht11.py)をThonnyで作成して、実行してみましょう。

Python test_sensor_dht11.py
import dht
import machine
import time

# DHT11センサーが接続されているGPIOピンを指定します
# Grove Shield for Pi PicoのD16ポートは、PicoのGPIO16に対応しています。
DHT11_PIN = 16

# DHT11デバイスを初期化します
# machine.Pin(ピン番号, machine.Pin.IN) でピンを入力として設定
sensor = dht.DHT11(machine.Pin(DHT11_PIN))

print("DHT11温湿度計測システム起動...")
print("GPIO:", DHT11_PIN)

while True:
    try:
        # センサーからデータを読み取ります
        sensor.measure()
        
        # 湿度と温度を取得します
        temperature = sensor.temperature()
        humidity = sensor.humidity()
        
        # 結果を表示します
        print("------------------------------")
        print(f"🌡️ 温度: {temperature:.1f} °C")
        print(f"💧 湿度: {humidity:.1f} %")
        print("------------------------------")
        
    except OSError as e:
        # センサー読み取りエラーが発生した場合の処理
        print("⚠️ センサーからデータを読み取れませんでした:", e)
        # エラーの原因として一般的なのは、タイミングの問題や配線の接触不良です。
        
    # 次の測定までの待機時間 (DHT11は頻繁な測定を推奨しません)
    time.sleep(5) 

センサからの情報を正しく取得できていれば、下のようにThonnyのコンソールに表示されます。

------------------------------
🌡 温度: 14.0 °C
💧 湿度: 24.0 %
------------------------------

データ送信

今回のシステムのコンセプトは「お手軽」ですので、サーバの構築やレンタルは行いません。

Raspberry Pi Pico W(MicroPython)からWi-Fi経由でGoogleスプレッドシートに温湿度データを書き込みます(Googleスプレッドシートをデータ保管サーバとします)。

ただし、Pico W単体ではGoogleのOAuth認証を直接扱えないので、Google Apps Script(GAS)を中継サーバとして利用します。

Google Apps Script(GAS)は、Google が提供するクラウド上で動作するスクリプト環境で、JavaScript によく似た言語で Google サービスを自動化・拡張できるツール

これも「お手軽」にAI「Google Gemini」へ聞いてみます。

Geminiへのプロンプト

[プロンプト]
Wi-Fi経由で計測したデータをGoogleスプレッドシートに書き込みたいです。
セキュリティ強化のため、以下の項目にしたがってください。
・Wi-Fiの接続情報とGoogleスプレッドシートへのアクセスに必要な情報は別ファイル(json形式)としてください。
・Googleスプレッドシートへのアクセスには、任意のAPIキーを使うようにしてください。
・POSTを使ってください。

Geminiからの回答

承知いたしました。Raspberry Pi Pico W(MicroPython)からWi-Fi経由で温湿度データをGoogleスプレッドシートに書き込むためのシステム構成と、必要なコードを作成します。

このシステムでは、以下の3つのコンポーネントを作成する必要があります。

  • MicroPythonコード (Pico W側): 温湿度を測定し、POSTリクエストをGASに送信するコード。
  • 設定ファイル (Pico W側): Wi-Fi情報とGASアクセスに必要なAPIキーを保存するJSONファイル。
  • Google Apps Script (GAS): Pico WからのPOSTリクエストを受け取り、APIキーを検証してスプレッドシートにデータを書き込むWebアプリケーションコード。

1. Google Apps Script (GAS) の設定

まず、データを受け取るための「中継サーバー」となるGASを作成します。

a. Googleスプレッドシートの準備

新しいGoogleスプレッドシートを作成し、名前を付けます(例: PicoW温湿度データ)。
1行目にヘッダーとして以下の項目を入力しておくと便利です。
Timestamp,Temperature,Humidity,API_Key (確認用)

b. Google Apps Scriptの記述

スプレッドシートのメニューから「拡張機能」>「Apps Script」を開きます。
以下のコードをコード.gsに貼り付けます。

// GAS WebアプリケーションのURLに埋め込む任意のAPIキーを設定します
// これをPico W側の設定ファイルにも同じ値を設定します。
const SECRET_API_KEY = "YOUR_CUSTOM_API_KEY_HERE"; // ★任意の文字列に変更してください

/**
 * Pico WからのPOSTリクエストを処理する関数
 * @param {object} e - リクエストパラメータを含むイベントオブジェクト
 */
function doPost(e) {
  // 1. JSONデータの解析
  let jsonString = e.postData.getDataAsString();
  let data;
  try {
    data = JSON.parse(jsonString);
  } catch (error) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: "Invalid JSON format"
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }

  // 2. APIキーの検証
  if (data.api_key !== SECRET_API_KEY) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: "Unauthorized access: Invalid API Key"
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }

  // 3. スプレッドシートへの書き込み
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const timestamp = new Date();
  const temperature = data.temperature || 'N/A';
  const humidity = data.humidity || 'N/A';
  const apiKeyUsed = data.api_key;
  
  sheet.appendRow([timestamp, temperature, humidity, apiKeyUsed]);

  // 4. 成功レスポンス
  return ContentService.createTextOutput(
    JSON.stringify({
      status: "success",
      message: "Data logged successfully"
    })
  ).setMimeType(ContentService.MimeType.JSON);
}
c. Webアプリケーションとしての公開
  1. Apps Scriptエディタで「デプロイ」>「新しいデプロイ」を選択します。
  2. 「種類の選択」で「ウェブアプリ」を選択します。
  3. 以下の設定を行います。
    • 実行アクセス:自分 (アカウント名)
    • アクセスできるユーザー:全員
    • 「デプロイ」をクリックします。

デプロイが完了すると、「ウェブアプリのURL」が表示されます。このURLを控えてください。これはPico WからPOSTリクエストを送る宛先になります。

2. MicroPythonコード (Raspberry Pi Pico W側)

Wi-Fi接続情報とGASのURL・APIキーを別ファイルで管理し、POSTリクエストを送信するコードです。

a. 設定ファイル (config.json)

Pico Wのルートディレクトリに以下の内容でファイルを作成し、アップロードします。

{
    "wifi_ssid": "YOUR_WIFI_SSID",
    "wifi_password": "YOUR_WIFI_PASSWORD",
    "gas_url": "YOUR_GAS_WEB_APP_URL",
    "api_key": "YOUR_CUSTOM_API_KEY_HERE"
}
  • YOUR_WIFI_SSID / YOUR_WIFI_PASSWORD: ご利用のWi-Fiアクセスポイントの情報。
  • YOUR_GAS_WEB_APP_URL: 1-cで取得したGASのウェブアプリURL。
  • YOUR_CUSTOM_API_KEY_HERE: 1-bで設定したSECRET_API_KEYと同じ文字列。
b. 温湿度計測と送信コード (main.py)

前回のコードに、Wi-Fi接続、JSON設定の読み込み、およびurequestsモジュールを使用したPOST送信機能を追加します。

注意点: MicroPythonでHTTPリクエストを行うには、通常、urequestsモジュールが必要です。urequests.pyファイルをPico Wにアップロードしてください。

import dht
import machine
import time
import network
import json
import urequests # urequestsモジュールが必要です

# --- 1. 定数と初期設定 ---

# DHT11センサーが接続されているGPIOピン (D16ポート)
DHT11_PIN = 16
sensor = dht.DHT11(machine.Pin(DHT11_PIN))

# --- 2. 設定ファイルの読み込み ---
def load_config():
    try:
        with open('config.json', 'r') as f:
            config = json.load(f)
        print("✅ 設定ファイル (config.json) を読み込みました。")
        return config
    except Exception as e:
        print(f"❌ 設定ファイルの読み込みエラー: {e}")
        return None

# --- 3. Wi-Fi接続処理 ---
def connect_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('📡 Wi-Fiに接続中...')
        wlan.connect(ssid, password)
        # 接続が確立するまで待機
        for _ in range(30): # 30回 (約15秒) 試行
            if wlan.isconnected():
                break
            time.sleep(0.5)
        
    if wlan.isconnected():
        print('✅ Wi-Fi接続成功:', wlan.ifconfig()[0])
        return True
    else:
        print('❌ Wi-Fi接続失敗。')
        return False

# --- 4. GASへデータをPOST送信 ---
def post_data_to_gas(config, temp, humid):
    url = config['gas_url']
    api_key = config['api_key']
    
    # 送信するJSONペイロードを作成
    payload = {
        "temperature": temp,
        "humidity": humid,
        "api_key": api_key 
    }
    
    # ヘッダーにContent-Typeを設定
    headers = {'Content-Type': 'application/json'}
    
    try:
        # POSTリクエストの送信
        response = urequests.post(url, json=payload, headers=headers)
        
        # 応答の確認
        if response.status_code == 200:
            print("📤 データ送信成功。GASからの応答:", response.json())
        else:
            print(f"⚠️ データ送信失敗。ステータスコード: {response.status_code}")
            print("応答本文:", response.text)
            
        response.close()
        
    except Exception as e:
        print(f"❌ HTTPリクエストエラー: {e}")


# --- 5. メイン処理 ---
def main():
    config = load_config()
    if not config:
        return

    # Wi-Fi接続
    if not connect_wifi(config['wifi_ssid'], config['wifi_password']):
        return
        
    # メインループ
    while True:
        try:
            # 温湿度測定
            sensor.measure()
            temperature = sensor.temperature()
            humidity = sensor.humidity()
            
            print("------------------------------")
            print(f"🌡️ 測定値: {temperature:.1f} °C, 💧 {humidity:.1f} %")
            
            # データ送信
            post_data_to_gas(config, temperature, humidity)
            
        except OSError as e:
            print(f"⚠️ センサー読み取りエラー: {e}")
            
        # 次の測定までの待機
        time.sleep(60) # 例: 60秒ごとに測定・送信

if __name__ == "__main__":
    main()

実装

それでは、Geminiからの回答をベースにして、実際にプログラミングを行っていきましょう。
提案されたものをそのままコピペするのではなく、少し変更します(セキュリティの関係などから)。

config.jsonの準備

次の情報(ネットワーク接続や認証情報)を記録したJSONファイルをRaspberry Pi Pico W上に作成、保存します。

{
    "wifi_ssid": "YOUR_WIFI_SSID",
    "wifi_password": "YOUR_WIFI_PASS",
    "gas_url": "https://script.google.com/macros/s/XXXXX/exec",
    "api_key": "YOUR_SECRET_API_KEY"
}
  • YOUR_WIFI_SSID:接続したいWi-Fiアクセスポイント(SSID)の名前
  • YOUR_WIFI_PASS:Wi-Fiアクセスポイントのパスワード
  • https://script.google.com/macros/s/XXXXX/exec:Google Apps Script(GAS)のWebアプリとして公開したURL
    • Googleスプレッドシートの作成と設定が必要です
    • 次の項目で説明します
  • YOUR_SECRET_API_KEY:GAS側で設定した任意の認証キー(秘密鍵)
    • このapi_keyはGoogleが発行する公式APIキーではありません。PicoW(送信側)とGAS(受信側)で共有する任意の秘密鍵です
    • GASを「誰でもPOSTできるWebAPI」として公開するため、不正なアクセスを防ぐためのパスワードのようなものです
    • 要件
      • 英数字+記号(-:ハイフン、_:アンダースコア)を組み合わせたランダム文字列が望ましい
      • 最低16文字以上
      • 可能なら32〜64文字程度
    • 作成サービス

それぞれの項目は"(ダブルクォーテーション)の中に記載してください。

  • YOUR_WIFI_PASS:Wi-Fiアクセスポイントのパスワード
  • YOUR_SECRET_KEY:GAS側で設定した任意の認証キー(秘密鍵)

この2つは絶対に漏えいなどしないよう厳重に取り扱ってください。
誰かに知られると、スプレッドシートなどを勝手に操作されてしまう可能性があります。

Googleスプレッドシート(データ保管サーバ)の準備

Googleドライブで作業します。

Googleアカウントでログインしてから、Googleドライブにアクセスしてください。

Googleスプレッドシートの作成とGASの入力
  1. Googleドライブにログインし、「新規」をクリック
    Googleスプレッドシート01.png
  2. Googleスプレッドシートを新規作成
    Googleスプレッドシート02.png
  3. 上部メニューの「拡張機能」を選択し、Apps Scriptを開く
    Googleスプレッドシート04.png
  4. エディタ部分に以下を貼り付ける
// GAS WebアプリケーションのURLに埋め込む任意のAPIキーを設定します
// これをPico W側の設定ファイルにも同じ値を設定します。
const SECRET_API_KEY = "YOUR_SECRET_API_KEY_HERE"; // ★config.jsonと同じにしてください

/**
 * Pico WからのPOSTリクエストを処理する関数
 * @param {object} e - リクエストパラメータを含むイベントオブジェクト
 */
function doPost(e) {
  // 1. JSONデータの解析
  let jsonString = e.postData.getDataAsString();
  let data;
  try {
    data = JSON.parse(jsonString);
  } catch (error) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: "Invalid JSON format"
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }

  // 2. APIキーの検証
  if (data.api_key !== SECRET_API_KEY) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: "Unauthorized access: Invalid API Key"
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }

  // 3. スプレッドシートへの書き込み
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const timestamp = new Date();
  const temperature = data.temperature || 'N/A';
  const humidity = data.humidity || 'N/A';
//セキュリティ強化のため、API_KEYを非表示にする修正をします
-  const apiKeyUsed = data.api_key;  // 削除
  
-  sheet.appendRow([timestamp, temperature, humidity, apiKeyUsed]);  // apikeyUsedを削除
+  sheet.appendRow([timestamp, temperature, humidity]);

  // 4. 成功レスポンス
  return ContentService.createTextOutput(
    JSON.stringify({
      status: "success",
      message: "Data logged successfully"
    })
  ).setMimeType(ContentService.MimeType.JSON);
}

↓入力後のApps Script画面
Googleスプレッドシート05.png

1行目のAPI_KEYは、Raspberry Pi Picoで設定したものと同じYOUR_SECRET_KEYを入力してください。

デプロイ(Googleスプレッドシート+GASをWebアプリとして公開)

  1. Apps Script上部の「デプロイ」をクリック → 「新しいデプロイ」をクリック
    Googleスプレッドシート06.png

  2. 「設定」をクリック → ウェブアプリを選択:
    Googleスプレッドシート07.png

  3. 「新しい説明文:任意」、「ウェブアプリ→「次のユーザとして実行:自分」、「アクセスできるユーザー:全員」と設定して、デプロイをクリック
    Googleスプレッドシート08.png

  4. 「アクセスを承認」をクリック
    Googleスプレッドシート09.png

  5. アカウントを選択
    Googleスプレッドシート10.png

  6. 警告画面が表示されます。

以下の作業はセキュリティ上の脆弱性となる可能性があります。作業を進める場合には、自己責任でお願いします。

[メッセージ]
Google hasn’t verified this app
The app is requesting access to sensitive info in your Google Account. Until the developer (xxxxxxx@gmail.com) verifies this app ※with Google, you shouldn't use it.
[日本語訳]
Googleはこのアプリを認証していません。
このアプリはGoogleアカウントの機密情報へのアクセスを求めています。
開発者(xxxxxxx@gmail.com)がGoogleによる認証を完了するまで※、このアプリを使用しないでください。

  1. 左下のAdvancedをクリック
    Googleスプレッドシート11.png
  2. スクロールして
    Googleスプレッドシート13.png
  3. 「Continue」をクリック
    Googleスプレッドシート13b.png
  4. ウェブアプリにあるURLをコピーする(このURLがRaspberry Pi Pico WHで設定するapi_urlとなる)
    Googleスプレッドシート14.png

データ送信の準備[Raspberry Pi Pico WH]

config.jsonの確認

GAS[Googleスプレッドシート]とconfig.json[Raspberry Pi Pico WH]の

が一致しているか確認してください。

Micro Pythonプログラムの実行

次のプログラムをThonnyなどで記述し、実行します。

python send_data_to_sheet.py
import dht
import machine
import time
import network
import json
import urequests # urequestsモジュールが必要です

# --- 1. 定数と初期設定 ---

# DHT11センサーが接続されているGPIOピン (D16ポート)
DHT11_PIN = 16
sensor = dht.DHT11(machine.Pin(DHT11_PIN))

# --- 2. 設定ファイルの読み込み ---
def load_config():
    try:
        with open('config_school.json', 'r') as f:
            config = json.load(f)
        print("✅ 設定ファイル (config.json) を読み込みました。")
        return config
    except Exception as e:
        print(f"❌ 設定ファイルの読み込みエラー: {e}")
        return None

# --- 3. Wi-Fi接続処理 ---
def connect_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('📡 Wi-Fiに接続中...')
        wlan.connect(ssid, password)
        # 接続が確立するまで待機
        for _ in range(30): # 30回 (約15秒) 試行
            if wlan.isconnected():
                break
            time.sleep(0.5)
        
    if wlan.isconnected():
        print('✅ Wi-Fi接続成功:', wlan.ifconfig()[0])
        return True
    else:
        print('❌ Wi-Fi接続失敗。')
        return False

# --- 4. GASへデータをPOST送信 ---
def post_data_to_gas(config, temp, humid):
    url = config['gas_url']
    api_key = config['api_key']
    
    # 送信するJSONペイロードを作成
    payload = {
        "temperature": temp,
        "humidity": humid,
        "api_key": api_key 
    }
    
    # ヘッダーにContent-Typeを設定
    headers = {'Content-Type': 'application/json'}
    
    try:
        # POSTリクエストの送信
        response = urequests.post(url, json=payload, headers=headers)
        
        # 応答の確認
        if response.status_code == 200:
            print("📤 データ送信成功。GASからの応答:", response.json())
        else:
            print(f"⚠️ データ送信失敗。ステータスコード: {response.status_code}")
            print("応答本文:", response.text)
            
        response.close()
        
    except Exception as e:
        print(f"❌ HTTPリクエストエラー: {e}")


# --- 5. メイン処理 ---
def main():
    config = load_config()
    if not config:
        return

    # Wi-Fi接続
    if not connect_wifi(config['wifi_ssid'], config['wifi_password']):
        return
        
    # メインループ
    while True:
        try:
            # 温湿度測定
            sensor.measure()
            temperature = sensor.temperature()
            humidity = sensor.humidity()
            
            print("------------------------------")
            print(f"🌡️ 測定値: {temperature:.1f} °C, 💧 {humidity:.1f} %")
            
            # データ送信
            post_data_to_gas(config, temperature, humidity)
            
        except OSError as e:
            print(f"⚠️ センサー読み取りエラー: {e}")
            
        # 次の測定までの待機
        time.sleep(60) # 例: 60秒ごとに測定・送信

if __name__ == "__main__":
    main()

プログラムを実行して、以下の結果が表示されれば、取得した温度と湿度がGoogleスプレッドシートに入力されています。

✅ 設定ファイル (config.json) を読み込みました。
📡 Wi-Fiに接続中...
✅ Wi-Fi接続成功: xxx.xxx.xxx.xxx
------------------------------
🌡 測定値: 21.0 °C, 💧 50.0 %

Googleスプレッドシートには日時も記録されていますが、これはデータがスプレッドシートに書き込まれた時間です。
データを取得した時間ではないので、注意してください。

センサの値を表示した後、エラーメッセージが表示されます。これは、通信のやり取り(Googleスプレッドシート → Pico W)において、エラーが発生しているからです(Google Geminiに聞いてみたところ)。
通信(Pico W → Googleスプレッドシート)は、正常に行われているので、データを記録することに差し支えないかと思いますので、そのままにしておきます。

複数センサデバイスへの対応

複数のセンサデバイスからの情報を受け取って、ロギングできるようにしてみましょう。
まず、センサデバイスごとのデータを区別するため、デバイスにID(デバイスID)を割り当てます。そして、センサの情報を送信する際に、デバイスIDも同時にGoogleスプレッドシートに送信します。

「どのようにコードを修正すれば良いか」をGeminiに聞いてみます。

Geminiへのプロンプト

[プロンプト]
複数のPicoから送られたデータをスプレッドシートに記録したいです。

PicoにはPythonプログラム上で任意のDEVIECE_IDを与えて、スプレッドシートには、タイムスタンプ・温度・湿度・デバイスIDが記録されるようにしてください。

Geminiからの回答

次のコードと補足などが提案されました。

複数のPico Wからデータを収集し、デバイスIDを記録するようにシステムを拡張するための変更を承知いたしました。

必要な修正は、主に以下の2つのコンポーネントで行います。

MicroPythonコード (Pico W側):

config.json に DEVICE_ID を追加。

POSTペイロードに device_id を含める。

Google Apps Script (GAS側):

受信した device_id を検証せずそのままスプレッドシートに書き込むように修正。

  1. 💻 MicroPythonコードの修正 (Pico W側)
    a. 設定ファイル (config.json) の変更
    Pico Wごとに一意のIDを割り当てるため、config.json に device_id を追加します。

Pico W A の config.json Pico W B の config.json
json { "wifi_ssid": "...", "wifi_password": "...", "gas_url": "...", "api_key": "SECRET", **"device_id": "PicoW_A01"** } json { "wifi_ssid": "...", "wifi_password": "...", "...", "api_key": "SECRET", > **"device_id": "PicoW_B02"** }
b. MicroPythonコード (main.py) の修正
post_data_to_gas 関数内のペイロードに device_id を追加します。

# main.py の post_data_to_gas 関数を修正

# ... (中略) ...

def post_data_to_gas(config, temp, humid):
    url = config['gas_url']
    api_key = config['api_key']
+   device_id = config['device_id'] # ★ 追加
    
    # 送信するJSONペイロードを作成
    payload = {
        "temperature": temp,
        "humidity": humid,
        "api_key": api_key, 
        "device_id": device_id # ★ 追加
    }
    
    headers = {'Content-Type': 'application/json'}
    
    # ... (後略 - 接続処理はそのまま) ...
  1. 🌐 Google Apps Script (GAS) の修正
    GAS側で、受信したJSONデータから device_id を取得し、スプレッドシートの行に追加します。

a. スプレッドシートのヘッダー修正
スプレッドシートの1行目のヘッダーに、新しい項目を追加または修正します。

A列 B列 C列 D列 E列
Timestamp Temperature Humidity Device_ID API_Key (確認用)
b. GASコード (コード.gs) の修正
doPost 関数内のデータ取得と書き込み部分を修正します。

// コード.gs (doPost 関数内)

// ... (APIキー検証処理まで同じ) ...

  // 3. スプレッドシートへの書き込み
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const timestamp = new Date();
  const temperature = data.temperature || 'N/A';
  const humidity = data.humidity || 'N/A';
  
  // ★ 新しく追加・修正した部分
  const deviceId = data.device_id || 'UNKNOWN'; // device_idを取得。ない場合は'UNKNOWN'

  // 書き込み行の構成: [タイムスタンプ, 温度, 湿度, デバイスID, APIキー]
  sheet.appendRow([timestamp, temperature, humidity, deviceId]); 
 // ヘッダーにAPI_Keyを入れる場合は、[timestamp, temperature, humidity, deviceId, data.api_key] に修正

  // 4. 成功レスポンスの作成と返却
  const successResponse = {
    status: "success",
    message: "Data logged successfully",
    temp_logged: temperature, 
    humid_logged: humidity,
    device_id_logged: deviceId // ★ 応答にもデバイスIDを追加(デバッグ用)
  };
  
  return ContentService.createTextOutput(JSON.stringify(successResponse))
    .setMimeType(ContentService.MimeType.JSON);
}

c. 🚀 GASの再デプロイ
GASコードを修正したら、必ず新しいバージョンとして再デプロイしてください。

これで、複数のPico Wから異なるIDでデータが送信され、スプレッドシートに正しく記録されるようになります。MicroPython側の400エラーの件については、前回の回答でご提示したmain.pyのpost_data_to_gasの最終版を適用することで、ログの> 表示を「成功」に切り替えることができます。

実装

Pico Wごとに、個別のdevice_idを設定します。device_idのところに任意で固有の文字列を入力してください。

デバイスIDの割り当て

Pico WHに保存しているconfig.jsonにデバイスIDを追加します。

{
    "wifi_ssid": "YOUR_WIFI_SSID",
    "wifi_password": "YOUR_WIFI_PASS",
    "gas_url": "https://script.google.com/macros/s/XXXXX/exec",
+    "api_key": "YOUR_SECRET_API_KEY", //←カンマを追加(忘れずに!)
+    "device_id": "DeviceXX"  //デバイスIDを追加(XXの部分は任意の数値)
}

データ送信プログラムの修正

Pico WHに保存してあるsent_data_to_sheet.pyを修正します。

import dht
import machine
import time
import network
import json
import urequests # urequestsモジュールが必要です

# --- 1. 定数と初期設定 ---

# DHT11センサーが接続されているGPIOピン (D16ポート)
DHT11_PIN = 16
sensor = dht.DHT11(machine.Pin(DHT11_PIN))

# --- 2. 設定ファイルの読み込み ---
def load_config():
    try:
        with open('config.json', 'r') as f:
            config = json.load(f)
        print("✅ 設定ファイル (config.json) を読み込みました。")
        return config
    except Exception as e:
        print(f"❌ 設定ファイルの読み込みエラー: {e}")
        return None

# --- 3. Wi-Fi接続処理 ---
def connect_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('📡 Wi-Fiに接続中...')
        wlan.connect(ssid, password)
        # 接続が確立するまで待機
        for _ in range(30): # 30回 (約15秒) 試行
            if wlan.isconnected():
                break
            time.sleep(0.5)
        
    if wlan.isconnected():
        print('✅ Wi-Fi接続成功:', wlan.ifconfig()[0])
        return True
    else:
        print('❌ Wi-Fi接続失敗。')
        return False

# --- 4. GASへデータをPOST送信 ---
def post_data_to_gas(config, temp, humid):
    url = config['gas_url']
    api_key = config['api_key']
    device_id = config['device_id'] #
    
    # 送信するJSONペイロードを作成
    payload = {
        "temperature": temp,
        "humidity": humid,
        "api_key": api_key ,
+       "device_id": device_id. # ペイロードにdevice_idを追加
    }
    
    # ヘッダーにContent-Typeを設定
    headers = {'Content-Type': 'application/json'}
    
    try:
        # POSTリクエストの送信
        response = urequests.post(url, json=payload, headers=headers)
        
        # 応答の確認
        if response.status_code == 200:
            print("📤 データ送信成功。GASからの応答:", response.json())
        else:
            print(f"⚠️ データ送信失敗。ステータスコード: {response.status_code}")
            print("応答本文:", response.text)
            
        response.close()
        
    except Exception as e:
        print(f"❌ HTTPリクエストエラー: {e}")


# --- 5. メイン処理 ---
def main():
    config = load_config()
    if not config:
        return

    # Wi-Fi接続
    if not connect_wifi(config['wifi_ssid'], config['wifi_password']):
        return
        
    # メインループ
    while True:
        try:
            # 温湿度測定
            sensor.measure()
            temperature = sensor.temperature()
            humidity = sensor.humidity()
            
            print("------------------------------")
            print(f"🌡️ 測定値: {temperature:.1f} °C, 💧 {humidity:.1f} %")
            
            # データ送信
-           post_data_to_gas(config, temperature, humidity)
+           post_data_to_gas(config, temperature, humidity, device_id)  # device_idを追加 
             
        except OSError as e:
            print(f"⚠️ センサー読み取りエラー: {e}")
            
        # 次の測定までの待機
        time.sleep(60) # 例: 60秒ごとに測定・送信


if __name__ == "__main__":
    main()

GASの修正

GoogleスプレッドシートのGASを修正します。

// GAS WebアプリケーションのURLに埋め込む任意のAPIキーを設定します
// これをPico W側の設定ファイルにも同じ値を設定します。
const SECRET_API_KEY = "YOUR_SECRET_API_KEY"; // ★任意の文字列に変更してください

/**
 * Pico WからのPOSTリクエストを処理する関数
 * @param {object} e - リクエストパラメータを含むイベントオブジェクト
 */
function doPost(e) {
  // 1. JSONデータの解析
  let jsonString = e.postData.getDataAsString();
  let data;
  try {
    data = JSON.parse(jsonString);
  } catch (error) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: "Invalid JSON format"
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }

  // 2. APIキーの検証
  if (data.api_key !== SECRET_API_KEY) {
    return ContentService.createTextOutput(
      JSON.stringify({
        status: "error",
        message: "Unauthorized access: Invalid API Key"
      })
    ).setMimeType(ContentService.MimeType.JSON);
  }

  // 3. スプレッドシートへの書き込み
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const timestamp = new Date();
  const temperature = data.temperature || 'N/A';
  const humidity = data.humidity || 'N/A';
+ const deviceId = data.device_id || UNKNOWN; //追加、device_idを取得。ない場合は'UNKNOWN'
  
- sheet.appendRow([timestamp, temperature, humidity]);
+ sheet.appendRow([timestamp, temperature, humidity, deviceId]);

  // 4. 成功レスポンス
  return ContentService.createTextOutput(
    JSON.stringify({
      status: "success",
      message: "Data logged successfully"
    })
  ).setMimeType(ContentService.MimeType.JSON);
}

デプロイ

プログラム修正後、新しいバージョンとして再デプロイしてください。

  1. 画面右上の「デプロイ」をクリック → 「デプロイを管理」をクリック
    デプロイ01.png

  2. 右上の「編集」アイコンをクリック
    デプロイ02.png

  3. バージョンを「新しいバージョン」に変更する → 「デプロイ」をクリック
    デプロイ03.png

PicoのMicroPythonプログラムを実行して、スプレッドシートに「タイムスタンプ」「温度」「湿度」「デバイスID」が記録されれば、OKです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?