DFplayer 원격제어 앱 v 2.2

안드로이드 버전업에 따라 안드로이드 특정버전에서만 와이이파이 및 블루투스 연결이 가능합니다. 

(*** 와이파이 연결은 안드로이드8 [갤럭시S7]이하 버전에서만 작동합니다 ***)

(*** 블루투스 연결은 안드로이드10 이하 버전에서만 작동합니다 ***)

 

https://play.google.com/store/apps/details?id=com.appybuilder.dfcontroller.DFcontroller

더보기

Arduino Bluetooth controller PWM 앱을 기반으로 프로그램되어 Arduino Bluetooth controller PWM기능을 똑같이 사용할 수 있습니다. 

Arduino Bluetooth controller PWM 기능 살펴보기

https://postman.tistory.com/13

 

블루투스 2.0 또는 BLE 모듈을 이용하여 직접 dfplayer를 제어하거나 아두이노 또는 ESP8266 개발보드, ESP32 개발보드의 블루투스 또는 와이파이 연결을 통해 dfplayer를 제어할 수 있습니다. 

 

1. SD카드에 사운드 파일 저장 방법

DFplayer 모듈 메뉴얼상 저장방법중 99개까지의 폴더를 사용하고 각 폴더당 255개까지의 사운드 파일을 저장하는 방식을 사용합니다. ADVERT폴더의 사용 유무에 따라 아래 저장 방식을 사용하면 됩니다. 

 

A. 저장방식 1

폴더명: 01 ~ 99까지 각 폴더별 파일갯수 1 ~ 255

파일명: 숫자 3자리 001 ~ 255 반드시 포함하는 파일명으로, 최대 파일갯수 255개 까지 각 폴더에 저장된 파일 인식

예) 폴더           파일명

      01       /     001.mp3, 002.wav, 003 시크릿a.mp3,,,,,, 255 yayayat.mp3

      02       /     001.mp3, 002.wav, 003 시크릿a.mp3,,,,,, 255 yayayat.mp3

 

B. 저장방식 2 (저장방식 1에 추가로 ADVERT 폴더 사용)

폴더명: 01 ~ 99까지 각 폴더별 파일갯수 1 ~ 255

파일명: 숫자 3자리 001 ~ 255 반드시 포함하는 파일명으로, 최대 파일갯수 255개 까지 각 폴더에 저장된 파일 인식

ADVERT 폴더: mp3 또는 wave파일의 이름은 4자리 이하 숫자로만 저장되어야만 한다. 1 ~ 3000

예) 폴더             파일명

      01          /    001.mp3, 002.wav, 003 시크릿a.mp3,,,,,, 255 yayayat.mp3

      02          /    001.mp3, 002.wav, 003 시크릿a.mp3,,,,,, 255 yayayat.mp3

      ADVERT  /    0001.mp3/wav, 0002.mp3/wav, 0003 시크릿a.mp3/wav, .......3000.mp3/wav.

※ 블루투스 연결 옵션에서 ADVERT 체크항목에 체크를 하고 연결해야 한다.

 

2. 기능 

2-1. 메인화면

 

 

2-2. 통신연결 / 옵션 설정

 

체크박스 옵션

- Used saved Data:

DFplayer로 부터 수신한 트랙 정보가 있을 경우 저장된 데이터를 이용하여 DFplayer를 제어합니다.
통신모듈 연결시 자동으로 실행되는 트랙 정보 질의 및 수신 과정을 생략합니다.
- ADVERT:   

SD카드에 ADVERT폴더가 있을경우 반드시 체크가 되어 있어야 합니다.
ADVERT 체크 변경시 초기화를 시작합니다.
- Custom Shuffle:





DFplayer의 랜덤 재생 기능을 사용하지 않고 안드로이드 프로그램에서 셔플 재생을 실행하는 
옵션입니다. 
옵션이 체크되어 있을 경우 전체 트랙 반복시 ADVERT폴더의 트랙은 재생되지 않습니다. 
또한, 폴더 1회 재생기능이 활성화됩니다.
- No Use Arduino:

아두이노 제어를 위한 프로그램(버튼/PWM제어)을 사용하지 않을 경우 체크합니다.
아두이노를 통한 DFplayer 연결과는 상관없습니다. 

 

통신 연결 세부 옵션

블루투스 4.0 BLE 연결을 위한 Custom UUID 설정 및 아두이노 제어를 위한 PWM 제어 간격 및 데이터 수신 간격을 설정할 수 있습니다.

 

2-3. 아이콘별 기능

 ADVERT 폴더 리스트 보기
 EQ 설정
 DCmode - 폴더번호와 트랙번호를 이용해서 제어하는 화면
 사용자 트랙 목록 설정


 폴더별 트랙 목록 보기
 폴더별 트랙 목록 불러오기
 폴더 목록 불러오기
 사용자 트랙 목록 사용 유무 설정
 사용자 트랙 목록 삭제 
 앱 전용 데이터 베이스 삭제 - 초기화시 저장된 폴더 및 트랙 숫자 포함 전체 데이터가 삭제 됩니다. 
 재생
 이전 곡 재생
 다음 곡 재생
 첫번째 트랙 재생 - 폴더에서는 폴더 첫번째, Root에서는 전체 트랙 첫번째
 마지막 트랙 재생 - 폴더에서는 폴더 마지막, Root에서는 전체 트랙 마지막
 재생중인 곡 반복재생 설정
 ADVERT폴더내 트랙 포함 전체 트랙 반복(Custom shuffle 사용 안할경우 )
 ADVERT폴더내 트랙 포함 전체 트랙 랜덤 재생(Custom shuffle 사용 안할경우 )
 Custom shuffle 사용할 때만 활성화 되며, 폴더내 셔플 재생
 Custom shuffle 사용할 때만 활성화 되며, 폴더내 전체 트랙 한번 재생

 

2-4 DCmode

 

폴더내 사운드 파일 번호 또는 ADVERT 파일 번호를 입력하여 제어할 수 있습니다. 

폴더제어 : 폴더를 기준으로 제어할 수 있습니다. 

              폴더 반복재생, 폴더 셔플 / 폴더 1회 재생(Custom Shuffle이 체크된 경우 활성화 됨)

 

3. 모듈 연결 방법

3-1. DFplayer + Bluetooth 2.0 연결 

특별한 설정없이 DFcontroller를 실행하고 블루투스에 연결하면 초기화를 시작하여 SD카드내 트랙정보를 자동으로 저장하게 됩니다. 트랙정보(폴더 갯수 및 트랙 갯수)가 생성되면 mp3 플레이어처럼 사용할 수 있습니다.

 

 

 

3-2. DFplayer + Bluetooth 2.0 / Bluetooth 4.0 BLE + Arduino 연결 

       (Bluetooth 2.0: HC-06, Bluetooth 4.0 BLE: AT-09)

아두이노 하드웨어 시리얼과 소프트웨어 시리얼을 이용하여 DFplayer를 제어할 수 있습니다. 

DFplayer는 소프트웨어 시리얼을 사용하고 블루투스 모듈을 하드웨어 시리얼을 사용하게 됩니다. 

아두이노에 아래 첨부된 스케치를 업로드 해야만 정상적으로 제어를 할 수 있습니다.

하드웨어 시리얼은 아두이노에 스케치를 업로드 하는데에도 사용되므로 스케치를 업로드 할 경우에는 아두이노 RX / TX핀에서 블루투스 TX / RX핀을 제거한 상태에서 업로드 해야만 정상적으로 스케치를 업로드 할 수 있습니다. 업로드가 완료된 뒤에는 다시 연결하고 앱에서 블루투스에 연결후 제어할 수 있습니다.  

 

  

DFplayer_bluetooth_basic.ino
다운로드

 

더보기

#include <SoftwareSerial.h> 

#define DF_rxPin 2 // DFplayer RX -> arduino 2

#define DF_txPin 3 // DFplayer TX -> arduino 3 

SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX)

 

bool send_df = false;

 

void setup() {

  Serial.begin(9600);   // bluetooth Serial 

  dfSerial.begin(9600); // DFplayer Serial

}

 

void loop() {

  if (dfSerial.available()) {    

    Serial.write(dfSerial.read());  // send to android via bluetooth

  }

  if (Serial.available()) {  

    if (Serial.peek() == 0x7E) send_df = true; 

    if (send_df == true) {   

      uint8_t temp = Serial.read();  

      if (temp == 0xEF) send_df = false;  

      dfSerial.write(temp);  // send to DFplayer

    }        

  }

}

DFplayer와 아두이노의 LED, pwm 동시에 제어하기

DFplayer_bluetooth_basic_with_pin_control_RGB_LED.ino
다운로드

 

 

더보기

#include <SoftwareSerial.h> 

#define DF_rxPin 2 // DFplayer RX -> arduino 2

#define DF_txPin 3 // DFplayer TX -> arduino 3 

SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX)

 

#define ledPin 13

 

// 블루투스 앱 디지털 핀 제어용 변수

bool get_pin_val = false;

uint8_t pin_count = 0;

uint8_t pin_a[3] = {0, };

uint8_t pin_val;

 

// 블루투스 앱 PWM 제어용 변수

bool get_pwm_val = false;

uint8_t pwm_count = 0;

uint8_t pwm_a[5] = {0, };

uint16_t pwm1 = 0;  

uint16_t pwm2 = 0;  

uint16_t pwm3 = 0;  

 

bool send_df = false;

 

void setup() {

  Serial.begin(9600);   // bluetooth Serial 

  dfSerial.begin(9600); // DFplayer Serial

  pinMode(ledPin, OUTPUT);

}

 

void loop() {

  if (dfSerial.available()) { 

    Serial.write(dfSerial.read());  // send to android via bluetooth

  }

  if (Serial.available()) {  

    if (Serial.peek() == 0x7E) send_df = true; 

    else if (Serial.peek() == 0xF3) get_pwm_val = true; 

    else if (Serial.peek() == 0xF0) get_pin_val = true; 

    if (send_df == true) {   

        uint8_t temp = Serial.read();  

        if (temp == 0xEF) send_df = false;  

        dfSerial.write(temp);  // send to DFplayer

    }   

    else if (get_pwm_val == true ) { // pwm 제어값 수신

      uint8_t temp = Serial.read();

      pwm_a[pwm_count] = temp;

      pwm_count++; 

      if (temp == 0xF1) {

        get_pwm_val = false;

        pwm_count = 0;

        pwm_control();

      }

    }

    else if (get_pin_val == true) {  // 디지털 핀 제어값 수신

      uint8_t temp = Serial.read();

      pin_a[pin_count] = temp;

      pin_count++;

      if (temp == 0xF1) {

        get_pin_val = false;

        pin_count = 0;

        pin_control();

      }

    }

    else {

      if (Serial.peek() == 0xF8) {

        String temp = Serial.readStringUntil('\n');

        Serial.println(temp);

      }

      else if (Serial.peek() == 0xF9) {

        String temp = Serial.readStringUntil('\n');

        Serial.println(temp);

      } 

      else {

        char text = Serial.read();  // text value for some purpose.

        if (text > 0 && text != 0x7E) Serial.write(text); // print text to Serial Monitor

      }

    }

  }

}

 

void pin_control() {

  pin_val = pin_a[1];

  if (pin_val != 0) {

    switch (pin_val) {

      case 11: digitalWrite(ledPin, true); // button 1 : on

               Serial.println("LED ON");

                  break;

      case 10: digitalWrite(ledPin, false); // button 1 : off

               Serial.println("LED OFF");

                  break;

    }

    pin_val = 0;

  }

}

 

void pwm_control() {

  uint16_t temp = pwm_a[3];

  temp = temp << 8 | pwm_a[2];

  if (pwm_a[1] == 1) {  // 슬라이드 1번

    pwm1 = temp;

    Serial.print("pwm1: "); Serial.println(pwm1);

  }

  else if (pwm_a[1] == 2) { // 슬라이드 2번

    pwm2 = temp;

    Serial.print("pwm2: "); Serial.println(pwm2);

  }

  else if (pwm_a[1] == 3) {  // 슬라이드 3번

    pwm3 = temp;

    Serial.print("pwm3: "); Serial.println(pwm3);

  }

}

3-3. DFplayer + NodeMcu(esp8266) + 내장 wifi 연결

NodeMcu에 기본 장치된 wifi의 soft AP 접속을 통해 DFplayer를 제어합니다. 

 

DFcontroller_esp8266_wifi_basic.ino
다운로드

 

더보기

#include <ESP8266WiFi.h>

#define AP_SSID  "Esp12"      // AP 이름

#define AP_PASS  "1234test"   // AP 비밀번호

 

WiFiServer server(80);

WiFiClient client;

 

#include <SoftwareSerial.h> 

#define DF_rxPin 4 // DFplayer RX -> NodeMcu D2

#define DF_txPin 5 // DFplayer TX -> NodeMcu D1 

SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX)

 

String income_wifi = ""; // 와이파이 수신 스트링 저장 변수

 

uint8_t received_df[10] = {0, }; // DFplayer에서 받은 데이터 저장

String df_value1 = "";  // 안드로이드에 전송할 값

String df_value2 = "";  // 안드로이드에 전송할 값

 

char buf[5]; // 스트링 헥사값 십진수 변경용 변수, FF -> 255

 

uint8_t df_count = 0;       // 연속된 값 받기위한 카운트

bool wait_receive = false;  // dfplayer에 질의후 대기시에만 dfSerial 확인용 플래그

bool convert = false;       // dfplayer의 데이터수신후 스트링 변환 진입 플래그

 

// DFplayer command 변수

uint8_t cmd_d; 

uint8_t ack_d;

uint8_t msb_d;

uint8_t lsb_d;

uint8_t df_command[] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF};

 

void send_command(uint8_t cmd, uint8_t ack, uint8_t msb, uint8_t lsb) {

  df_command[3] = cmd;

  df_command[4] = ack;

  df_command[5] = msb;

  df_command[6] = lsb;

  for(int i = 0; i < sizeof(df_command); i++){

    dfSerial.write(df_command[i]);

  }

}

 

void setup() {

  Serial.begin(115200); // 시리얼 모니터 보드레이트 -> 115200 

  WiFi.softAP(AP_SSID, AP_PASS);  // WiFi.softAP(ssid, password);

  IPAddress myIP = WiFi.softAPIP();

  Serial.print("AP IP address: ");

  Serial.println(myIP);

  server.begin();

  dfSerial.begin(9600); // DFplayer Serial baud rate: 9600

}

 

void loop() { 

  if (wait_receive == true) {

    if (dfSerial.available()) {

      if (dfSerial.peek() == 0x7E) {

        uint8_t dump = dfSerial.readBytes(received_df, 10);

        df_count++;

        if (df_count == 2) wait_receive = false;

        convert = true;

      }

      else {

        uint8_t dump = dfSerial.read();

      }

    }

  }

  if (convert == true) {

    if (df_count == 1) {

      for(int i = 0; i < 10; i++) {

        if (received_df[i] < 16) df_value1 += String('0') + String(received_df[i], HEX);

        else df_value1 += String(received_df[i], HEX);

      }

      received_df[10] = {0,};

    }

    else if (df_count == 2) {

      for(int i = 0; i < 10; i++) {

        if (received_df[i] < 16) df_value2 += String('0') + String(received_df[i], HEX);

        else df_value2 += String(received_df[i], HEX);

      }

      received_df[10] = {0,};

      df_count = 0;

    }

    convert = false;

  }

  wifi_delay(); // 와이파이 데이터 읽기

}

 

unsigned long int one_millis = 0;

 

void wifi_delay() { // 30 밀리초 마다 client 연결 확인 및 데이터 수신

  if (millis() - one_millis > 30) {

    one_millis = millis();

    wifi_read();

  }

}

 

void wifi_read() { // wifi 데이터 수신

  client = server.available(); // 서버가 작동하고 있으면 client 생성

  if(client.available()) {     // 클라이언트에 데이터가 있으면

    income_wifi = client.readStringUntil('\r');

    if (income_wifi.indexOf("7EFF06") != -1) {          // 종료문자가 있으면

      String wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+6, income_wifi.indexOf("7EFF06")+8);

      wifi_temp.toCharArray(buf, 5);

      cmd_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+8, income_wifi.indexOf("7EFF06")+10);

      wifi_temp.toCharArray(buf, 5);

      ack_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+10, income_wifi.indexOf("7EFF06")+12);

      wifi_temp.toCharArray(buf, 5);

      msb_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+12, income_wifi.indexOf("7EFF06")+14);

      wifi_temp.toCharArray(buf, 5);

      lsb_d = strtol(buf, NULL, 16);

      send_command(cmd_d, ack_d, msb_d, lsb_d);

      wait_receive = true;

      Serial.println(wait_receive);

      Serial.print(cmd_d); Serial.print(ack_d); Serial.print(msb_d); Serial.println(lsb_d);

    }

    else if (income_wifi.indexOf("%%F5") != -1) { // 스케쥴 접속확인 응답, 텍스트 및 로컬값 전송

      client.println("HTTP/1.1 200 OK\r\n");      // 스케쥴 웹요청시 응답 헤더 

      if (df_value1 != "") { 

        client.print(df_value1);  

        df_value1 = "";  

        df_count = 0;

      }

      if (df_value2 != "") { 

        client.print(df_value2);  

        df_value2 = "";  

        df_count = 0;

      }

    }

    else {  // 텍스트 시리얼 출력

      String wifi_temp = income_wifi.substring(income_wifi.indexOf("GET /")+5, income_wifi.indexOf("HTTP/1.1")-1);

      if (wifi_temp.indexOf("%20") != -1) { // 웹브라우저 공백문자 %20을 공백문자로 변경

        String space = "%20";

        String space_convert = " ";

        wifi_temp.replace(space, space_convert);

      }

    }

    income_wifi = "";

  }

}

DFplayer와 아두이노의 LED, pwm 동시에 제어하기

DFcontroller_esp8266_wifi_full.ino
다운로드

더보기

#include <ESP8266WiFi.h>

#define AP_SSID  "Esp12"      // AP 이름

#define AP_PASS  "1234test"   // AP 비밀번호

 

WiFiServer server(80);

WiFiClient client;

 

#include <SoftwareSerial.h> 

#define DF_rxPin 4 // DFplayer RX -> NodeMcu D2

#define DF_txPin 5 // DFplayer TX -> NodeMcu D1 

SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX)

 

String income_wifi = ""; // 와이파이 수신 스트링 저장 변수

 

uint8_t received_df[10] = {0, }; // DFplayer에서 받은 데이터 저장

String df_value1 = "";  // 안드로이드에 전송할 값

String df_value2 = "";  // 안드로이드에 전송할 값

 

char buf[5]; // 스트링 헥사값 십진수 변경용 변수, FF -> 255

 

uint8_t df_count = 0;       // 연속된 값 받기위한 카운트

bool wait_receive = false;  // dfplayer에 질의후 대기시에만 dfSerial 확인용 플래그

bool convert = false;       // dfplayer의 데이터수신후 스트링 변환 진입 플래그

 

// DFplayer command 변수

uint8_t cmd_d; 

uint8_t ack_d;

uint8_t msb_d;

uint8_t lsb_d;

uint8_t df_command[] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF};

 

void send_command(uint8_t cmd, uint8_t ack, uint8_t msb, uint8_t lsb) {

  df_command[3] = cmd;

  df_command[4] = ack;

  df_command[5] = msb;

  df_command[6] = lsb;

  for(int i = 0; i < sizeof(df_command); i++){

    dfSerial.write(df_command[i]);

  }

}

 

void setup() {

  Serial.begin(115200); // 시리얼 모니터 보드레이트 -> 115200 

  WiFi.softAP(AP_SSID, AP_PASS);  // WiFi.softAP(ssid, password);

  IPAddress myIP = WiFi.softAPIP();

  Serial.print("AP IP address: ");

  Serial.println(myIP);

  server.begin();

  dfSerial.begin(9600); // DFplayer Serial baud rate: 9600

}

 

void loop() { 

  if (wait_receive == true) {

    if (dfSerial.available()) {

      if (dfSerial.peek() == 0x7E) {

        uint8_t dump = dfSerial.readBytes(received_df, 10);

        df_count++;

        if (df_count == 2) wait_receive = false;

        convert = true;

      }

      else {

        uint8_t dump = dfSerial.read();

      }

    }

  }

  if (convert == true) {

    if (df_count == 1) {

      for(int i = 0; i < 10; i++) {

        if (received_df[i] < 16) df_value1 += String('0') + String(received_df[i], HEX);

        else df_value1 += String(received_df[i], HEX);

      }

      received_df[10] = {0,};

    }

    else if (df_count == 2) {

      for(int i = 0; i < 10; i++) {

        if (received_df[i] < 16) df_value2 += String('0') + String(received_df[i], HEX);

        else df_value2 += String(received_df[i], HEX);

      }

      received_df[10] = {0,};

      df_count = 0;

    }

    convert = false;

  }

  wifi_delay(); // 와이파이 데이터 읽기

}

 

unsigned long int one_millis = 0;

 

void wifi_delay() { // 30 밀리초 마다 client 연결 확인 및 데이터 수신

  if (millis() - one_millis > 30) {

    one_millis = millis();

    wifi_read();

  }

}

 

void wifi_read() { // wifi 데이터 수신

  client = server.available(); // 서버가 작동하고 있으면 client 생성

  if(client.available()) {     // 클라이언트에 데이터가 있으면

    income_wifi = client.readStringUntil('\r');

    if (income_wifi.indexOf("7EFF06") != -1) {          // 종료문자가 있으면

      String wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+6, income_wifi.indexOf("7EFF06")+8);

      wifi_temp.toCharArray(buf, 5);

      cmd_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+8, income_wifi.indexOf("7EFF06")+10);

      wifi_temp.toCharArray(buf, 5);

      ack_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+10, income_wifi.indexOf("7EFF06")+12);

      wifi_temp.toCharArray(buf, 5);

      msb_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+12, income_wifi.indexOf("7EFF06")+14);

      wifi_temp.toCharArray(buf, 5);

      lsb_d = strtol(buf, NULL, 16);

      send_command(cmd_d, ack_d, msb_d, lsb_d);

      wait_receive = true;

      Serial.println(wait_receive);

      Serial.print(cmd_d); Serial.print(ack_d); Serial.print(msb_d); Serial.println(lsb_d);

    }

    else if (income_wifi.indexOf("%%F1") != -1) {          // 종료문자가 있으면

      if (income_wifi.indexOf("%%F0") != -1) {        // 디지털 핀 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F0")+4, income_wifi.indexOf("%%F1"));

        pin_val = wifi_temp.toInt();

        pin_control();

      }

      else if (income_wifi.indexOf("%%F31") != -1) {  // 슬라이드1, PWM 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F31")+5, income_wifi.indexOf("%%F1"));

        pwm1 = wifi_temp.toInt();

        Serial.print("pwm1: "); Serial.println(pwm1);

      }

      else if (income_wifi.indexOf("%%F32") != -1) {  // 슬라이드2, PWM 값 수신 

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F32")+5, income_wifi.indexOf("%%F1"));

        pwm2 = wifi_temp.toInt();

        Serial.print("pwm2: "); Serial.println(pwm2);

      }

      else if (income_wifi.indexOf("%%F33") != -1) {  // 슬라이드3, PWM 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F33")+5, income_wifi.indexOf("%%F1"));

        pwm3 = wifi_temp.toInt();

        Serial.print("pwm3: "); Serial.println(pwm3); 

      }

    }

    else if (income_wifi.indexOf("%%F5") != -1) { // 스케쥴 접속확인 응답, 텍스트 및 로컬값 전송

      client.println("HTTP/1.1 200 OK\r\n");      // 스케쥴 웹요청시 응답 헤더 

      if (df_value1 != "") { 

        client.print(df_value1);  

        df_value1 = "";  

        df_count = 0;

      }

      if (df_value2 != "") { 

        client.print(df_value2);  

        df_value2 = "";  

        df_count = 0;

      }

      if (send_pin != "") {                       // 전송할 핀 상태 값이 있으면 "/ HTTP/1.1"

        client.print(send_pin); 

        send_pin = "";

      }

      if (send_Pwm_echo != "") {                  // 전송할 PWM 상태(echo) 값이 있으면

        client.print(send_Pwm_echo);

        send_Pwm_echo = "";

      }

      if (send_Pwm_slide != "") {                 // 전송할 PWM 슬라이브 바 상태 값이 있으면

        client.print(send_Pwm_slide);

        send_Pwm_slide = "";

      }

      if (send_text != "") {                      // 전송할 텍스트가 있으면

        client.print(send_text);

        send_text = "";

      }

    }

    else{

      if (income_wifi.indexOf("%%F8") != -1) {  // 슬라이드3, PWM 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F8")+4, income_wifi.indexOf("\n"));

        Serial.print("Date/Time: "); Serial.println(wifi_temp); 

      }

      else if (income_wifi.indexOf("%%F9") != -1) {  // 슬라이드3, PWM 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F9")+4, income_wifi.indexOf("\n"));

        Serial.print("Alarm set: "); Serial.println(wifi_temp); 

      }

      else {  // 텍스트 시리얼 출력

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("GET /")+5, income_wifi.indexOf("HTTP/1.1")-1);

        if (wifi_temp.indexOf("%20") != -1) { // 웹브라우저 공백문자 %20을 공백문자로 변경

          String space = "%20";

          String space_convert = " ";

          wifi_temp.replace(space, space_convert);

        }

        Serial.println(wifi_temp);

      }

    }

    income_wifi = "";

  }

}

soft AP 접속방법

통신연결 설정 화면으로 이동한 뒤 No Use Arduino 옵션을 체크해제 합니다. 

블루투스 아이콘을 클릭하여 설정화면을 빠져나갑니다. 

 

wifi 아이콘을 클릭합니다. 

 

스마트폰 와이파이 세팅에서 NodeMcu의 soft AP에 접속을 한뒤 뒤로 가기를 클릭하면 soft AP입력 창이 표시되어 있습니다. 그 곳에 AP 주소를 입력하고 confirm 버튼을 클릭합니다. 

 

연결이 되면 초기화를 시작하고 SD카드내 트랙정보를 자동으로 저장하게 됩니다. 트랙정보(폴더 갯수 및 트랙 갯수)가 생성되면 mp3 플레이어처럼 사용할 수 있습니다.

 

3-4. DFplayer + ESP32 + 내장 Bluetooth 2.0 / Bluetooth BLE / wifi 연결

Esp32 개발보드는 블루투스와 와이파이 기본 장착되어 있습니다. 장착된 블루투스와 와이파이를 이용해 제어할 수 있습니다.

 

 

ESP32 Bluetooth 2.0 코드

DFcontroller_esp32_BT_uart_full.ino
다운로드

 

더보기

#include <HardwareSerial.h> 

#define DF_rxPin 17 // DFplayer RX -> arduino 17

#define DF_txPin 16 // DFplayer TX -> arduino 16 

HardwareSerial dfSerial(2);  // 이름 정의, 시리얼 이름이 Serial1, 2 가 아닌경우 사용 

 

#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

 

String s =""; // 시리얼 모니터 텍스트 제어용 변수

 

// 블루투스 앱 디지털 핀 제어용 변수

bool get_pin_val = false;

uint8_t pin_count = 0;

uint8_t pin_a[3] = {0, };

uint8_t pin_val;

 

// 블루투스 앱 PWM 제어용 변수

bool get_pwm_val = false;

uint8_t pwm_count = 0;

uint8_t pwm_a[5] = {0, };

uint16_t pwm1 = 0;  

uint16_t pwm2 = 0;  

uint16_t pwm3 = 0;  

 

bool send_df = false;

 

// 블루투스 앱 상태표시 변수

uint8_t pin_echo[] = {0xF0, 0, 0xF1};

uint8_t pwm_echo[] = {0xF3, 0, 0, 0, 0xF1};

uint8_t pwm_slide[] = {0xF4, 0, 0, 0, 0xF1};

 

void setup() {

  Serial.begin(115200);

  dfSerial.begin(9600,SERIAL_8N1, DF_txPin, DF_rxPin);  // (통신속도, UART모드, RX핀번호, TX핀번호) - 핀번호 지정가능

  SerialBT.begin("ESP32test"); //Bluetooth device name

}

 

void loop() {

  if (dfSerial.available()) { 

    uint8_t temp = dfSerial.read();

    SerialBT.write(temp);  // send to android via bluetooth

  }

  SerialBT_read(); // 블루투스 시리얼 읽기 함수

  if(Serial.available()) { // 시리얼 버퍼에 데이터 있으면

    char c = Serial.read();  

    if(c != '\n') {

      s += c;

    } else {

      if(s == "on"){ // 시리얼 모니터 제어 led on

        Serial.println("LED ON");

        send_pin_echo(11); // 블루투스 앱 버튼 상태: on

        SerialBT.print("LED ON");

        s = "";  

      }

      else if(s == "off"){ // 시리얼 모니터 제어 led off 

        Serial.println("LED OFF"); 

        send_pin_echo(10); // 블루투스 앱 버튼 상태: off

        SerialBT.print("LED OFF");  

        s = "";

      }

      else {

        SerialBT.println(s); 

        s = "";

      }

    }

  }

}

 

void general_use() { 

  char text = SerialBT.read();  // text value for some purpose.

  Serial.write(text);      // print text to Serial Monitor

}

 

void pin_control() {

  pin_val = pin_a[1];

  if (pin_val != 0) {

    switch (pin_val) {

      case 11: Serial.println("button 1 : on"); 

                  break;

      case 10: Serial.println("button 1 : off");

                  break;

      case 21: Serial.println("button 2 : on");

               send_pin_echo(pin_val); // 버튼 상태 echo 

                  break;

      case 20: Serial.println("button 2 : off");

               send_pin_echo(pin_val); // 버튼 상태 echo 

                  break;

      case 31: Serial.println("button 3 : on");

                  break;

      case 30: Serial.println("button 3 : off");

                  break;

      case 41: Serial.println("button 4 : on");

                  break;

      case 40: Serial.println("button 4 : off");

                  break;

      case 51: Serial.println("button 5 : on");

                  break;

      case 50: Serial.println("button 5 : off");

                  break;

      case 61: Serial.println("button 6 : on");

                  break;

      case 60: Serial.println("button 6 : off");

                  break;

      case 71: Serial.println("button 7 : on");

                  break;

      case 70: Serial.println("button 7 : off");

                  break;

      case 81: Serial.println("button 8 : on");

                  break;

      case 80: Serial.println("button 8 : off");

                  break;

      case 91: Serial.println("button 9 : on");

                  break;

      case 90: Serial.println("button 9 : off");

                  break;  

      case 101: Serial.println("button 10 : on");

                  break;

      case 100: Serial.println("button 10 : off");

                  break;  

      case 111: Serial.println("button 11 : on");

                  break;

      case 110: Serial.println("button 11 : off");

                  break;  

      case 121: Serial.println("button 12 : on");

                  break;

      case 120: Serial.println("button 12 : off");

                  break;  

    }

  pin_val = 0;

  }

}

 

void pwm_control() {

  uint16_t temp = pwm_a[3];

  temp = temp << 8 | pwm_a[2];

  if (pwm_a[1] == 1) {  // 슬라이드 1번

    pwm1 = temp;

//    send_pwm_echo(1, pwm1); // PWM 값 echo

    Serial.print("pwm1: "); Serial.println(pwm1);

  }

  else if (pwm_a[1] == 2) { // 슬라이드 2번

    pwm2 = temp;

    Serial.print("pwm2: "); Serial.println(pwm2);

  }

  else if (pwm_a[1] == 3) {  // 슬라이드 3번

    pwm3 = temp;

    Serial.print("pwm3: "); Serial.println(pwm3);

  }

}

 

void SerialBT_read() {

  if (SerialBT.available()) { 

    if (SerialBT.peek() == 0x7E) send_df = true;  

    else if (SerialBT.peek() == 0xF3) get_pwm_val = true; 

    else if (SerialBT.peek() == 0xF0) get_pin_val = true; 

    if (send_df == true) {   

        uint8_t temp = SerialBT.read();  

        if (temp == 0xEF) send_df = false;  

        dfSerial.write(temp);  // send to DFplayer

    }

    else if (get_pwm_val == true ) { // pwm 제어값 수신

      uint8_t temp = SerialBT.read();

      pwm_a[pwm_count] = temp;

      pwm_count++; 

      if (temp == 0xF1) {

        get_pwm_val = false;

        pwm_count = 0;

        pwm_control();

      }

    }

    else if (get_pin_val == true) {  // 디지털 핀 제어값 수신

      uint8_t temp = SerialBT.read();

      pin_a[pin_count] = temp;

      pin_count++;

      if (temp == 0xF1) {

        get_pin_val = false;

        pin_count = 0;

        pin_control();

      }

    }

    else {

      if (SerialBT.peek() == 0xF8) {

        String temp = SerialBT.readStringUntil('\n');

        Serial.println(temp);

      }

      else if (SerialBT.peek() == 0xF9) {

        String temp = SerialBT.readStringUntil('\n');

        Serial.println(temp);

      } 

      else {

        general_use();  // 텍스트 출력함수

      }

    }

  }

}

 

void send_pin_echo(uint8_t pin_val){

  pin_echo[1] = pin_val;

  for (int i = 0; i < 3; i++) {

    SerialBT.write(pin_echo[i]);

  }

}

 

void send_pwm_echo(uint8_t slide, uint16_t pwm_val) {

  pwm_echo[1] = slide;

  if (pwm1 < 256) {

    pwm_echo[2] = pwm_val;

    pwm_echo[3] = 0;

  }

  else {

    pwm_echo[2] = pwm_val;

    pwm_echo[3] = pwm_val >> 8;

  }

  for (int i = 0; i < 5; i++) {

    SerialBT.write(pwm_echo[i]);

  }

}

 

void send_pwm_slide(uint8_t slide, uint16_t pwm_val) {

  pwm_slide[1] = slide;

  if (pwm1 < 256) {

    pwm_slide[2] = pwm_val;

    pwm_slide[3] = 0;

  }

  else {

    pwm_slide[2] = pwm_val;

    pwm_slide[3] = pwm_val >> 8;

  }

  for (int i = 0; i < 5; i++) {

    SerialBT.write(pwm_slide[i]);

  }

}

ESP32 Wifi Soft AP 코드

DFcontroller_esp32_wifi_full.ino
다운로드

더보기

#include <WiFi.h>

#include <WiFiClient.h>

#include <WiFiAP.h>

#define AP_SSID  "Esp32"

#define AP_PASS  "1234test"

 

String s =""; // 시리얼 모니터 텍스트 제어용 변수

 

// 디지털 핀 제어용 변수

uint8_t pin_val = 0;

 

// PWM 제어용 변수

uint16_t pwm1 = 0;  

uint16_t pwm2 = 0;  

uint16_t pwm3 = 0;  

 

String income_wifi = ""; // 와이파이 수신 스트링 저장 변수

 

// 웹요청 응답용 변수

String send_pin = "";

String send_Pwm_echo = "";

String send_Pwm_slide = "";

String send_text = "";

 

WiFiServer server(80);

WiFiClient client;

 

#include <HardwareSerial.h> 

#define DF_rxPin 17 // DFplayer RX -> arduino 17

#define DF_txPin 16 // DFplayer TX -> arduino 16 

HardwareSerial dfSerial(2);  // 이름 정의, 시리얼 이름이 Serial1, 2 가 아닌경우 사용

 

uint8_t received_df[10] = {0, }; // DFplayer에서 받은 데이터 저장

String df_value1 = "";  // 안드로이드에 전송할 값

String df_value2 = "";  // 안드로이드에 전송할 값

 

char buf[5]; // 스트링 헥사값 십진수 변경용 변수, FF -> 255

 

uint8_t df_count = 0;       // 연속된 값 받기위한 카운트

bool wait_receive = false;  // dfplayer에 질의후 대기시에만 dfSerial 확인용 플래그

bool convert = false;       // dfplayer의 데이터수신후 스트링 변환 진입 플래그

 

// DFplayer command 변수

uint8_t cmd_d; 

uint8_t ack_d;

uint8_t msb_d;

uint8_t lsb_d;

uint8_t df_command[] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF};

 

void send_command(uint8_t cmd, uint8_t ack, uint8_t msb, uint8_t lsb) {

  df_command[3] = cmd;

  df_command[4] = ack;

  df_command[5] = msb;

  df_command[6] = lsb;

  for(int i = 0; i < sizeof(df_command); i++){

    dfSerial.write(df_command[i]);

  }

}

 

void setup() {

  Serial.begin(115200);

  WiFi.softAP(AP_SSID, AP_PASS);

  IPAddress myIP = WiFi.softAPIP();

  Serial.print("AP IP address: ");

  Serial.println(myIP);

  server.begin();

  dfSerial.begin(9600); // DFplayer Serial baud rate: 9600

}

 

void loop() {

  if (wait_receive == true) {

    if (dfSerial.available()) {

      if (dfSerial.peek() == 0x7E) {

        uint8_t dump = dfSerial.readBytes(received_df, 10);

        df_count++;

        if (df_count == 2) wait_receive = false;

        convert = true;

      }

      else {

        uint8_t dump = dfSerial.read();

      }

    }

  }

  if (convert == true) {

    if (df_count == 1) {

      for(int i = 0; i < 10; i++) {

        if (received_df[i] < 16) df_value1 += String('0') + String(received_df[i], HEX);

        else df_value1 += String(received_df[i], HEX);

      }

      received_df[10] = {0,};

    }

    else if (df_count == 2) {

      for(int i = 0; i < 10; i++) {

        if (received_df[i] < 16) df_value2 += String('0') + String(received_df[i], HEX);

        else df_value2 += String(received_df[i], HEX);

      }

      received_df[10] = {0,};

      df_count = 0;

    }

    convert = false;

  }

  wifi_delay(); // 와이파이 데이터 읽기

  if (Serial.available()) {   

    char c = Serial.read();  

    if(c != '\n') {

      s += c;

    } else {

      if(s == "on"){ // 시리얼 모니터 제어 led on

        Serial.println("LED ON");

        send_pin_echo(11);        // 스케쥴 웹요청 응답, 블루투스 앱 버튼 상태: on

        send_text = "LED ON";     // 스케쥴 웹요청 응답

        s = "";  

      }

      else if(s == "off"){ // 시리얼 모니터 제어 led off 

        Serial.println("LED OFF");

        send_pin_echo(10);        // 스케쥴 웹요청 응답, 블루투스 앱 버튼 상태: off

        send_text = "LED OFF";    // 스케쥴 웹요청 응답

        s = "";

      }

      else {

        send_text = s;  // 스케쥴 웹요청시 응답변수에 저장

        s = "";

      }

    }      

  }

}

 

void pin_control() {

  if (pin_val != 0) {

    switch (pin_val) {

      case 11: Serial.println("button 1 : on"); 

               client.println("HTTP/1.1 200 OK\r\n"); // 사용자 웹요청시 응답 헤더

               send_pin_echo(pin_val);

               client.print("LED_ON_11"); 

                  break;

      case 10: Serial.println("button 1 : off");

               client.println("HTTP/1.1 200 OK\r\n"); // 사용자 웹요청시 응답 헤더

               send_pin_echo(pin_val);

               client.print("LED_ON_10");

                  break;

      case 21: Serial.println("button 2 : on");

                  break;

      case 20: Serial.println("button 2 : off");

                  break;

      case 31: Serial.println("button 3 : on");

                  break;

      case 30: Serial.println("button 3 : off");

                  break;

      case 41: Serial.println("button 4 : on");

                  break;

      case 40: Serial.println("button 4 : off");

                  break;

      case 51: Serial.println("button 5 : on");

                  break;

      case 50: Serial.println("button 5 : off");

                  break;

      case 61: Serial.println("button 6 : on");

                  break;

      case 60: Serial.println("button 6 : off");

                  break;

      case 71: Serial.println("button 7 : on");

                  break;

      case 70: Serial.println("button 7 : off");

                  break;

      case 81: Serial.println("button 8 : on");

                  break;

      case 80: Serial.println("button 8 : off");

                  break;

      case 91: Serial.println("button 9 : on");

                  break;

      case 90: Serial.println("button 9 : off");

                  break;  

      case 101: Serial.println("button 10 : on");

                  break;

      case 100: Serial.println("button 10 : off");

                  break;  

      case 111: Serial.println("button 11 : on");

                  break;

      case 110: Serial.println("button 11 : off");

                  break;  

      case 121: Serial.println("button 12 : on");

                  break;

      case 120: Serial.println("button 12 : off");

                  break;  

    }

  pin_val = 0;

  }

}

 

unsigned long int one_millis = 0;

 

void wifi_delay() { // 30 밀리초 마다 client 연결 확인 및 데이터 수신

  if (millis() - one_millis > 30) {

    one_millis = millis();

    wifi_read();

  }

}

 

void wifi_read() { // wifi 데이터 수신

  client = server.available(); // 서버가 작동하고 있으면 client 생성

  if(client.available()) {     // 클라이언트에 데이터가 있으면

    income_wifi = client.readStringUntil('\r');

    if (income_wifi.indexOf("7EFF06") != -1) {          // 종료문자가 있으면

      String wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+6, income_wifi.indexOf("7EFF06")+8);

      wifi_temp.toCharArray(buf, 5);

      cmd_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+8, income_wifi.indexOf("7EFF06")+10);

      wifi_temp.toCharArray(buf, 5);

      ack_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+10, income_wifi.indexOf("7EFF06")+12);

      wifi_temp.toCharArray(buf, 5);

      msb_d = strtol(buf, NULL, 16);

      wifi_temp = "0x" + income_wifi.substring(income_wifi.indexOf("7EFF06")+12, income_wifi.indexOf("7EFF06")+14);

      wifi_temp.toCharArray(buf, 5);

      lsb_d = strtol(buf, NULL, 16);

      send_command(cmd_d, ack_d, msb_d, lsb_d);

      wait_receive = true;

      Serial.println(wait_receive);

      Serial.print(cmd_d); Serial.print(ack_d); Serial.print(msb_d); Serial.println(lsb_d);

    }

    else if (income_wifi.indexOf("%%F1") != -1) {          // 종료문자가 있으면

      if (income_wifi.indexOf("%%F0") != -1) {        // 디지털 핀 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F0")+4, income_wifi.indexOf("%%F1"));

        pin_val = wifi_temp.toInt();

        pin_control();

      }

      else if (income_wifi.indexOf("%%F31") != -1) {  // 슬라이드1, PWM 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F31")+5, income_wifi.indexOf("%%F1"));

        pwm1 = wifi_temp.toInt();

        Serial.print("pwm1: "); Serial.println(pwm1);

      }

      else if (income_wifi.indexOf("%%F32") != -1) {  // 슬라이드2, PWM 값 수신 

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F32")+5, income_wifi.indexOf("%%F1"));

        pwm2 = wifi_temp.toInt();

        Serial.print("pwm2: "); Serial.println(pwm2);

      }

      else if (income_wifi.indexOf("%%F33") != -1) {  // 슬라이드3, PWM 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F33")+5, income_wifi.indexOf("%%F1"));

        pwm3 = wifi_temp.toInt();

        Serial.print("pwm3: "); Serial.println(pwm3); 

      }

    }

    else if (income_wifi.indexOf("%%F5") != -1) { // 스케쥴 접속확인 응답, 텍스트 및 로컬값 전송

      client.println("HTTP/1.1 200 OK\r\n");      // 스케쥴 웹요청시 응답 헤더 

      if (df_value1 != "") { 

        client.print(df_value1);  

        df_value1 = "";  

        df_count = 0;

      }

      if (df_value2 != "") { 

        client.print(df_value2);  

        df_value2 = "";  

        df_count = 0;

      }

      if (send_pin != "") {                       // 전송할 핀 상태 값이 있으면 "/ HTTP/1.1"

        client.print(send_pin); 

        send_pin = "";

      }

      if (send_Pwm_echo != "") {                  // 전송할 PWM 상태(echo) 값이 있으면

        client.print(send_Pwm_echo);

        send_Pwm_echo = "";

      }

      if (send_Pwm_slide != "") {                 // 전송할 PWM 슬라이브 바 상태 값이 있으면

        client.print(send_Pwm_slide);

        send_Pwm_slide = "";

      }

      if (send_text != "") {                      // 전송할 텍스트가 있으면

        client.print(send_text);

        send_text = "";

      }

    }

    else{

      if (income_wifi.indexOf("%%F8") != -1) {  // 슬라이드3, PWM 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F8")+4, income_wifi.indexOf("\n"));

        Serial.print("Date/Time: "); Serial.println(wifi_temp); 

      }

      else if (income_wifi.indexOf("%%F9") != -1) {  // 슬라이드3, PWM 값 수신

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("%%F9")+4, income_wifi.indexOf("\n"));

        Serial.print("Alarm set: "); Serial.println(wifi_temp); 

      }

      else {  // 텍스트 시리얼 출력

        String wifi_temp = income_wifi.substring(income_wifi.indexOf("GET /")+5, income_wifi.indexOf("HTTP/1.1")-1);

        if (wifi_temp.indexOf("%20") != -1) { // 웹브라우저 공백문자 %20을 공백문자로 변경

          String space = "%20";

          String space_convert = " ";

          wifi_temp.replace(space, space_convert);

        }

        Serial.println(wifi_temp);

      }

    }

    income_wifi = "";

  }

}

 

void send_pin_echo(uint8_t pin_val){  // 디지털 버튼 상태값 전송

  String s_temp ="";

  s_temp += "%%F0";

  s_temp += pin_val;

  s_temp += "%%P1";

  if (client.connected())  client.print(s_temp); // 사용자 웹요청 응답

  else  send_pin = s_temp;                       // 스케쥴 웹요청 응답

}

 

void send_pwm_echo(uint8_t slide, uint16_t pwm_val) { // PWM값 echo 전송

  String s_temp ="";                                  

  s_temp += "%%F3";

  s_temp += slide;

  s_temp += pwm_val;

  s_temp += "%%E1";

  if (client.connected())  client.print(s_temp); // 사용자 웹요청 응답

  else  send_Pwm_echo = s_temp;                  // 스케쥴 웹요청 응답

}

 

void send_pwm_slide(uint8_t slide, uint16_t pwm_val) { // 슬라이더 상태값 전송

  String s_temp ="";

  s_temp += "%%F4";

  s_temp += slide;

  s_temp += pwm_val;

  s_temp += "%%S1";

  if (client.connected())  client.print(s_temp); // 사용자 웹요청 응답

  else  send_Pwm_slide = s_temp;                 // 스케쥴 웹요청 응답

}

ESP32 Bluetooth 4.0 BLE 코드

DFcontroller_esp32_ble_uart_full.ino
다운로드

더보기

#include <HardwareSerial.h> 

#define DF_rxPin 17 // DFplayer RX -> arduino 17

#define DF_txPin 16 // DFplayer TX -> arduino 16 

HardwareSerial dfSerial(2);  // 이름 정의, 시리얼 이름이 Serial1, 2 가 아닌경우 사용 

 

#include <BLEDevice.h>

#include <BLEServer.h>

#include <BLEUtils.h>

#include <BLE2902.h>

 

BLEServer *pServer = NULL;

BLECharacteristic * pTxCharacteristic;

bool deviceConnected = false;

bool rx_received = false;      // BLE 수신시 함수 실행 플래그

std::string rxValue; // BLE 클래스 수신 데이터 값 전역변수 설정

 

#define SERVICE_UUID           "0000FFE0-0000-1000-8000-00805F9B34FB" // UART service UUID

#define CHARACTERISTIC_UUID_RX "0000FFE1-0000-1000-8000-00805F9B34FB"

#define CHARACTERISTIC_UUID_TX "0000FFE2-0000-1000-8000-00805F9B34FB"

 

String s =""; // 시리얼 모니터 텍스트 제어용 변수

 

// 블루투스 앱 디지털 핀 제어용 변수

bool get_pin_val = false;

uint8_t pin_a[3] = {0, };

uint8_t pin_val;

 

// 블루투스 앱 PWM 제어용 변수

bool get_pwm_val = false;

uint8_t pwm_a[5] = {0, };

uint16_t pwm1 = 0;  

uint16_t pwm2 = 0;  

uint16_t pwm3 = 0;  

 

// 블루투스 앱 상태표시 변수

uint8_t pin_echo[] = {0xF0, 0, 0xF1};

uint8_t pwm_echo[] = {0xF3, 0, 0, 0, 0xF1};

uint8_t pwm_slide[] = {0xF4, 0, 0, 0, 0xF1};

 

// DFplayer 변수

bool receive_df = false;

uint8_t df_val[10] = {0,};

uint8_t df_count = 0;

 

bool date_time = false;

bool alarm_set = false;

 

class MyServerCallbacks: public BLEServerCallbacks {

  void onConnect(BLEServer* pServer) {

    deviceConnected = true;

  };

  void onDisconnect(BLEServer* pServer) {

    deviceConnected = false;

  }

};

 

class MyCallbacks: public BLECharacteristicCallbacks {

  void onWrite(BLECharacteristic *pCharacteristic) {

    rxValue = pCharacteristic->getValue();

//    if (rxValue.length() > 0) {

//      for (int i = 0; i < rxValue.length(); i++) {

//        Serial.print(i); Serial.print(": "); Serial.println(rxValue[i], HEX);

//      }

//    }

    rx_received = true;

  }

};

 

void setup() {

  Serial.begin(115200);

  dfSerial.begin(9600,SERIAL_8N1, DF_txPin, DF_rxPin);  // (통신속도, UART모드, RX핀번호, TX핀번호) - 핀번호 지정가능

  BLEDevice::init("ESP32 BLE");  // Create the BLE Device

  pServer = BLEDevice::createServer();  // Create the BLE Server

  pServer->setCallbacks(new MyServerCallbacks());

  BLEService *pService = pServer->createService(SERVICE_UUID);  // Create the BLE Service

  pTxCharacteristic = pService->createCharacteristic (  // Create a BLE Characteristic

   CHARACTERISTIC_UUID_TX,

   BLECharacteristic::PROPERTY_NOTIFY

       );

  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic (

                      CHARACTERISTIC_UUID_RX,

                      BLECharacteristic::PROPERTY_WRITE

                    );

  pRxCharacteristic->setCallbacks(new MyCallbacks());

  pService->start();  // Start the service

  pServer->getAdvertising()->start();  // Start advertising

  Serial.println("Waiting a client connection to notify...");

}

 

void loop() {

  if (dfSerial.available()) { 

    if (dfSerial.peek() == 0x7E) receive_df = true;

    if (receive_df == true){

      uint8_t temp = dfSerial.read();

      df_val[df_count] = temp;

      df_count++;

      if (temp == 0xEF) {

        df_count = 0;

        receive_df = false;

        pTxCharacteristic->setValue((uint8_t*)&df_val,10);  // print text to App

        pTxCharacteristic->notify(); 

      }

    }

  }

  BLE_read();

  if(Serial.available()) { // 시리얼 버퍼에 데이터 있으면

    char c = Serial.read();  

    if(c != '\n') {

      s += c;

    } else {

      if(s == "on"){ // 시리얼 모니터 제어 led on

        Serial.println("LED ON");

        send_pin_echo(11); // 블루투스 앱 버튼 상태: on

        pTxCharacteristic->setValue("LED ON");

        pTxCharacteristic->notify();

        s = "";  

      }

      else if(s == "off"){ // 시리얼 모니터 제어 led off 

        Serial.println("LED OFF");

        send_pin_echo(10); // 블루투스 앱 버튼 상태: off

        pTxCharacteristic->setValue("LED OFF");

        pTxCharacteristic->notify();

        s = "";

      }

      else {

        char temp [21]; 

        s.toCharArray(temp, s.length()+1); 

        pTxCharacteristic->setValue(temp); 

        pTxCharacteristic->notify();

        s = "";

      }

    }

  }

}

 

void general_use() { 

  for (int i = 0; i < rxValue.length(); i++) Serial.write(rxValue[i]);

}

 

void pin_control() {

  pin_val = pin_a[1];

  if (pin_val != 0) {

    switch (pin_val) {

      case 11: Serial.println("button 1 : on"); 

                  break;

      case 10: Serial.println("button 1 : off"); 

                  break;

      case 21: Serial.println("button 2 : on");

               send_pin_echo(pin_val); // 버튼 상태 echo 

                  break;

      case 20: Serial.println("button 2 : off");

               send_pin_echo(pin_val); // 버튼 상태 echo 

                  break;

      case 31: Serial.println("button 3 : on");

                  break;

      case 30: Serial.println("button 3 : off");

                  break;

      case 41: Serial.println("button 4 : on");

                  break;

      case 40: Serial.println("button 4 : off");

                  break;

      case 51: Serial.println("button 5 : on");

                  break;

      case 50: Serial.println("button 5 : off");

                  break;

      case 61: Serial.println("button 6 : on");

                  break;

      case 60: Serial.println("button 6 : off");

                  break;

      case 71: Serial.println("button 7 : on");

                  break;

      case 70: Serial.println("button 7 : off");

                  break;

      case 81: Serial.println("button 8 : on");

                  break;

      case 80: Serial.println("button 8 : off");

                  break;

      case 91: Serial.println("button 9 : on");

                  break;

      case 90: Serial.println("button 9 : off");

                  break;  

      case 101: Serial.println("button 10 : on");

                  break;

      case 100: Serial.println("button 10 : off");

                  break;  

      case 111: Serial.println("button 11 : on");

                  break;

      case 110: Serial.println("button 11 : off");

                  break;  

      case 121: Serial.println("button 12 : on");

                  break;

      case 120: Serial.println("button 12 : off");

                  break;  

    }

  pin_val = 0;

  }

}

 

void pwm_control() {

  uint16_t temp = pwm_a[3];

  temp = temp << 8 | pwm_a[2];

  if (pwm_a[1] == 1) {  // 슬라이드 1번

    pwm1 = temp;

//    send_pwm_echo(1, pwm1); // PWM 값 echo

    Serial.print("pwm1: "); Serial.println(pwm1);

  }

  else if (pwm_a[1] == 2) { // 슬라이드 2번

    pwm2 = temp;

    Serial.print("pwm2: "); Serial.println(pwm2);

  }

  else if (pwm_a[1] == 3) {  // 슬라이드 3번

    pwm3 = temp;

    Serial.print("pwm3: "); Serial.println(pwm3);

  }

}

 

void BLE_read() {

  if (rx_received == true) {   

    if (rxValue[0] == 0x7E) {

      for (int i = 0; i < rxValue.length(); i++) dfSerial.write(rxValue[i]);       

    }

    else if (rxValue[0] == 0xF0) get_pin_val = true;

    else if (rxValue[0] == 0xF3) get_pwm_val = true;

    if (get_pin_val == true) {  // 디지털 핀 제어값 수신

      pin_a[0] = rxValue[0];

      pin_a[1] = rxValue[1];

      pin_a[2] = rxValue[2];

      if (pin_a[0] == 0xF0 && pin_a[2] == 0xF1) { // 수신값 검증 완료시

        pin_control();    // 디지털 핀 출력함수 실행}

      }

      get_pin_val = false;

    }

    else if (get_pwm_val == true) { // pwm 제어값 수신

      pwm_a[0] = rxValue[0];

      pwm_a[1] = rxValue[1];

      pwm_a[2] = rxValue[2];

      pwm_a[3] = rxValue[3];

      pwm_a[4] = rxValue[4];

      if (pwm_a[0] == 0xF3 && pwm_a[4] == 0xF1) { // 수신값 검증 완료시

        pwm_control(); // PWM 출력함수 실행

      }

      get_pwm_val = false;

    }

    else {

      if (rxValue[0] == 0xF8) date_time = true;

      else if (rxValue[0] == 0xF9) alarm_set = true;

      if (date_time == true) {

        String temp = "";

        for (int i = 0; i < rxValue.length(); i++) {

          if (rxValue[i] != '\n') temp += rxValue[i];

          else break;

        }

        Serial.println(temp);

      }

      else if (alarm_set == true) {

        String temp = "";

        for (int i = 0; i < rxValue.length(); i++) {

          if (rxValue[i] != '\n') temp += rxValue[i];

          else break;

        }

        Serial.println(temp);

      } 

      else {

        general_use();  // 텍스트 출력함수

      }

    }

    rx_received = false;

  }

}

 

void send_pin_echo(uint8_t pin_val){

  pin_echo[1] = pin_val;

  pTxCharacteristic->setValue((uint8_t*)&pin_echo, 3);

  pTxCharacteristic->notify();

}

 

void send_pwm_echo(uint8_t slide, uint16_t pwm_val) {

  pwm_echo[1] = slide;

  if (pwm1 < 256) {

    pwm_echo[2] = pwm_val;

    pwm_echo[3] = 0;

  }

  else {

    pwm_echo[2] = pwm_val;

    pwm_echo[3] = pwm_val >> 8;

  }

    pTxCharacteristic->setValue((uint8_t*)&pwm_echo, 5);

    pTxCharacteristic->notify();

}

 

void send_pwm_slide(uint8_t slide, uint16_t pwm_val) {

  pwm_slide[1] = slide;

  if (pwm1 < 256) {

    pwm_slide[2] = pwm_val;

    pwm_slide[3] = 0;

  }

  else {

    pwm_slide[2] = pwm_val;

    pwm_slide[3] = pwm_val >> 8;

  }

    pTxCharacteristic->setValue((uint8_t*)&pwm_slide, 5);

    pTxCharacteristic->notify();

}

 

4. App 스크린샷

   

 

 

 

   

 

 

 

 

5. 사용자 트랙 목록 만들기 및 불러오기

DFcontroller는 스마트폰에 저장된 사운드 트랙 목록을 불러와 트랙 번호와 매칭시켜 번호 대신 이름을 표시 할 수 있는 기능이 있습니다. 이를 이용하기 위해서는 사운드 트랙 목록 텍스트 파일이 필요하고 스마트폰의 내장 메모리 Download 폴더에 그 텍스트 파일을 저장한 뒤 DFcontroller 앱에서 목록을 불러와 매칭시켜 이용할 수 있습니다. 

 

5-1. 사용자 목록 만들기

메모장에서 트랙목록을 작성한뒤 텍스트 파일로 저장합니다.

목록의 행 번호가 트랙번호입니다. 

 

예)















001  테스트 폴더1.mp3 - 1행 - 트랙번호 1
002  테스트 폴더1.mp3 - 2행 - 트랙번호 2
003  테스트 폴더1.mp3
004  테스트 폴더1.mp3
005  테스트 폴더1.mp3
006  테스트 폴더1.mp3
007  테스트 폴더1.mp3
008  테스트 폴더1.mp3
009  테스트 폴더1.mp3

 

한글이 포함된 경우에는 저장시 인코딩을 "UTF-8"로 지정하고 저장해야 앱에서 한글이 깨지지 않습니다.

 

 

*** 주의: SD카드 폴더내 트랙 갯수 보다 사용자 목록상의 갯수가 적을 경우 오류가 발생됩니다.  최소 같거나 많아야 합니다. 많을 경우 SD카드 폴더내 트랙 갯수 만큼만 표시됩니다. 

 

예제파일

list01.txt
다운로드
list02.txt
다운로드
folder.txt
다운로드
ADlist.txt
다운로드

 

저장된 사용자 목록 파일을 스마트폰의 내장메모리 Download 폴더에 저장합니다. (SD카드의 Download 폴더가 아닙니다.)

예) Samsung Galaxy S7\Phone\Download

 

DFcontroller 앱에서 저장된 목록 불러오기 - DFplayer의 SD카드내 트랙 정보가 저장된 상태에서만 불러올 수 있습니다.

메인화면에서 
 을 선택하여 사용자 목록 화면으로 이동한 뒤 아래 그림처럼 진행합니다. 

 

트랙목록 불러오기 - Select File을 선택하면 스마트폰 Download 폴더내의 모든 텍스트 파일을 표시합니다. 

 

 

 

사용자 목록을 성공적으로 불러오면 아래 그림처럼 목록이 표시됩니다. 

 

폴더 및 ADVERT목록 불러오기

 

 

불러온 폴더 목록이 표시됩니다.

 

 

불러온 ADVERT목록이 표시됩니다.

 

데이터 베이스에 저장되어 있는 목록 리스트 확인하기

 

 

폴더를 선택하면 목록이 표시됩니다. 

데이터 베이스에 저장되어 있는 사용자 목록 사용하기 - 체크표시가 녹색이면 메인화면의 트랙 목록에 사용자 목록이 표시됩니다. 

 

데이터 베이스에서 사용자 목록 관련 데이터 삭제하기

 

앱 전용 데이터 베이스 삭제하기 - 앱에서 사용하는 모든 데이이터가 삭제됩니다. 

 

메인 화면으로 나가기

 

 

** 사운드 파일에 일련번호 편하게 추가하기

사운드 파일에 001 또는 0001 형식의 3자리, 4자리 번호를 추가해주는 방법으로 곰오디오 프로그램을 사용해보자. 수동으로 추가할 경우 사운드 파일이 몇개 안된다면 문제 없지만, 파일 수가 많다면 인내를 필요로하는 단순 반복 작업이 된다. 

 

곰오디오를 실행한 뒤 환경설정에 들어가서 제목표시 -> 제목 표시 방법 -> 파일명 -> 확인 을 클릭한다. 

 

  

 

아래쪽 + 아이콘(추가)를 클릭하고 폴더 추가를 선택하고 사운드 파일이 저장된 폴더를 선택한다. 

 

 

 파일 목록이 표시되면 아래 맨오른쪽 재생목록 아이콘을 클릭하고 재생목록 내보내기를 클릭한다.

리스트 순서대로 파일명 앞에 번호 붙임 이라는 항목이 체크된 상태에서 시작번호 란에 001을 넣으면 001, 002, .... 순서대로 3자리 숫자가 추가되어 저장위치에 숫자가 추가된 파일이 새롭게 저장된다. 

 

저장 위치의 폴더에는 목록형식의 데이터 파일이 아니라 원본파일의 이름이 변경된 파일이 복사된것을 확인 할 수 있다. 

이름이 수정된 파일을 DFplayer에 사용할 저장장치에 복사하면 된다. 

시작번호를 0001로 변경하고 내보내기를 클릭하면 0001, 0002 ....  이렇게 4자리 숫자가 추가되어 저장된다.

 

** DarkNamer 프로그램을 이용하여 사용자 목록 쉽게 만들기

DarkNamer.zip
다운로드

 

 

게시자를 알수없다는 경고 메세지가 표시됩니다. 무시하고 실행을 클릭합니다. 

 

파일 -> 경로목록에 파일 추가하기 클릭

 

트랙이 저장된 폴더로 가서 전체 파일을 지정해 주고 열기를 클릭합니다. 

 

목록이 불러와지면 파일 -> 문서파일로 바꿀이름 저장을 클릭합니다. 

 

원하는 위치에 저장을 하면 트랙 목록이 저장된 텍스트 파일이 생성됩니다. 

 

저장된 파일을 불러와서 확인합니다. 

 

한글이 포함된 트랙목록은 다른이름으로 저장을 클릭하신다음에 인코딩을 "UTF-8"로 지정한뒤 다시 저장해 주어야 앱에서 한글이 깨지지 않습니다. 

 

DFPlayer 사용 예제 글

 

[arduino] - DFplayer - 아두이노 사운드 모듈

[arduino] - NodeMcu(ESP8266)에서 DFplayer를 제어하는 코드

[arduino] - ESP32(DevKit)로 DFplayer 제어하기

[arduino] - 안드로이드 앱 DFcontroller를 이용하여 DFplayer 제어

[arduino] - 아두이노시계 예제, ESP01 WiFi 이용 시간 동기화 하기

[arduino] - 아두이노 말하는알람시계 예제 - DFPlayer

[arduino] - 말하는알람시계 - 블루투스 연결 및 시간 동기화, DFPlayer 제어

[arduino] - NodeMcu - 말하는 알람시계, wifi이용 시간 동기화 및 DFPlayer 원격제어

 

 

 

 

https://play.google.com/store/apps/details?id=com.tistory.postpop.hmi 

 

ADUCON - Google Play 앱

무선(WiFi/Bluetooth/BLE)을 통한 Arduino 원격 제어 앱

play.google.com

※ 특징
- 블루투스 2.0 / 블루투스 4.0 BLE 연결 : HC-05, HC-06, HM-10, AT-09, BT05
- WiFi를 통해 모듈의 Access Point 또는 공용 네트워크의 IP에 연결
- 15개의 디지털 버튼: 토글 및 푸시 버튼 속성 설정
- 6개의 아날로그 컨트롤러(SeekBar): 최소 및 최대 값 설정
- 메인 화면 표시 설정
- 버튼 레이블 사용자 정의
- 설정값 저장 기능
- RGB LED 제어를 위한 색상 선택기
- PWM 제어를 위한 다이얼 노브
- Arduino Sketch의 HTML 페이지용 웹 뷰어
- 비밀번호를 이용하여 제어권한을 설정할 수 있는 대화창 제공
- 사용자 정의 대화창 제공(라디오박스, 체크박스, 텍스트 입력 등)
- 앱이 실행되는 동안 화면이 계속 켜져 있음(화면이 꺼지는 것을 방지)

 

[Arduino] - Setting up Wi-Fi module ESP01 and basic code for Arduino remote control

[Arduino/ADUCON] - Arduino WiFi remote control with ADUCON and ESP-01

[Arduino] - Arduino Basic code for bluetooth remote contol with HC-06

[Arduino/ADUCON] - Arduino Bluetooth remote control with ADUCON and HC-06

[Arduino] - Arduino Basic code for BLE remote contol with BT05

[Arduino/ADUCON] - Arduino BLE remote control with ADUCON and BT05

[Arduino] - ESP32/NodeMcu Basic code for WiFi remote contol

[Arduino/ADUCON] - ESP32/NodeMcu WiFi remote control with ADUCON

[Arduino/ADUCON] - ESP32 Bluetooth remote control with ADUCON

[Arduino/ADUCON] - ESP32 BLE remote control with ADUCON

 

 

 

https://play.google.com/store/apps/details?id=io.kodular.skyship72.pad01 

 

ADUPAD - Google Play 앱

무선(WiFi/Bluetooth/BLE)을 통한 Arduino 원격 제어 PAD

play.google.com

- 조이스틱 2개.
- 1개의 중력 센서 컨트롤러.
- 제어용 20개 버튼: 토글 및 푸시 버튼 속성 설정 기능.
일부 버튼 레이블은 사용자 정의할 수 있습니다.
- 비밀번호를 이용하여 제어권한을 설정할 수 있는 대화창 제공
- 사용자 정의 대화창 제공(라디오박스, 체크박스, 텍스트 입력 등)
- 앱이 실행되는 동안 화면이 계속 켜져 있음(화면이 꺼지는 것을 방지)

 

[Arduino] - Setting up Wi-Fi module ESP01 and basic code for Arduino remote control

[Arduino/ADUPAD] - Arduino WiFi remote control with ADUPAD and ESP-01

[Arduino] - Arduino Basic code for bluetooth remote contol with HC-06

[Arduino/ADUPAD] - Arduino Bluetooth remote control with ADUPAD and HC-06

[Arduino] - Arduino Basic code for BLE remote contol with BT05

[Arduino/ADUPAD] - Arduino BLE remote control with ADUPAD and BT05

[Arduino] - ESP32/NodeMcu Basic code for WiFi remote contol

[Arduino/ADUPAD] - ESP32/NodeMcu WiFi remote control with ADUPAD

[Arduino/ADUPAD] - ESP32 Bluetooth remote control with ADUPAD

[Arduino/ADUPAD] - ESP32 BLE remote control with ADUPAD

 

+ Recent posts