전격Z작전 시스템 구성

소개

시도하고자 했던 것과 그 이유를 알려주세요.

시스템 구성 형태를 변경하고 코드를 구현

진행 방법

어떤 도구를 사용했고, 어떻게 활용하셨나요?

Tip: 사용한 프롬프트 전문을 꼭 포함하고, 내용을 짧게 소개해 주세요.

Tip: 활용 이미지나 캡처 화면을 꼭 남겨주세요.

Tip: 코드 전문은 코드블록에 감싸서 작성해주세요. ( / 을 눌러 '코드 블록'을 선택)

전체적인 시나리오는

키트를 나에게 오게 하고 오는 동안 신호등을 파란불로 바꾸는걸로 했습니다.

시스탬 구성

[Telegram]

명령 : Jetbot를 10초 이동하고 신호등을 on 해줘

1. 사용자 → Telegram 메시지 전송

2. Hermes 수신 → LLM 시나리오 파악

3. [MCP] drive_car(forward, 10) 호출

→ JetBot 전진 시작 🚗💨

4. [MCP] lua.run_script("traffic_on.lua") 호출

→ ESP32 신호등 ON 🟢

5. [MCP] drive_car(stop) 호출

→ JetBot 정지

[GCP VM ]

├── Hermes Gateway (systemd, 실행 중)

│ └── MCP 서버 2개 등록됨

│ ├── iot-bridge → JetBot 제어

│ └── esp → 신호등 제어

├── mcp-iot bridge (~/work/mcp-iot/dist/index.js)

│ ├── drive_car()

│ ├── check_obstacle()

│ ├── get_sensor()

│ └── set_actuator()

└── Mock IoT Server (localhost:8080)

[사무실 네트워크 ]

├── 공유기 포트포워딩

│ ├── 8080 → 192.168.219.43:8080 (JetBot)

│ └── 8081 → ESP32_IP:80 (신호등)

├── Jetson Nano B01 (192.168.219.43)

│ └── server.py (FastAPI)

│ ├── POST /jetbot/command → robot.forward/stop...

│ └── GET /jetbot/status

└── 신호등(192.168.219.53)

└── ESP 펌웨어

├── MCP Server (HTTP :80)

└── Lua scripts

├── led_on.lua → gpio.set_level(2, 1)

└── led_off.lua → gpio.set_level(2, 0)

텔레그램으로 자연어를 입력하면 에이전트는 디바이스가 작동 가능하게 명령으로 변환하여 호출하는 구조 입니다.

Agent 가 MCPtool 호출해 사용할 수 있게 MCP IoT Bridge를 구축합니다.

// ~/work/mcp-iot/src/index.ts

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

import axios from "axios";

import { z } from "zod";

const MOCK_BASE = "http://localhost:8080";

const server = new McpServer({ name: "iot-bridge", version: "1.0.0" });

// Tool 1: 차량 제어

server.tool(

"drive_car",

{ action: z.enum(["stop","forward","backward","left","right"]),

speed: z.number().min(0).max(80).default(0) },

async ({ action, speed }) => {

const res = await axios.post(`${MOCK_BASE}/jetbot/command`, { action, speed });

return { content: [{ type: "text",

text: JetBot: ${action} (speed=${speed}) → ${JSON.stringify(res.data)} }] };

}

);

// Tool 2: 거리/장애물 감지

server.tool(

"get_obstacle_status",

{},

async () => {

const res = await axios.get(`${MOCK_BASE}/jetbot/status`);

// 카메라 프레임 대신 status API로 단순화

const sensors = await axios.get(`${MOCK_BASE}/esp32/sensors`);

const obstacle = sensors.data.distance_cm < 50;

return { content: [{ type: "text",

text: JSON.stringify({

obstacle_detected: obstacle,

distance_cm: sensors.data.distance_cm,

car_status: res.data

})

}]};

}

);

// Tool 3: ESP32 센서 조회

server.tool(

"get_sensor_data",

{ sensor: z.enum(["temperature","distance","ir"]) },

async ({ sensor }) => {

const res = await axios.get(`${MOCK_BASE}/esp32/sensors`);

const val = sensor === "temperature" ? res.data.temperature

: sensor === "distance" ? res.data.distance_cm

: res.data.ir_triggered;

return { content: [{ type: "text",

text: ${sensor}: ${val} }] };

}

);

// Tool 4: 액추에이터 제어

server.tool(

"set_actuator",

{ device: z.string(), state: z.enum(["on","off"]) },

async ({ device, state }) => {

const res = await axios.post(`${MOCK_BASE}/esp32/actuator/${device}?state=${state}`);

return { content: [{ type: "text",

text: Actuator ${device}: ${JSON.stringify(res.data)} }] };

}

);

const transport = new StdioServerTransport();

await server.connect(transport);

Jetbot디바이스에는 기존방식의 작동 코드 스타일로 정의 됩니다.

# JetBot Jetson Nano에서 실행 — Waveshare 드라이버 직접 제어

# waveshare 공식 repo: git clone -b jetbot_0.4.2 https://github.com/waveshare/jetbot.git

from jetbot import Robot

import uvicorn

from fastapi import FastAPI

robot = Robot()   # PCA9685 → TB6612 I2C 자동 초기화

app   = FastAPI()

@app.post("/command")

async def command(cmd: dict):

    action = cmd["action"]

    speed  = cmd.get("speed", 0.4)

    if   action == "stop":     robot.stop()

    elif action == "forward":  robot.forward(speed)

    elif action == "backward": robot.backward(speed)

    elif action == "left":     robot.left(speed)

    elif action == "right":    robot.right(speed)

    return {"status": action}

저번에 jetbot은 배터리 방전으로 다른 배터리 교체 했는데 sd 카드에 손상이 있어 정상적으로 부팅이 안되네요.
배터리 찾다가 다른게 있어서 대채 했습니다.

https://github.com/NVIDIA-AI-IOT/jetbot

https://www.waveshare.com/catalog/product/view/id/3755

jetbot에는 jetson nano가 있어 에이전트와 통신이 쉬운데 작은 디바이스들은 좀 애로사항이 많습니다.

그런데 esp-calw 에서 소개된 방법에는 가능해서 신호등에는 이걸로 적용했습니다.

https://github.com/espressif/esp-claw

작은 디바이스에서도 AI agent와 통신하며 사용할 수 있겠네요.

오후에 카메라를 MCP IoT Bridge에 등록했습니다.

휴대전화의 문자 메시지 스크린샷
휴대전화의 문자 메시지 스크린샷

결과와 배운 점

배운 점과 나만의 꿀팁을 알려주세요.

과정 중에 어떤 시행착오를 겪었나요?

도움이 필요한 부분이 있나요?

앞으로의 계획이 있다면 들려주세요.

디바이스 연동이다 보니 준비할것이 많네요.

기본적인 것은 구현이 되어서 이제 셋팅해 보면 될꺼 같습니다.

Hermes를 무료 모델로 돌리다 보네 iot bridge 에서 timeout이 나서 gpt로 변경해 보니 괜찮습니다.

https://event-us.kr/skintellixhackathon/event/125194?utm_source=eventus&utm_medium=mix&utm_campaign=ddgrimq6im&pit_id=8512

공기 청정기 인데 바퀴가 있어 Fallow-me 요청으로 나를 따라오게 할 수 있네요.
Agent와 이런 디바이스들 연동해 보면 재미있을꺼 같습니다.

참여하려고 팀원 모집합니다.
연락주세요 ^^

도움 받은 글 (옵션)

참고한 지피터스 글이나 외부 사례를 알려주세요.

(내용 입력)

1개의 답글

뉴스레터 무료 구독