LangChainとマイコンをWi-Fi接続してLチカしてみた

# はじめに
最近LangChainのToolsに魅了されてから、なにかこれを実機のハードと連動することができないかと模索して、プロトタイプを作ってみました。
関数が呼べるのであれば、IoTデバイスも制御できるだろうと言うことで。

### ちなみに、Lチカってなあに？と思った方。
Lチカとは「LEDをチカチカさせる」という言葉の略称です。
この記事は、LEDをLangChainのToolsを使ってチカチカさせてみた紹介です。

# 今回作ったもの
https://youtu.be/cgsZLUYkOLk

# アーキイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418662/930241b8-cc45-f3e0-3c19-34b5c962034d.png)


ユーザーは、FastAPIに命令し、それをLangChainで処理後Mosquittoにメッセージをパブリッシュし、NodeMCU v3側でサブスクライブしLEDをチカっとさせるものです。

◆NodeMCU　250円くらいですね
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418662/6a37318a-16e5-d9aa-46f1-b307eacdb4b0.png)

https://ja.aliexpress.com/item/1005001621719732.html?spm=a2g0o.productlist.main.15.54be6093Plw8Cv


# コードについて少し解説
いきなり見せられても、よくわからないと思うので、コードを解説していきたいと思います。

## API側
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418662/928b688d-84ee-245b-d636-b3bd43a13990.png)

ここの赤丸２つ部分です。
```python
def _run(self, message: str) -> str:
    result = client.publish("test", '{"led":"'+message+'"}')
    if result.rc == mqtt.MQTT_ERR_SUCCESS:
        return "I turned 'ON' the LED."
    else:
        return "I turned 'OFF' the LED."
        # return f"Sending completed."
```
この部分で、_runのmessageにon（点灯）が来たら、{"led":"on"}をMosquittoBrokerにパブリッシュします。
逆に消灯の時は{"led":"off"}をパブリッシュします。

https://github.com/umaxiaotian/LangChainIoT/blob/main/app/Tools/MQTT.py

```python
Tool(
name = "Mqtt_Tool",
func=MqttSentTool()._run,
  description = """
    Effective when turning on or off the LED.
    If you want to turn on the LED, you will receive the string “on”.
    Also, if you want the LED to turn off, you will receive the string "off".
)
````
ここでLangchainのAgentが使用するツールを設定してあげます。
このToolの説明として、
「LEDのオン・オフ時にさせたいときに有効です。
　LEDを点灯させたい場合は、"on "という文字列を受け取ります。
　また、LEDを消灯させたい場合は、"off "という文字列を受け取ります。」
という説明文が記載してあります。

```python
agent = initialize_agent(tools, chat, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
result = agent.run(message)
return (result)
````
ここでは、ZERO_SHOT_REACT_DESCRIPTIONというエージェントを使っていますが、このエージェントはツールを説明文のみを見て決定して、実行するエージェントです。なので、説明文の方に受け取る引数の説明をしてあげる必要があるというわけです。
あとはこのパスをPostmanなどで叩いてあげれば、実行できます。

https://github.com/umaxiaotian/LangChainIoT/blob/main/app/main.py

## マイコン側
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418662/9988dd0e-d748-e0cd-c941-647c5d528762.png)

ここの赤丸部分です。
マイコン（ESP8266 NodeMCU v3)では、Platformioを使っています。なので拡張機能をインストール後にVSCodeでこのespファイルを開いてあげるだけで実行環境が整います。

https://github.com/umaxiaotian/LangChainIoT/tree/main/esp

```C++
// WiFiの設定
const char* ssid = ""; //SSID
const char* password = ""; //PASSWORD

// MQTTサーバの設定
const char* mqtt_server = ""; //MQTT BrokerのIP
const int mqtt_port = 1883;
const char* mqtt_user = "admin"; //本環境のデフォルトユーザー
const char* mqtt_password = "admin123"; //本環境のデフォルトユーザーのパスワード
```
冒頭のこの部分で、Wi-FiやMQTTブローカーのサーバーを設定する箇所を設けています。
実際にお試しされる場合はここを埋めてください。

また、MQTTのデフォルトのユーザーとパスワードは
|  ユーザー  |  パスワード  |
| ---- | ---- |
|  admin  |  admin123  |

こちらが、デフォルトのユーザーとなります。
もし変更したい場合は、Mosquittoコンテナ内に入ってユーザーを追加してください。

```C++
void callback(char* topic, byte* payload, unsigned int length) {
  String message;
  for (unsigned int i = 0; i < length; i++) {
    message += (char)payload[i];
  }

  // コールバックメッセージをシリアルモニターに出力
  Serial.println("Received message on topic: " + String(topic));
  Serial.println("Message: " + message);

  if (message == "{\"led\":\"on\"}") {
    digitalWrite(D0, HIGH);
  } else if (message == "{\"led\":\"off\"}") {
    digitalWrite(D0, LOW);
  }
}

```
次にコールバック関数を作ります。
ここでは、{"led":"on"}という文字列がMQTTにパブリッシュされた際にそれをサブスクライブして、digitalWriteというデジタルピンを操作する関数を叩き、onであれば、HIGHに設定しLEDを点灯させます。逆に{"led":"off"}が来た際はdigitalWrite関数を使ってD0ピンをLOWに設定しLEDを消灯させます。
この部分は無限ループに設定します。

```C++
void setup() {
  Serial.begin(9600);  // ボーレートを9600に変更
  Serial.println("Starting up...");

  // WiFi接続
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // MQTTコールバック関数の設定
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);

  // D0ピンの設定
  pinMode(D0, OUTPUT);
}
```
次はセットアップ関数です。
この部分でシリアルピンの速度を指定します。PlatformioやTeratermのシリアルスピードはデフォルトが9600に設定されているため、こちらの速度で設定します。
Wi-Fi接続時に、"Wi-Fi Connected!"とかやっているのは全部デバッグ目的です。
目視ではどうしてもWi-Fiに繋がっているかとか、MQTTBrokerにつなげているかわからないので設定してあげるとデバッグがしやすいと思います。

https://github.com/umaxiaotian/LangChainIoT/blob/main/esp/src/main.cpp

## マイコン側の配線
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418662/3615e8c4-15d6-01cf-4420-061a7904a51a.png)
こんな感じで配線を行ってください。必ずD0に合わせます。
# 完成品
下記に完成品をアップロードしています。
良ければお試しください。感想もお待ちしております。

https://github.com/umaxiaotian/LangChainIoT