251107 [ Day 83 ] - Arduino (5)

TaeHyun·2025년 11월 7일

TIL

목록 보기
96/185

시작하며

이제 아두이노의 마지막 파트인 통신 파트가 시작되었다. 역시 마지막 파트다 보니 상당히 복잡한 내용이 많았다.

RFID

  • 라이브러리 설치 필요
  • 무선 주파수 식별
  • 사물 또는 동물에 고유한 식별코드를 부여하고 이를 전파를 이용하여 식별하는 기술
  • RFID 핀 설명
    • 3.3V : 전원
    • RST : 리셋(모듈 초기화 및 다시 시작 시 사용)
    • GND : 접지
    • IRQ : 인터럽트(태그 감지 시 하드웨어 인터럽트 전송) 보통 사용 X
    • MISO : Master In Slave Out(RFID 모듈 → 아두이노)
    • MOSI : Master Out Slave In(아두이노 → RFID 모듈)
    • SCK : Serial Clock(데이터 전송 동기화를 위한 클럭 신호)
    • SDA(SS) : 슬레이브 선택(어떤 장치와 통신할지 선택하는 신호)

RFID와 NFC

RFID

  • 사용 주파수 : 125kHz ~ 2.45GHz
  • 연결 범위 : 최대 100m
  • 통신 : 단방향 통신(태그/리더 별도)
  • 장점 : 장거리 인식 가능

NFC

  • 사용 주파수 : 13.56MHz
  • 연결 범위 : 10cm 내외
  • 통신 : 양방향 통신(태그/리더 통합)
  • 장점 : 높은 보안성

RFID 코드

  • mfrc.PICC_IsNewCardPresent()
    • 새로운 카드가 인식 범위 안에 들어왔는지 확인
    • 태그가 접촉되었는지 확인
  • mfrc.PICC_ReadCardSerial()
    • 카드가 UUID(고유번호)가 읽히는지 확인
#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN 9
#define SS_PIN 10
#define BUZZER 8
#define R_LED 2
#define B_LED 3
#define W_CARD "3777252"
#define B_CARD "1722392051"

MFRC522 mfrc(SS_PIN, RST_PIN);

void setup() {
  Serial.begin(9600);
  SPI.begin();
  mfrc.PCD_Init();

  pinMode(BUZZER, OUTPUT);
  pinMode(R_LED, OUTPUT);
  pinMode(B_LED, OUTPUT);
}

void loop() {
  if(!mfrc.PICC_IsNewCardPresent() || !mfrc.PICC_ReadCardSerial()) {
    delay(500);
    return;
  }

  digitalWrite(BUZZER, LOW);
  digitalWrite(R_LED, LOW);
  digitalWrite(B_LED, LOW);

  String cardUID = "";

  for(byte i = 0; i < 4; i++) { 
    cardUID += String(mfrc.uid.uidByte[i]);
    Serial.println("")
  }

  if(cardUID == W_CARD) {
    digitalWrite(B_LED, HIGH);
    digitalWrite(BUZZER, HIGH);
    delay(100);
    digitalWrite(BUZZER, LOW);
    digitalWrite(B_LED, LOW);
  }

  if(cardUID == B_CARD) {
  digitalWrite(R_LED, HIGH);
  digitalWrite(BUZZER, HIGH);
  delay(100);
  digitalWrite(BUZZER, LOW);
  digitalWrite(R_LED, LOW);
  delay(100);
  digitalWrite(R_LED, HIGH);
  digitalWrite(BUZZER, HIGH);
  delay(100);
  digitalWrite(BUZZER, LOW);
  digitalWrite(R_LED, LOW);
  }
}

시리얼 통신

  • 컴퓨터와 데이터를 주고 받는 직렬 통신
  • 우노 보드
    • 0핀(RX) : Receive Data / 컴퓨터에서 보내는 신호를 받는 곳
    • 1핀(TX) : Transit Data / 컴퓨터로 신호를 보내는 곳
int data;

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
}

void loop() {
  while (Serial.available()) {
    data = Serial.read();
  }

  if(data == '1') {
    digitalWrite(13, HIGH);
  } else if(data == '0') {
    digitalWrite(13, LOW);
  }
}

Python 연동

  • pyserial / flask 라이브러리 설치
from flask import Flask, request, jsonify
from flask_cors import CORS
import serial, time

SERIAL_PORT = ""
BAUD = 9600

arduino = serial.Serial(SERIAL_PORT, BAUD, timeout=1)
time.sleep(2)

app = Flask(__name__)
CORS(app) # 다른 출처(파일/로컬서버)에서 접근 허용
state = "off"

@app.post("/led")
def led():
    global state
    data = request.get_json(force=True)
    desired = data.get("state")

    if desired == "on":
        arduino.write(b'1')
        state = "on"
    elif desired == "off":
        arduino.write(b'0')
        state = "off"
    else:
        return jsonify({
            "ok": False,
            "error": "state must be 'on' or 'off'"
        }, 400)
    return jsonify({
        "ok": True,
        "state": state
    })

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True, use_reloader=False, threaded=False)
  • HTML 내 JavaScript code
<script>
    async function setLed(state) {
        try {
            const res = await fetch("http://localhost:5001/led", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ state: state }),
            });
            const json = await res.json();
            document.getElementById("status").textContent = json.state;
        } catch (e) {
            alert(
                "서버에 연결할 수 없습니다. python 코드가 실행 중인지 확인하세요."
            );
        }
    }
</script>

웹으로 연동

  • Python 없이 JavaScript만 사용
<script>
    let port, writer;

    async function connect() {
        try {
            port = await navigator.serial.requestPort();
            await port.open({ baudRate: 9600 });
            writer = port.writable.getWriter();

            // 아두이노 리셋 안정화 약간 대기
            await new Promise((r) => setTimeout(r, 1500));
            // 정상 연결 후 on 버튼과 off 버튼 활성화
            on.disabled = off.disabled = false;
        } catch (e) {
            alert("연결 실패: " + e.message);
        }
    }
    async function sendChar(ch) {
        if (!writer) return;
        const data = new TextEncoder().encode(ch);
        await writer.write(data);
    }
</script>

WiFi 통신

ESP8266(ESP-01)

  • WiFi 모듈 중 가장 보편적인 모듈
  • 시리얼 통신을 통해 AT 변경 가능하며 3.3v에서 동작
  • 2.4GHz만 지원
from flask import Flask, request, jsonify
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.get("/data")
def getData():
    print("getData", dict(request.args))
    return jsonify(ok=True)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001, debug=True, use_reloader=False, threaded=False)
#include <SoftwareSerial.h>
SoftwareSerial esp8266(2, 3);

const char* ssid = "";
const char* password = "s";
const char* server = "";
const int port = 5001;

bool sendCommand(const String& cmd, const char* expect = "OK", unsigned long timeout = 5000) {
  esp8266.print(cmd); esp8266.print("\r\n");
  unsigned long t = millis(); String buf;
  while (millis() - t < timeout) {
    while (esp8266.available()) {
      char c = (char)esp8266.read();
      buf += c;
      Serial.write(c);
      if (expect && *expect && buf.indexOf(expect) != -1) return true;
    }
  }
  return false;
}

void setup() {
  Serial.begin(9600);
  esp8266.begin(9600);
  delay(1000);
  sendCommand("AT", "OK", 1500);
  delay(1000);
  sendCommand("AT+CWMODE=1", "OK", 2000);
  delay(1000);
  sendCommand(String("AT+CWJAP=\"") + ssid + "\",\"" + password + "\"", "WIFI GOT IP", 20000);
  delay(1000);
  sendCommand("AT+CIPMUX=0", "OK", 2000);
  delay(1000);
}

void loop() {
  sendDataToServer(25, 60);
  delay(5000);
}

void sendDataToServer(int temp, int hum) {
  String url = "/data?temperature=" + String(temp) + "&humidity=" + String(hum);

  if (!sendCommand(String("AT+CIPSTART=\"TCP\",\"") + server + "\"," + port, "CONNECT", 5000)) return;

  String req = 
    String("GET ") + url + " HTTP/1.1\r\n"
    "Host: " + String(server) + ":" + String(port) + "\r\n"
    "Connection: close\r\n\r\n";
  
  if (sendCommand(String("AT+CIPSEND=") + req.length(), ">", 3000)) {
    esp8266.print(req);
    sendCommand("", "SEND OK", 4000);
  }

  sendCommand("AT+CIPCLOSE", "OK", 2000);
  Serial.println("\n[데이터 전송 시도 종료]");
}

마치며

이제 다음 주에 와이파이 통신 파트가 마무리되면 본격적인 프로젝트가 시작이다. 주말간 머신러닝을 위한 데이터를 찾아보면서 모델의 기본적인 틀을 구현해봐야겠다.

profile
Hello I'm TaeHyunAn, Currently Studying Data Analysis

0개의 댓글