본문 바로가기

ESP32 Project

Wi-Fi와 블루투스 동시 사용 + 4개 가상 인터페이스

반응형

8. Wi-Fi와 블루투스 동시 사용 + 4개 가상 인터페이스 ESP32는 Wi-Fi 스테이션 모드, 소프트AP 모드, Promiscuous 모드를 동시에 사용할 수 있고, 최대 4개의 가상 Wi-Fi 인터페이스를 생성할 수 있습니다. 블루투스 클래식과 BLE도 동시에 사용 가능합니다. 이를 활용하면 하나의 보드로 게이트웨이 + 블루투스 브리지 + 모니터링 역할을 동시에 할 수 있습니다.

 

ESP32 동시 통신 기능 상세 설명

1. 동시 사용 가능한 모드 조합

ESP32는 다음 모드들을 동시에 활성화할 수 있습니다:
시트
 
 
모드 조합설명
Wi-Fi STA + SoftAP 외부 Wi-Fi에 연결하면서 동시에 자체 AP 운영
Wi-Fi + Bluetooth Classic Wi-Fi 통신 + 블루투스 2.0 장치 연결
Wi-Fi + BLE Wi-Fi 통신 + 블루투스 저전력 기기 연결
Wi-Fi + BT Classic + BLE 세 가지 모드 모두 동시 활성화
Promiscuous 모드 주변 모든 패킷 스니핑 (보안/분석용)

2. 가상 인터페이스 (VIF - Virtual Interface)

ESP32는 최대 4개의 가상 Wi-Fi 인터페이스를 생성할 수 있습니다:
  • STA 인터페이스 (1개): 외부 Wi-Fi 라우터에 연결
  • SoftAP 인터페이스 (1개): 자체 AP 운영 (최대 10개 클라이언트)
  • Promiscuous 인터페이스 (1개): 패킷 스니핑
  • 추가 STA 인터페이스 (1개): 다른 Wi-Fi 네트워크 동시 연결

3. 실용적 활용 시나리오

 

이미지 1: ESP32 Wi-Fi + Bluetooth 듀얼 모드 아키텍처 ESP32 보드 내부의 4개 모듈(Wi-Fi STA, SoftAP, BLE, BT Classic)과 외부 기기들의 연결 구조를 보여줍니다. 2.4GHz 공유 안테나, 각 프로토콜별 연결 방식, 그리고 클라우드 연결까지 한눈에 확인할 수 있습니다.

 

 

실제 사용 예제

예제 1: Wi-Fi STA + SoftAP + BLE 동시 사용 (Arduino IDE)

#include <WiFi.h>
#include <BLEDevice.h>
#include <BLEServer.h>

// ========== Wi-Fi 설정 ==========
const char* STA_SSID = "YourHomeWiFi";
const char* STA_PASSWORD = "YourPassword";

const char* AP_SSID = "ESP32_Gateway";
const char* AP_PASSWORD = "12345678";

// ========== BLE 설정 ==========
#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

BLEServer *pServer = NULL;
BLECharacteristic *pCharacteristic = NULL;

// Wi-Fi 이벤트 핸들러
void WiFiEvent(WiFiEvent_t event) {
  switch(event) {
    case SYSTEM_EVENT_STA_GOT_IP:
      Serial.print("STA IP: ");
      Serial.println(WiFi.localIP());
      break;
    case SYSTEM_EVENT_AP_STACONNECTED:
      Serial.println("AP에 클라이언트 연결됨");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      Serial.println("AP 클라이언트 연결 해제");
      break;
  }
}

// BLE 콜백
class MyCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pCharacteristic) {
    std::string value = pCharacteristic->getValue();
    if (value.length() > 0) {
      Serial.print("BLE 데이터 수신: ");
      Serial.println(value.c_str());
      
      // 수신한 데이터를 Wi-Fi로 전송 (예: HTTP POST)
      // sendToCloud(value);
    }
  }
};

void setup() {
  Serial.begin(115200);
  
  // Wi-Fi 이벤트 등록
  WiFi.onEvent(WiFiEvent);
  
  // 1. Wi-Fi STA 모드 설정 (인터넷 연결)
  WiFi.mode(WIFI_AP_STA);  // 동시 모드 활성화!
  WiFi.begin(STA_SSID, STA_PASSWORD);
  
  // 2. SoftAP 모드 설정 (로컬 기기 연결)
  WiFi.softAP(AP_SSID, AP_PASSWORD);
  Serial.print("AP IP: ");
  Serial.println(WiFi.softAPIP());
  
  // 3. BLE 초기화
  BLEDevice::init("ESP32_Gateway");
  pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  
  pCharacteristic = pService->createCharacteristic(
    CHARACTERISTIC_UUID,
    BLECharacteristic::PROPERTY_READ |
    BLECharacteristic::PROPERTY_WRITE
  );
  pCharacteristic->setCallbacks(new MyCallbacks());
  pCharacteristic->setValue("Hello BLE");
  
  pService->start();
  
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->start();
  
  Serial.println("ESP32 Gateway 준비 완료!");
  Serial.println("- Wi-Fi STA: 인터넷 연결");
  Serial.println("- Wi-Fi AP: 로컬 네트워크 192.168.4.1");
  Serial.println("- BLE: 저전력 기기 통신");
}

void loop() {
  // STA 연결 상태 확인
  if (WiFi.status() == WL_CONNECTED) {
    // 인터넷 연결된 상태에서 작업
  }
  
  // AP 연결된 클라이언트 수 확인
  int apClients = WiFi.softAPgetStationNum();
  Serial.printf("AP 클라이언트: %d\n", apClients);
  
  delay(5000);
}

 

 

예제 2: Promiscuous 모드로 패킷 스니핑 (보안 모니터링)

 

#include "esp_wifi.h"

// Wi-Fi 채널 설정
#define CHANNEL 1

// 패킷 구조체
typedef struct {
  uint8_t mac[6];
  int8_t rssi;
  uint8_t channel;
} wifi_client_t;

// Promiscuous 콜백
void promiscuous_callback(void *buf, wifi_promiscuous_pkt_type_t type) {
  wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)buf;
  
  // 패킷 길이 확인
  int len = pkt->rx_ctrl.sig_len;
  uint8_t *data = pkt->payload;
  
  // 802.11 프레임 타입 확인
  uint8_t frame_type = data[0] & 0xFC;
  
  // Probe Request 감지 (기기 스캐닝 중)
  if (frame_type == 0x40) { // Probe Request
    Serial.print("Probe Request 감지! MAC: ");
    for (int i = 10; i < 16; i++) {
      Serial.printf("%02X:", data[i]);
    }
    Serial.printf(" RSSI: %d dBm\n", pkt->rx_ctrl.rssi);
  }
  
  // Beacon 프레임 감지 (주변 AP 목록)
  if (frame_type == 0x80) { // Beacon
    Serial.print("Beacon: ");
    // SSID 추출 (데이터 오프셋 24부터)
    int ssid_len = data[37];
    for (int i = 0; i < ssid_len && i < 32; i++) {
      Serial.print((char)data[38 + i]);
    }
    Serial.printf(" (Ch:%d, RSSI:%d)\n", 
      pkt->rx_ctrl.channel, pkt->rx_ctrl.rssi);
  }
}

void setup() {
  Serial.begin(115200);
  
  // Wi-Fi 초기화
  WiFi.mode(WIFI_MODE_STA);
  WiFi.disconnect();
  
  // Promiscuous 모드 활성화
  esp_wifi_set_promiscuous(true);
  esp_wifi_set_promiscuous_rx_cb(&promiscuous_callback);
  
  // 채널 설정
  esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);
  
  Serial.println("Promiscuous 모드 시작 - 주변 패킷 스니핑 중...");
}

void loop() {
  // 채널 호핑 (1~13 채널 순회)
  static uint8_t channel = 1;
  esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
  channel = (channel % 13) + 1;
  delay(200);
}

 

 

예제 3: Bluetooth Classic + BLE 동시 사용 (오디오 + 센서)

 

#include <BluetoothSerial.h>
#include <BLEDevice.h>
#include <BLEServer.h>

BluetoothSerial SerialBT;  // Bluetooth Classic (SPP)

// BLE 설정
#define BLE_SERVICE_UUID "180D"  // Heart Rate Service
#define BLE_CHAR_UUID "2A37"

BLECharacteristic *hrCharacteristic;

void setup() {
  Serial.begin(115200);
  
  // 1. Bluetooth Classic (오디오/데이터 통신용)
  SerialBT.begin("ESP32_Audio");  // 블루투스 기기 이름
  Serial.println("Bluetooth Classic 시작");
  
  // 2. BLE (센서 데이터용)
  BLEDevice::init("ESP32_Sensor");
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(BLE_SERVICE_UUID);
  
  hrCharacteristic = pService->createCharacteristic(
    BLE_CHAR_UUID,
    BLECharacteristic::PROPERTY_NOTIFY
  );
  
  pService->start();
  
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(BLE_SERVICE_UUID);
  pAdvertising->start();
  
  Serial.println("BLE Heart Rate 서비스 시작");
}

void loop() {
  // Bluetooth Classic으로 데이터 수신
  if (SerialBT.available()) {
    String data = SerialBT.readString();
    Serial.print("BT Classic 수신: ");
    Serial.println(data);
  }
  
  // BLE로 센서 데이터 전송 (예: 심박수)
  static uint8_t heartRate = 60;
  uint8_t hrValue[2] = {0x06, heartRate};  // Flags + 값
  hrCharacteristic->setValue(hrValue, 2);
  hrCharacteristic->notify();
  
  heartRate = 60 + random(0, 40);  // 60~100 BPM 시뮬레이션
  
  delay(1000);
}

 

 

예제 4: 완전한 IoT 게이트웨이 (ESP-IDF 프레임워크)

 

#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_bt.h"
#include "esp_ble_mesh.h"

// 동시 모드 설정
void init_dual_mode() {
    // 1. Wi-Fi 초기화 (STA + AP 동시)
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    
    // STA 설정
    wifi_config_t sta_config = {
        .sta = {
            .ssid = "HomeWiFi",
            .password = "password",
        },
    };
    
    // AP 설정
    wifi_config_t ap_config = {
        .ap = {
            .ssid = "ESP_Gateway",
            .password = "12345678",
            .max_connection = 4,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK,
        },
    };
    
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &sta_config));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &ap_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    
    // 2. 블루투스 초기화 (Classic + BLE 동시)
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
    
    // Dual Mode 활성화
    ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BTDM));
    
    // BLE Mesh 초기화 (메시 네트워크용)
    ESP_ERROR_CHECK(esp_ble_mesh_init(&provision, &composition));
}

// 데이터 브리지: BLE → Wi-Fi
void ble_to_wifi_bridge(uint8_t *data, uint16_t len) {
    if (WiFi.status() == WL_CONNECTED) {
        // HTTP POST로 클라우드 전송
        // 또는 MQTT Publish
    }
}

// 데이터 브리지: Wi-Fi → BLE
void wifi_to_ble_bridge(uint8_t *data, uint16_t len) {
    // BLE Notification으로 전송
    esp_ble_gatts_send_indicate(gatts_if, conn_id, 
        char_handle, len, data, false);
}

 

 

주의사항 및 성능 팁

시트
 
 
항목설명
메모리 사용 동시 모드 시 RAM 사용량이 크게 증가합니다. PSRAM(외부 RAM) 사용을 권장합니다.
CPU 부하 Wi-Fi + BT 동시 사용 시 CPU 코어 하나가 Radio Driver를 독점합니다. FreeRTOS 태스크 우선순위를 조정하세요.
전력 소모 동시 모드 시 전류 소모가 150~250mA에 달합니다. 배터리 사용 시 주의하세요.
안테나 공유 Wi-Fi와 블루투스가 동일한 2.4GHz 안테나를 공유합니다. TDMA 방식으로 시간 분할 사용합니다.
채널 충돌 Wi-Fi와 BLE 모두 2.4GHz 대역을 사용하므로, Wi-Fi 채널 1/6/11과 BLE 채널 간 간섭에 주의하세요.

 

실제 활용 아키텍처 예시

이미지 2: ESP32 데이터 브리지 흐름도 BLE 센서에서 수집한 데이터가 ESP32 게이트웨이를 거쳐 Wi-Fi로 클라우드에 전송되는 전체 흐름을 보여줍니다. BLE RX → Process → Wi-Fi TX → MQTT Publish 단계와 함께, BT Speaker를 통한 알림 및 Smartphone을 통한 설정/모니터링 연결도 포함되어 있습니다.

 

이러한 동시 모드 기능을 활용하면 단일 ESP32 보드로 Wi-Fi 게이트웨이 + 블루투스 브리지 + 로컬 AP + 보안 모니터 역할을 모두 수행할 수 있습니다.

 

 

 

반응형

캐어랩 고객 지원

취업, 창업의 막막함, 외주 관리, 제품 부재!

당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약, 아이디어는 있지만 구현할 기술이 없는 막막함.

우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.

이제 고민을 멈추고, 캐어랩을 만나세요!

코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.

제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!

귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.

지난 30년 여정, 캐어랩이 얻은 모든 것을 함께 나누고 싶습니다.

카카오 채널 추가하기

카톡 채팅방에서 무엇이든 물어보세요

당신의 성공을 위해 캐어랩과 함께 하세요.

캐어랩 온라인 채널 바로가기

캐어랩