프로젝트 요구사항:
임베디드 시스템 설계 요청사항: 소형 전기 기기를 모니터링하는 마이크로 스마트 미터 역할을 하는 모듈 개발. 소형 전기 기기에 연결하여 전압, 전류 및 전력을 측정하고, 블루투스를 통해 원격으로 전원을 차단할 수 있는 기능이 필요합니다.
프로젝트 분석:
이 프로젝트는 ESP32 플랫폼을 기반으로 구현됩니다. 이 모듈은 내장 블루투스 기능을 갖추고 있어 별도의 블루투스 모듈 구매가 필요하지 않습니다.
디스플레이로는 OLED 모듈을 사용하며, 전압 및 전류 센서를 연결하여 전기 기기의 전압과 전류를 측정합니다. 시스템에는 전원을 제어하기 위한 릴레이가 추가되었으며, ESP32의 전원이 부족할 경우를 대비해 확장 보드를 사용하여 보드에 전원을 공급합니다.
시뮬레이션을 위해 LED나 DC 모터를 전기 기기로 사용합니다.
실험 플랫폼:
메인 컨트롤러: ESP32
구매 링크: https://item.taobao.com/item.htm?id=675317800762&skuId=4855638220833
전압 전류 센서:
OLED 디스플레이:
릴레이:
LED:
DC 모터:
스마트폰 블루투스 앱:
앱 스크린샷 및 APK 설치 파일:
앱 구매 링크: https://pan.baidu.com/s/1llxYHOOaDbKY6SHFSPb7ww?pwd=nix1 추출 코드: nix1
하드웨어 연결:
OLED 디스플레이 연결 표:
| OLED 모듈 핀 | ESP32 MCU 핀 |
|---|---|
| SCK | 18 |
| SDA | 13 |
| RES | 15 |
| DC | 5 |
| CS | 17 |
전압 전류 센서 연결 표:
| INA219 모듈 핀 | ESP32 MCU 핀 |
|---|---|
| VCC | 5V |
| GND | GND |
| SCL | GPIO22 |
| SDA | GPIO21 |
| VIN- | 전기 기기 양극 |
| VIN+ | 릴레이 COM 포트 |
릴레이 연결 표:
| 릴레이 핀 | MCU 핀 | 전기 기기 핀 (LED 또는 DC 모터) | INA219 전류계 |
|---|---|---|---|
| 릴레이 출력: | |||
| NO | 5V | ||
| COM | VIN+ | ||
| NC | GND | 전기 기기 음극 | |
| 릴레이 입력: | |||
| DC+ | 5V | ||
| DC- | GND | ||
| IN | GPIO12 |
참고:
전기 기기의 음극은 릴레이 NC 포트에 연결되며, 릴레이의 NC 포트는 GND와 연결됩니다. 따라서 전기 기기의 음극은 GND에 연결됩니다. 릴레이에 전원이 공급된 후 IN 핀에 하이 레벨 신호가 출력되면 전기 기기가 작동합니다.
소프트웨어 구현:
이 프로젝트는 주요 세 부분으로 구성되며, 각 부분은 아두이노의 서드파티 라이브러리를 사용하여 구현됩니다. 구체적인 코드는 다음과 같습니다:
코드 상세:
헤더 파일 포함:
/* 블루투스 BLE 헤더 파일 */
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <stdio.h> // sprintf 함수 사용
/* OLED 관련 헤더 파일 */
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/* INA219 관련 헤더 파일 */
#include <Wire.h>
#include <Adafruit_INA219.h>
각 모듈 관련 변수 및 매크로 정의:
/* 전압 센서 관련 변수 */
#define VOLT_POT 26
int volt_pot_value = 0;
/* 릴레이 관련 변수 */
#define RELAY_PIN 12
/* INA219 관련 변수 */
Adafruit_INA219 ina219;
float shuntvoltage = 0;
float busvoltage = 0;
float current_mA = 0;
float loadvoltage = 0;
float power_mW = 0;
/* OLED 관련 변수 */
#define WIDTH 128 // OLED 디스플레이 너비(픽셀)
#define HEIGHT 64 // OLED 디스플레이 높이(픽셀)
// 소프트웨어 SPI 버스
// 소프트웨어 SPI를 사용하여 연결된 SSD1306 디스플레이 선언:
#define OLED_SCK 18
#define OLED_SDA 13
#define OLED_RES 15
#define OLED_DC 5
#define OLED_CS 17
char display_oled[64] = {};
// 객체 생성
Adafruit_SSD1306 display_oled(WIDTH, HEIGHT, OLED_SDA, OLED_SCK, OLED_DC, OLED_RES, OLED_CS);
/* 블루투스 관련 변수 */
#define BLE_BUFFER_SIZE 64
BLECharacteristic *pCharacteristic;
bool deviceConnected = false; // 블루투스 장치 연결 상태
char ble_buffer[BLE_BUFFER_SIZE] = {0}; // 블루투스로 전송할 데이터 저장용
// uint32_t cnt = 0;
int device_type_flag = 1;
char send_once_flag = 0; // 한 번만 전송하는 플래그
char weight_start_flag = 1; // 전원 켜졌을 때 출력 플래그
char weight_middle_flag = 0;
char weight_end_flag = 0;
char get_start_flag = 0;
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART 서비스 UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" // 스마트폰 -> 모듈
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" // 모듈 -> 스마트폰
블루투스 처리 함수:
/* 콜백 서비스 클래스 */
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
/* 시리얼 모니터에 "블루투스 연결 성공" 출력 */
Serial.println("블루투스 연결 성공");
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("블루투스 연결 해제");
};
};
/* 데이터 수신 함수 */
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
send_once_flag = 1; // 한 번만 전송하도록 플래그 설정
Serial.print("------> 수신된 값: ");
/* 수신된 데이터 모두 출력 */
for (int i = 0; i < rxValue.length(); i++) {
Serial.print(rxValue[i]);
}
Serial.println(); // 줄바꿈 출력
/* 특수 문자 찾기 */
if(rxValue.find("전원꺼") != -1) // 전원 꺼짐 수신
{
/* 시리얼 모니터에 전원 꺼짐 출력, 릴레이 핀 LOW로 설정 */
Serial.println("전원 꺼짐");
digitalWrite(RELAY_PIN, LOW);
}
else if(rxValue.find("전켜") != -1) // 전원 켜짐 수신
{
/* 시리얼 모니터에 전원 켜짐 출력, 릴레이 핀 HIGH로 설정 */
Serial.println("전원 켜짐");
digitalWrite(RELAY_PIN, HIGH);
}
}
}
};
setup 함수:
void setup() {
/*********** 시리얼 통신 초기화 ***********/
Serial.begin(115200); // 시리얼 통신 속도 115200
/* INA219 초기화 */
if (! ina219.begin())
{
Serial.println("INA219 칩을 찾을 수 없습니다");
}
/* 보정 */
ina219.setCalibration_16V_400mA();
/* 전압 센서 초기화 */
pinMode(VOLT_POT, INPUT);
/* 릴레이 초기화 */
pinMode(RELAY_PIN, OUTPUT);
/********** OLED 초기화 **********/
/* 테스트 결과, 폰트 크기 0일 때 최대 7줄, 20열 출력 가능 */
// OLED 초기화
if(!display_oled.begin())
{
// 초기화 실패
Serial.println("OLED 초기화 실패");
}
// OLED 화면 지우기
display_oled.clearDisplay();
// 커서 위치 설정
display_oled.setCursor(1, 1);
// 텍스트 색상 설정
display_oled.setTextColor(SSD1306_WHITE);
// 폰트 크기 설정
display_oled.setTextSize(0); // 0-9까지의 폰트 크기
// 문자열 출력 (테스트 결과, 폰트 크기 0일 때 최대 7줄, 20열 출력 가능)
sprintf(display_oled,"초기화완료\r\n");
display_oled.println(display_oled);
// 내용 표시
display_oled.display();
/*********** 블루투스 초기화 ***********/
// BLE 장치 생성
BLEDevice::init("ESP32 BLE 테스트");
// 블루투스 서버 생성
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// 서비스 UUID 생성
BLEService *pService = pServer->createService(SERVICE_UUID);
// 속성 UUID 생성
pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
pCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
pCharacteristic->setCallbacks(new MyCallbacks());
// 블루투스 서비스 시작
pService->start();
// 브로드캐스팅 시작
pServer->getAdvertising()->start();
Serial.println("클라이언트 연결 대기 중...");
}
loop 함수:
void loop() {
if (deviceConnected) { // 장치가 연결된 경우 매초마다 txValue 전송
#if 0
/* 전압 값 읽기 */
volt_pot_value = analogReadMilliVolts(VOLT_POT);
/* 전압 값 계산 */
volt_pot_value = volt_pot_value * 5 / 1024 * 100; // 값 100배로 증폭
/* 전압 값 출력 */
Serial.println(volt_pot_value);
#endif
/* 전압 및 전류 값 읽기 */
shuntvoltage = ina219.getShuntVoltage_mV();
busvoltage = ina219.getBusVoltage_V();
current_mA = ina219.getCurrent_mA();
power_mW = ina219.getPower_mW();
loadvoltage = busvoltage + (shuntvoltage / 1000);
/* 전류 값 출력 */
Serial.print("부하 전압: "); Serial.print(loadvoltage); Serial.println(" V");
Serial.print("전류: "); Serial.print(current_mA); Serial.println(" mA");
Serial.print("전력: "); Serial.print(power_mW); Serial.println(" mW");
Serial.println("");
/* 전압 및 전류 값을 OLED로 표시 */
// OLED 화면 지우기
display_oled.clearDisplay();
// 커서 위치 설정
display_oled.setCursor(1, 1);
// 문자열 출력 (테스트 결과, 폰트 크기 0일 때 최대 7줄, 20열 출력 가능)
sprintf(display_oled,"전압=%.2fV, 전류=%.2fmA, 전력=%.2fmW\r\n",loadvoltage,current_mA,power_mW);
display_oled.println(display_oled);
// 내용 표시
display_oled.display();
/* 전압 및 전류 값을 블루투스로 전송 */
pCharacteristic->setValue((uint8_t *)display_oled,strlen(display_oled)); // 데이터를 pCharacteristic에 저장
pCharacteristic->notify(); // 앱으로 값 전송
}
delay(1000);
}
디버깅 상세:
GPT에 문의한 결과, 보정이 필요하다는 답변을 받았습니다. 이에 따라 보정을 진행하더니 문제가 해결되었습니다.
실험 플랫폼 실제 사진:
실험 결과:
참고 자료:
ESP-01S 기반 USB 전압 전류계:
https://blog.csdn.net/qq_35042880/article/details/123518816?ops_request_misc=%257B%2522request_id%2522%253A%2522171803672616800213089603%2522%252C%2522scm%2522%253A%252220140713.130102334.pc_all.%2522%257D&request_id=171803672616800213089603&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-10-123518816-null-null.142^v100^pc_search_result_base9&utm_term=ESP32개발 ina219 i2c 모듈&spm=1018.2226.3001.4187
INA219 전압 전류계:
https://blog.csdn.net/HaaSTech/article/details/124375577?ops_request_misc=%257B%2522request_id%2522%253A%2522171803664116800178517970%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171803664116800178517970&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-2-124375577-null-null.142^v100^pc_search_result_base9&utm_term=ina219 i2c 모듈&spm=1018.2226.3001.4187