ESP8266 - WiFi 모듈 활용하기

Jeonghyun·2023년 10월 5일
post-thumbnail
아두이노 마이크로 프로ESP8266
  • NodeMCU가 아닌 아두이노와 사용하기 위한 모듈을 사용
  • 모듈에 코드를 업로드하는게 아닌 AT커맨드만을 이용

아두이노와 ESP8266 통신하기

ESP8266 어댑터
  • ESP8266의 동작전압은 3.3V

  • 어댑터를 사용하면 5V로 전원을 인가할 수 있고, 무엇보다 아두이노의 TX핀에서 나오는 전압 5V를 3.3V로 조정해줘서 레귤레이터 또는 전압분배회로 등을 안써도 돼서 편함

    아두이노 + ESP8266 회로도 (사진은 아두이노 나노지만 연결은 같다)
  • 코드는 Rx, Tx핀 연결 기준

  • 직렬 포트로 시리얼 통신하면서 AT커맨드로 제어하려면 Tx, Rx를 디지털핀으로 연결해서 SoftwareSerial를 사용해야함

매우 간단한 상호 통신 코드

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
}
void loop() {
  if (Serial.available()) {
    Serial1.write(Serial.read());
  }
  if (Serial1.available()) {
    Serial.write(Serial1.read());
  }
}

AT 커맨드

  • AT 입력 시 ok가 뜨면 정상 작동
  • AT+() 로 입력
  • AT+()? 로 일부 커맨드는 상태를 확인할 수 있음
    (ex) AT+CWMODE?

AT+GMR : 버전 확인
AT+RST : 환경 리셋
AT+UART_DEF=속도,8,1,0,0 : 통신속도 변경(재부팅해도 유지)
AT+CIOBAUD=속도 : 통신속도 일시적 변경
AT+CWMODE? : 모드 확인
AT+CWMODE=1 : 모드 변경 (1 : 스테이션(접속만), 2 : ap모드(공유기), 3 : 둘다)
AT+CWSAP="와이파이 이름","비밀번호",1,3 : ap설정(ssid, password, channel, security),
security = { 0 : OPEN, 2 : WPA_PSK, 3 : WPA2_PSK, 4 : WPA_WPA2_PSK }
AT+CIFSR : IP 확인
AT+CWLAP : 접속 가능 wifi 리스트
AT+CWJAP? : 현재 접속 wifi 정보
AT+CWJAP="ssid","password" : wifi 접속
AT+CWQAP : 접속 해제
AT+CWLIF : (ap모드에서) 연결된 station 확인
AT+CIPMUX=0 : 연결 모드(0 : single, 1 : multiple)
AT+CIPSERVER=1,포트 : 서버 생성(0: 해제, 1: 생성)
AT+CIPSTART=”TCP”,”IP주소”,포트 : TCP or UDP 서버 접속
AT+CIPMODE=1 : WiFi Passthrough을 위한 모드 (1 : 활성화)
AT+CIPSEND : 송수신 시작
AT+CIPCLOSE : TCP/UDP 통신 종료


접속 방법

  • 서버 ↔ 클라이언트
  • 와이파이 연결은 서버와 클라이언트가 같은 와이파이에 접속되어있으면 됨

1. PC(서버) ↔ ESP8266(클라이언트)

> PC에서 TCP(또는 UDP) 서버를 생성하고 ESP8266으로 접속하는 방법

PC에서 서버를 생성하는 방법은 여러가지 있으나 netcat프로세싱을 이용한 방법을 설명함
(네트워크 관련은 자세하게 모르기 때문에 사용방법 위주이며 틀린 설명일수 있음)

Netcat (nc) 커맨드로 서버 생성

  • TCP 또는 UDP 프로토콜을 사용하여 네트워크 연결을 통해 데이터를 읽고 쓰는 명령줄 유틸리티
  • 맥os에 내장되어있는 커맨드(아닐수도 있음)

간단하게 TCP 서버 생성하는 법

> nc -l (port)

TCP 서버의 해당 port로 listen한다는 뜻 (인데 서버를 만든다는 개념인지 이미 열려있는 서버에 포트로 다리만 이어주는 건지 잘 모름)

UDP로 열려면 -u를 추가하면 된다.

Host(왼쪽) / Client(오른쪽) 터미널로 간단하게 TCP 통신을 해볼 수 있다.

프로세싱에서 서버 생성

Processing - 시각화 툴

net 라이브러리를 사용하여 TCP 서버를 만들 수 있다

import processing.net.*; // network 라이브러리
import controlP5.*; // 컨트롤러 라이브러리

Server myserver;  //서버 객체
Client myclient;  //클라리언트 객체
ControlP5 cp5;    //컨트롤러 객체

void setup() {
  size(680, 80); 
  noStroke();

  myserver = new Server(this, 1234); // 해당 포트로 서버 생성
  
  cp5 = new ControlP5(this); // 컨트롤러
  cp5.addSlider("slider")
    .setRange(0, 100)
    .setPosition(20, 20)
    .setSize(600, 39)
    .setValue(0)
    ;
}

void draw() {
  background(0);
  myclient = myserver.available();
  if (myclient != null) { // client가 접속되어 있으면 받은 문자 출력
    println("input : "+ myclient.readString());
  }
}

public void slider(int input) {
  println(input);
  myserver.write(input); // slider의 값을 서버 전체로 전송  
}
  • 프로세싱(서버)에서 문자 전송을 위해 슬라이더 사용

ESP8266에서 접속하기

서버는 생성했고 ESP8266에서 접속을 해야한다.

위 AT 커맨드를 참고하여 서버 접속을 위해 사용하는 커맨드는 다음과 같다.

AT+CIPSTART
AT+CIPMODE(필요 시)
AT+CIPMUX
AT+CIPSEND

먼저 AT+CIPSTART="TCP","ip주소",포트 로 접속 후
AT+CIPSEND=문자길이 로 전송할 문자의 길이(byte)를 설정해준 뒤
커맨드 없이 전송할 문자를 입력해준다.

AT+CIPSTART="TCP","192.168.200.163",1234
AT+CIPSEND=5
hello

* 하지만 매번 전송할 때마다 전송할 문자 길이를 설정하는게 귀찮기 때문에 WiFi Passthrough를 이용할 수 있다.

WiFi Passthrough
먼저 WiFi Passthrough를 사용하기 위해서 AT+CIPMUX=0으로 바꿔줘야 한다.

AT+CIPMODE=1로 모드를 설정하면 앞으로 입력하는 모든 문자를 그대로 전송한다.

(AT 커맨드에도 반응하지 않기 때문에 취소하려면 줄바꿈 없이 +++ 입력 후 1초 뒤 커맨드를 입력하면 된다)

종료 시

참고로 AT+SAVETRANSLINK 명령어를 이전에 등록해두면 ESP8266 부팅 시 바로 WiFi Passthrough 모드로 들어간다 함.

ex)
AT+SAVETRANSLINK=1,"192.168.200.200",1234,"TCP"

2. ESP8266(서버) ↔ PC(클라이언트)

> ESP8266에서 TCP(또는 UDP) 서버를 생성하고 PC에서 접속

ESP8266에서 서버 열기

사용하는 AT 커맨드는 다음과 같다.

AT+CIPSERVER
AT+CIPSEND

먼저 CIPMUX=1로 설정해야 서버를 열 수 있다.
AT+CIPSERVER=1,포트로 서버를 생성하면 client에서 해당 포트로 접속할 수 있다.

단점은, 클라이언트에서 ESP8266(서버)으로 전송하는 문자는 그대로 출력되지만 전송하려면 CIPSEND로 문자길이 설정 후 전송하는 귀찮은 방식을 사용해야한다.

PC에서 접속

앞서 설명한 netcat을 사용해도 좋고 telnet으로 접속해도 된다.

사용 예시

  • PC(서버) ↔ ESP8266(클라이언트)
  • 아두이노 및 ESP8266 부팅 전 프로세싱 실행(TCP 서버 on)
  • 키보드 방향키로 드론 쓰로틀을 조정

코드

아두이노

#define THROTTLE_MAX 255
#define THROTTLE_MIN 0

const int motorA_pin = 5;
const int motorB_pin = 6;
const int motorC_pin = 9;
const int motorD_pin = 10;
int throttle = 0;

enum {
  HEAD1,
  HEAD2,
  THROTTLE,
  CRC,
  PACKETSIZE
};
uint8_t inputPacket[PACKETSIZE];

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
  Serial1.setTimeout(0);
  delay(5000); // esp8266 부팅 대기 

  Serial1.print("AT+CIPMUX=0\r\n");
  delay(200);
  Serial1.print("AT+CIPSTART=\"TCP\",\"192.168.200.200\",1234\r\n");
  delay(200);
  Serial1.print("AT+CIPMODE=1\r\n");
  delay(500);
  Serial1.print("AT+CIPSEND\r\n");
  delay(500);
  Serial1.print("ready");
}


void loop() { // 무결성 체크를 위해 패킷으로 전송
  static uint32_t cnt;

  if (Serial1.available()) {
    while (Serial1.available()) {
      uint8_t data = Serial1.read();
      if (data == '$') {
        cnt = HEAD1;
      } else {
        cnt++;
      }
      inputPacket[cnt] = data;

      if (cnt == CRC && data == '{') {
        throttle = inputPacket[THROTTLE];
      }
    }

    if (throttle >= 0 && throttle < 255) {
      analogWrite(motorA_pin, throttle);
      analogWrite(motorB_pin, throttle);
      analogWrite(motorC_pin, throttle);
      analogWrite(motorD_pin, throttle);
    }
  }
}

프로세싱

import processing.net.*; // network 라이브러리
import controlP5.*; // 컨트롤러 라이브러리

Server myserver;  //서버 객체
Client myclient;  //클라리언트 객체
ControlP5 cp5;    //컨트롤러 객체

void setup() {
  size(680, 80);
  noStroke();

  myserver = new Server(this, 1234); // 해당 포트로 서버 생성

  cp5 = new ControlP5(this); // 컨트롤러
  cp5.addSlider("slider")
    .setRange(0, 100)
    .setPosition(20, 20)
    .setSize(600, 39)
    .setScrollSensitivity(0.1)
    .setValue(0)
    ;
}

void draw() {
  background(0);
  myclient = myserver.available();
  if (myclient != null) { // client가 접속되어 있으면 받은 문자 출력
    println("input : "+ myclient.readString());
  }
}

public void slider(int input) {
  println(input);
  myserver.write('$'); // slider의 값을 서버 전체로 전송
  myserver.write('T');
  myserver.write(input);
  myserver.write(123);
}
void keyPressed() {
  if (key == ENTER) {
    cp5.getController("slider").setValue(0);
  } else if (key == CODED) {
    float throttle = cp5.getController("slider").getValue();
    if (keyCode == LEFT) {
      cp5.getController("slider").setValue(throttle - 5);
    }
    if (keyCode == RIGHT) {
      cp5.getController("slider").setValue(throttle + 5);
    }
    if (keyCode == UP) {
      cp5.getController("slider").setValue(throttle + 10);
    }
    if (keyCode == DOWN) {
      cp5.getController("slider").setValue(throttle - 10);
    }
  }
}

참고

ESP8266 AT커맨드 제어 : https://www.hardcopyworld.com/?p=2595
사용 프로젝트 : https://github.com/Jlnus/Capstone_quadcopter-altitude-control

0개의 댓글