0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントを活用してArduinoの制御を試してみた

0
Posted at

はじめに

ソフトウェア開発においては近年、AIエージェントを活用した開発の効率化が進んでいるが、組込み開発の領域においてAIエージェントの活用はソフトウェア領域程は耳にしない印象がありました。
そこでこの記事では、単純なLED点灯から始まり、RGB LEDのカラー制御、そして直感的に操作できるTkinter GUIアプリへと段階的にArduino開発をAIエージェントを活用して実施した一連のプロセスを紹介します。


Antigravityを活用したArduino制御の方法

AntigravityはGoogle DeepMindが開発したエージェント型AIコーディングアシスタントです。VS Codeなどのエディタに統合されており、以下のような操作をチャット形式の自然言語で指示できます。

  • コードの生成・編集: 「〜の機能を追加して」と伝えるだけでファイルを直接書き換える
  • コマンドの実行: 「このスクリプトを実行して」と指示すると仮想環境上でコマンドを発行する

なので、今回はAntigravityにPythonおよびArduinoのスケッチを作成させる形をとり、Arduinoの制御を行いました。

コード作成とArduinoへのプログラム書き込み時

ユーザー(自然言語で指示)
    ↓
Antigravity(Pythonおよびスケッチのコード作成)
    ↓
Arduino IDE(スケッチをArduino UNOに送信)
    ↓
Arduino

Arduinoの操作時

ユーザー(自然言語で指示)
    ↓
Antigravity(Pythonのコード修正やPythonプログラムの実行)
    ↓
Arduino(Pythonプログラムからシリアル通信で制御)

イメージ図

image.png


ステップ1: シリアル通信でLEDを点灯させる

最初の目標はシンプルで、PCからシリアル通信でコマンドを送り、Arduinoに接続したLEDを点灯・消灯させることでした。

回路図

image.png

Arduinoスケッチ

#include <Servo.h>
Servo myServo;
const int ledPin = 12;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  myServo.attach(9);
  myServo.write(0);
}

void loop() {
  if (Serial.available() > 0) {
    String command = Serial.readStringUntil('\n');
    command.trim();

    if (command == "LED_ON") {
      digitalWrite(ledPin, HIGH);
      Serial.println("LED turned ON");
    } else if (command == "LED_OFF") {
      digitalWrite(ledPin, LOW);
      Serial.println("LED turned OFF");
    }
  }
}

Python制御スクリプト(control_arduino.py)

import serial, time, sys

PORT = 'COM3'
BAUD_RATE = 9600

def send_command(command):
    ser = serial.Serial(PORT, BAUD_RATE, timeout=1)
    time.sleep(2)  # Arduinoのリセット待ち
    ser.write((command + '\n').encode('utf-8'))
    time.sleep(0.5)
    response = ser.readline().decode('utf-8').strip()
    print(f"Arduinoからの返答: {response}")
    ser.close()

if __name__ == "__main__":
    send_command(sys.argv[1])

コマンドラインから python control_arduino.py LED_ON を実行するとLEDが点灯し、Arduinoから「LED turned ON」と返答が返ってきました。


ステップ2: 単色LEDからRGB LEDへ

単色LEDでは色の表現に限界があるので、次のステップとしてRGB LEDに切り替え、コマンドで色を変えられるように拡張しました。

回路図

image.png

Arduinoスケッチ(RGB対応版)

#include <Servo.h>

Servo myServo;
const int redPin   = 10;
const int greenPin = 11;
const int bluePin  = 12;

void setup() {
  Serial.begin(9600);
  pinMode(redPin,   OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin,  OUTPUT);
  myServo.attach(9);
  myServo.write(0);
}

void loop() {
  if (Serial.available() > 0) {
    String command = Serial.readStringUntil('\n');
    command.trim();

    if (command == "COLOR_RED") {
      digitalWrite(redPin, HIGH); digitalWrite(greenPin, LOW);  digitalWrite(bluePin, LOW);
      Serial.println("LED turned RED");
    } else if (command == "COLOR_GREEN") {
      digitalWrite(redPin, LOW);  digitalWrite(greenPin, HIGH); digitalWrite(bluePin, LOW);
      Serial.println("LED turned GREEN");
    } else if (command == "COLOR_BLUE") {
      digitalWrite(redPin, LOW);  digitalWrite(greenPin, LOW);  digitalWrite(bluePin, HIGH);
      Serial.println("LED turned BLUE");
    } else if (command == "COLOR_YELLOW") {
      digitalWrite(redPin, HIGH); digitalWrite(greenPin, HIGH); digitalWrite(bluePin, LOW);
      Serial.println("LED turned YELLOW");
    } else if (command == "COLOR_OFF") {
      digitalWrite(redPin, LOW);  digitalWrite(greenPin, LOW);  digitalWrite(bluePin, LOW);
      Serial.println("LED turned OFF");
    } 
  }
}

これにより、python control_arduino.py COLOR_RED のようなコマンドでLEDの色を変えられるようになりました。


ステップ3: TkinterによるGUIアプリへの進化

コマンドライン操作は毎回ターミナルを使う必要があり、直感的ではなかったです。そこでPython標準ライブラリの Tkinter を使い、ボタンひとつで操作できるGUIアプリへ移行しました。

GUIの構成

  • RGB LEDカラーボタン: Red / Green / Blue / Yellow / OFF
  • ステータスバー: Arduinoからの返答をリアルタイムで表示

image.png
(実際のプログラムはサーボモーターの制御も追加していたのでサーボモーター用の制御画面の記載もあります)

最終的なPythonスクリプト(GUI版)

import serial
import time
import tkinter as tk
from tkinter import messagebox

PORT = 'COM3'
BAUD_RATE = 9600

class ArduinoGUI:
    def __init__(self, master):
        self.master = master
        self.master.title("Arduino RGB & Servo Control")
        self.master.geometry("300x450")
        self.ser = None
        self.connect_serial()

        # LED制御ボタン
        tk.Label(master, text="Select Color", font=("Arial", 14)).pack(pady=5)
        tk.Button(master, text="RED",    bg="red",   fg="white", font=("Arial", 12), command=lambda: self.send_command("COLOR_RED")).pack(fill=tk.X, padx=20, pady=2)
        tk.Button(master, text="GREEN",  bg="green", fg="white", font=("Arial", 12), command=lambda: self.send_command("COLOR_GREEN")).pack(fill=tk.X, padx=20, pady=2)
        tk.Button(master, text="BLUE",   bg="blue",  fg="white", font=("Arial", 12), command=lambda: self.send_command("COLOR_BLUE")).pack(fill=tk.X, padx=20, pady=2)
        tk.Button(master, text="YELLOW", bg="gold",  fg="black", font=("Arial", 12), command=lambda: self.send_command("COLOR_YELLOW")).pack(fill=tk.X, padx=20, pady=2)
        tk.Button(master, text="OFF",    bg="gray",  fg="white", font=("Arial", 12), command=lambda: self.send_command("COLOR_OFF")).pack(fill=tk.X, padx=20, pady=2)

        # サーボ制御スライダー
        tk.Label(master, text="Servo Angle (0 - 180)", font=("Arial", 14)).pack(pady=(15, 0))
        self.servo_scale = tk.Scale(master, from_=0, to=180, orient=tk.HORIZONTAL, length=220)
        self.servo_scale.set(0)
        self.servo_scale.pack(pady=5)
        # スライダーを離した時だけ送信(連続送信による通信詰まりを防ぐ)
        self.servo_scale.bind("<ButtonRelease-1>", self.on_slider_release)

        # ステータス表示
        self.status_label = tk.Label(master, text="Status: Connected" if self.ser else "Status: Disconnected",
                                     fg="green" if self.ser else "red")
        self.status_label.pack(pady=10)
        self.master.protocol("WM_DELETE_WINDOW", self.on_closing)

    def connect_serial(self):
        try:
            self.ser = serial.Serial(PORT, BAUD_RATE, timeout=1)
            time.sleep(2)
        except Exception as e:
            messagebox.showerror("Connection Error", f"Arduinoへの接続に失敗しました:\n{e}")
            self.ser = None

    def on_slider_release(self, event):
        self.send_command(f"MOTOR_{self.servo_scale.get()}")

    def send_command(self, command):
        if self.ser and self.ser.is_open:
            self.ser.write((command + '\n').encode('utf-8'))
            self.master.after(100, self.read_response)
        else:
            messagebox.showwarning("Warning", "Arduinoが接続されていません。")

    def read_response(self):
        if self.ser and self.ser.in_waiting > 0:
            response = self.ser.readline().decode('utf-8', errors='ignore').strip()
            self.status_label.config(text=f"Arduino: {response}", fg="blue")

    def on_closing(self):
        if self.ser and self.ser.is_open:
            self.ser.close()
        self.master.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    ArduinoGUI(root)
    root.mainloop()

まとめと感想

今回の開発を通じて、以下の技術要素を段階的に実践しました。

ステップ 内容
Pythonのpyserialでシリアル通信を介してArduinoを制御
RGB LEDの配線
Tkinterで直感的に操作できるGUIアプリへ移行

Antigravityが開発を加速した点

今回の開発で印象的だったのが、Antigravityとの対話だけで開発サイクルが完結したことです。

  • 「LEDを消して」LED_OFF コマンドをターミナルから即時実行し、Arduinoからの返答を確認できました。
  • 「RGB LEDに変えて」.ino ファイルのピン定義・コマンド処理をすべて自動で書き換え、ハードウェアとしてどういう変更をかけるべきかという指示も出すことができていました。

従来であればリファレンスを調べながら手打ちしていたコード修正や、Arduinoの配線の作業もスクラッチと整合される様に作業していたのですが、AIエージェントが一括で管理、指示できる状況はとなり、ハードウェア開発のハードルを大きく下げてくれました。

感想

今回の例は簡単なLチカに留まりましたが、Antigravityから直接Arduinoの制御ができるということが分かったので、これからできることの可能性が感じられる検証だったかなと個人的に感じています。


参考

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?