-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathESP32_BLE_Server.ino
More file actions
233 lines (195 loc) · 7.35 KB
/
ESP32_BLE_Server.ino
File metadata and controls
233 lines (195 loc) · 7.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#define BUS_NUMBER "2221"
#include <NimBLEDevice.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_NeoPixel.h>
#include <Arduino.h>
// === [AUDIO] MP3 라이브러리 (ESP8266Audio 설치 필요) ===
#include "AudioFileSourcePROGMEM.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2S.h"
#include "sound_data.h" // 우리가 만든 MP3 데이터 (반드시 탭에 있어야 함!)
// ====== 핀 설정 ======
#define LED_PIN 3 // LED (GPIO 3)
#define NUM_PIXELS 30
#define I2C_SDA 4 // LCD SDA
#define I2C_SCL 5 // LCD SCL
#define I2S_BCLK 2 // AMP BCLK
#define I2S_LRC 10 // AMP LRC
#define I2S_DIN 11 // AMP DIN
// ====== 객체 생성 ======
Adafruit_NeoPixel pixels(NUM_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
LiquidCrystal_I2C lcd(0x27, 20, 4);
// 오디오 객체
AudioFileSourcePROGMEM *file = NULL;
AudioGeneratorMP3 *mp3 = NULL;
AudioOutputI2S *out = NULL;
// ====== 상태 변수 ======
bool isLedOn = false;
unsigned long ledStartTime = 0;
bool isMessageDisplaying = false;
unsigned long messageStartTime = 0;
// BLE 변수
NimBLEServer *pServer = NULL;
NimBLECharacteristic *pTxCharacteristic;
int connectedDevices = 0;
#define SERVICE_UUID "12345678-1234-1234-1234-123456789ABC"
#define CHARACTERISTIC_UUID_RX "12345678-1234-1234-1234-123456789ABD"
#define CHARACTERISTIC_UUID_TX "12345678-1234-1234-1234-123456789ABE"
String deviceName = "BF_DREAM_" + String(BUS_NUMBER);
// --- LED 함수 ---
void setBlue() {
for(int i=0; i<NUM_PIXELS; i++) pixels.setPixelColor(i, pixels.Color(0, 0, 255));
pixels.show();
}
void setOff() {
pixels.clear();
pixels.show();
}
// --- 오디오 재생 함수 (MP3) ---
void playMP3() {
// 오디오 객체가 제대로 생성되었는지 확인
if (mp3 == NULL || file == NULL || out == NULL) {
Serial.println("Error: Audio objects not initialized!");
return;
}
// 기존 재생 중이면 중지
if (mp3->isRunning()) mp3->stop();
// 파일 포인터 처음으로 리셋 (mom_mp3 사용)
file->open(mom_mp3, sizeof(mom_mp3));
mp3->begin(file, out);
Serial.println("MP3 Playback Started");
}
class MyServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
connectedDevices = pServer->getConnectedCount();
Serial.println("BLE Device Connected");
}
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
connectedDevices = pServer->getConnectedCount();
Serial.println("BLE Device Disconnected - Restart Advertising");
NimBLEDevice::startAdvertising();
}
};
// ====== [수정됨] 콜백 클래스 ======
class MyCallbacks: public NimBLECharacteristicCallbacks {
void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.print("Received: "); Serial.println(rxValue.c_str());
bool triggerAction = false; // 동작 실행 여부
bool playSound = false; // 소리 재생 여부
// 1. 기본 신호 (소리 O) -> "DEFAULT"
if(rxValue == "DEFAULT") {
triggerAction = true;
playSound = true;
}
// 2. 무음 신호 (소리 X) -> "SILENT"
else if(rxValue == "SILENT") {
triggerAction = true;
playSound = false;
}
// 동작 실행
if (triggerAction) {
// (1) MP3 재생 (playSound가 true일 때만)
if (playSound) {
playMP3();
}
// (2) LCD 표시
isMessageDisplaying = true;
messageStartTime = millis();
lcd.setCursor(0, 2); lcd.print("COURTESY_SEAT Signal"); // LCD 텍스트는 그대로 유지
// 소리 유무에 따라 상태 표시
lcd.setCursor(0, 3);
if(playSound) lcd.print("Voice Alert... ");
else lcd.print("Silent Alert... ");
// (3) LED 켜기
isLedOn = true;
ledStartTime = millis();
setBlue();
// (4) 앱으로 응답(ACK) 전송
pTxCharacteristic->setValue("ACK");
pTxCharacteristic->notify(connInfo.getConnHandle());
}
}
}
};
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("\n=== System Booting... ===");
Serial.print("Free Heap at start: "); Serial.println(ESP.getFreeHeap());
// 1. BLE 설정
Serial.println("1. Configuring BLE...");
NimBLEDevice::init(deviceName.c_str());
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
NimBLEService *pService = pServer->createService(SERVICE_UUID);
pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
NimBLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
pRxCharacteristic->setCallbacks(new MyCallbacks());
pService->start();
// 2. LED 초기화
Serial.println("2. Initializing LED...");
pixels.begin(); pixels.setBrightness(50); setOff();
// 3. LCD 초기화
Serial.println("3. Initializing LCD...");
Wire.begin(I2C_SDA, I2C_SCL);
lcd.init(); lcd.backlight(); lcd.clear();
lcd.setCursor(0, 0); lcd.print("BUS_NUM: "); lcd.print(BUS_NUMBER);
lcd.setCursor(0, 1); lcd.print("System Ready");
// 4. 오디오 초기화
Serial.println("4. Initializing Audio...");
file = new AudioFileSourcePROGMEM(mom_mp3, sizeof(mom_mp3));
out = new AudioOutputI2S();
out->SetPinout(I2S_BCLK, I2S_LRC, I2S_DIN);
mp3 = new AudioGeneratorMP3();
Serial.print("Free Heap after init: "); Serial.println(ESP.getFreeHeap());
// 5. BLE 광고 시작
Serial.println("5. Starting BLE Advertising...");
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
NimBLEAdvertisementData advertisementData;
advertisementData.setCompleteServices(NimBLEUUID(SERVICE_UUID));
advertisementData.setName(deviceName.c_str());
pAdvertising->setAdvertisementData(advertisementData);
NimBLEAdvertisementData scanResponseData;
scanResponseData.setName(deviceName.c_str());
pAdvertising->setScanResponseData(scanResponseData);
pAdvertising->start();
Serial.println("=== Setup Complete ===");
}
void loop() {
// MP3 재생 루프
if (mp3 != NULL && mp3->isRunning()) {
if (!mp3->loop()) {
mp3->stop();
Serial.println("MP3 Playback Finished");
}
}
// LED 타이머
if (isLedOn && (millis() - ledStartTime > 5000)) {
isLedOn = false;
setOff();
}
// LCD 타이머
if (isMessageDisplaying && (millis() - messageStartTime > 5000)) {
isMessageDisplaying = false;
lcd.setCursor(0, 2); lcd.print(" ");
lcd.setCursor(0, 3); lcd.print(" ");
}
// BLE 연결 상태 업데이트
static int prevDevices = -1;
if (connectedDevices != prevDevices) {
lcd.setCursor(0, 1);
lcd.print("Devices Connected: ");
lcd.print(connectedDevices);
lcd.print(" ");
prevDevices = connectedDevices;
}
// 동작 확인용 심장박동
static unsigned long lastHeartbeat = 0;
if (millis() - lastHeartbeat > 1000) {
lastHeartbeat = millis();
}
}