ADUPAD application
https://play.google.com/store/apps/details?id=io.kodular.skyship72.pad01
Basic code for remote control
- Set BLE library for the Bluetooth component of ESP32
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
- Library function to Inform if the bluetooth component of ESP32 has BLE connection or not
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
Serial.println(F("connected"));
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
pServer->startAdvertising(); // restart advertising
Serial.println(F("start advertising"));
}
};
- Library function to receive data through ESP32's BLE connection
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
rxValue = pCharacteristic->getValue();
// if (rxValue.length() > 0) { // output the received data to Serial Monitor
// for (int i = 0; i < rxValue.length(); i++) {
// Serial.print(i); Serial.print(": "); Serial.println(rxValue[i], HEX);
// }
// }
rx_received = true;
}
};
- BLE name and UUID settings
#define btName "ESP32ble"
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
- setup() function
void setup() {
Serial.begin(115200);
BLEDevice::init(btName); //("ESP32");// 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(F("Waiting a client connection to notify..."));
}
- loop() function
bool gotData = false;
void loop() {
if (rx_received == true) { // when incomming data from BLE
for (int i = 0; i < rxValue.length(); i++) Serial.print(rxValue[i]); // Output data of BLE to the serial monitor.
Serial.println();
gotData = true; // the flag for response
rx_received = false;
} else {
if (gotData) { // if response
char temp [21];
String setVal = "";
setVal += char(0xF4); // protocol header of text for ADUPAD with BLE
setVal += "Hello World ";
setVal += random(100,200); // the message for sending
setVal += char(0xF1); // protocol footer of text for ADUPAD with BLE
setVal += '\0'; // end character for sending, pTxCharacteristic->setValue(temp);
Serial.println(setVal); // Output the message to the serial monitor.
if (setVal.length() <= 21) { // convert String to array of Char
for (int i = 0; i < setVal.length(); i++) temp[i] = setVal[i];
}
pTxCharacteristic->setValue(temp);
pTxCharacteristic->notify();
gotData = false;
}
}
}
ESP32_BLE_BASIC.zip
- Output of Serial Monitor after upload scketch "ESP32_BLE_BASIC.ino"
connecting to BLE of ESP32 on the ADUPAD app
- Bluetooth 4.0 Search and Connection in the App
Click the Bluetooth icon in the app and select Bluetooth 4.0 in Select Ver.
Click the Bluetooth button and select the BLE of ESP32.
※ If a Bluetooth device is registered using the same device for Bluetooth Classic connection, the registered device must be removed to connect by BLE from the app.
If the device has registered, it will interfere with the BLE connection and the device will not respond to BLE connection requests from the app.
Code for remote control with BLE of ADUPAD applications
- SerialBT_read() function
1. Protocol header definition
Define the headers for BLE in the Arduino sketch according to the protocol header settings in the ADUPAD app.
#define pinHD 0xF0
#define joyHD 0xF3
#define strHD 0xF4
#define footHD 0xF1
#define echoHD 0xE1
#define connectHD 0xF8
#define connectHDstr F("%%F8")
#define optionHD 0xF9
2. Code to check the connection attempt of ADUPAD app
bool appConnection = false;
void BLE_read() {
if (appConnection) { // If app connection is confirmed
// Execute code after app connection is confirmed
} else CheckF8(""); // check "%%F8connect"
}
3. Handling of values according to connection header "connectHDstr(%%F8)"
%%F8 + connect: Transmit the necessary app settings and current Arduino state values through the iniSet() and send_State() functions.
%%F8 + disconnect: Disconnect and change value of appConnection to false.
%%F8 + NA: Change setting values for BT05.
void CheckF8(String temp) {
if (temp == "") {
String hd = connectHDstr; // %%F8
if (rxValue[0] == hd[0] && rxValue[1] == hd[1] && rxValue[2] == hd[2] && rxValue[3] == hd[3]) {
for (int i = hd.length(); i < rxValue.length(); i++) {
if (rxValue[i] != '\n') temp += rxValue[i];
else break;
}
if (temp == F("connect")) { delay(100); needAppCheck = false; appConnection = true; iniSet(); send_State(); }
} else { btDisconnect(); needAppCheck = false; } // disconnect & advertise
} else {
if (temp == F("disconnect")) appConnection = false;
else if (temp.startsWith(F("NA"))) { sendMessage(F("This funtion is not available.")); }
else Serial.println(temp);
}
}
- If "%%F8connect" is not confirmed within disconnectTime after receiving the first data through BLE, the connection is terminated and re-advertise the name of BLE.
void orderDisconnect() {
if (needAppCheck) {
if (!appConnection) {
if (millis() - disconnectTime > 3000) {
needAppCheck = false;
btDisconnect();
Serial.println(F("enter app check disconnect"));
}
}
}
}
- Command the app to click the disconnec button and terminate the connected BLE to ESP32
void btDisconnect() {
sendDisconnect();
delay(10);
pServer->disconnect(pServer->getConnId());
}
4. Handling of values according to option header "optionHDstr(%%F9)"
void CheckF9(String temp) {
Serial.println(temp);
if (temp.startsWith(F("RAD"))) { } // if it starts with "RAD"
else if (temp.startsWith(F("CKB"))) { } // if it starts with "CKB"
else if (temp.startsWith(F("CHD"))) { } // if it starts with "CHD"
else if (temp.startsWith(F("LSD"))) { } // if it starts with "LSD"
else if (temp.startsWith(F("STR"))) { } // if it starts with "STR"
}
5. Handling of values according to button header "pinHD(0xF0)"
// 0xF0 + 1byte Num + 0xF1
uint8_t cn = 0;
while (rxValue[cn] == pinHD) { // need depend on left arrow pad mode
if (rxValue[cn+2] == footHD) { // if the third byte is 0xF1
pin_control(rxValue[cn+1]); // execute the code with the value of second byte
}
cn = cn+3; // index of next 0xF0
}
6. Handling of values according to SeekBar(slide bar) header "joyHD(0xF3)"
// 0xF3 + 1byte Num(id) + 2byte Num(heading) + 1byte Num(strength) + 0xF1
if (rxValue[5] == footHD) { // if the sixth byte is 0xF1
uint16_t value = rxValue[3]; // Swap third and fourth bytes
value = value << 8 | rxValue[2];
joystick_control(rxValue[1], value, rxValue[4]); // execute the code with values
}
Protocol function to send data from Arduino to app
1. Change the state of the button.
void sendPinState(uint8_t pin_val){
pin[1] = pin_val;
pin[2] = footHD;
pTxCharacteristic->setValue((uint8_t*)&pin, 3);
pTxCharacteristic->notify();
}
2. Transmits an echo for the received value of the button.
void sendPinEcho(uint8_t pin_val){
pin[1] = pin_val;
pin[2] = echoHD;
pTxCharacteristic->setValue((uint8_t*)&pin, 3);
pTxCharacteristic->notify();
}
3. Change the Joystick state of the app.
// id 0:Left Joystick, 1:Right Joystick 2:Gravity Sensor(change only text)
void sendJoyState(uint8_t id, uint16_t heading, uint8_t strength) {
joystick[1] = id;
joystick[2] = heading;
joystick[3] = heading >> 8;
joystick[4] = strength;
joystick[5] = footHD;
pTxCharacteristic->setValue((uint8_t*)&joystick, 6);
pTxCharacteristic->notify();
}
4. Transmits the echo for the received value of the Joystick.
void sendJoyEcho(uint8_t id, uint16_t heading, uint8_t strength) {
joystick[1] = id;
joystick[2] = heading;
joystick[3] = heading >> 8;
joystick[4] = strength;
joystick[5] = echoHD;
pTxCharacteristic->setValue((uint8_t*)&joystick, 6);
pTxCharacteristic->notify();
}
5. Send text with over the limitation of transmit data length of BLE.
void sendText(String str) {
char temp [21]; // 18
uint16_t len = str.length();
uint8_t repeat = 1;
uint8_t endLen = 0;
if (len > 18) {
repeat = (len/18)+1;
endLen = (len%18);
} else endLen = len;
for (int r = 0; r < repeat; r++) {
temp[0] = strHD;
uint8_t ed = 0;
if (repeat-r > 1) ed = 18;
else ed = endLen;
for (int i = 1; i < ed+1; i++) {
temp[i] = str[(r*18)+i-1];
}
if (repeat-r > 1) temp[ed+1] = echoHD;
else temp[ed+1] = footHD;
temp[ed+2] = '\0';
pTxCharacteristic->setValue(temp);
pTxCharacteristic->notify();
}
}
6. Instruct the app to click the Disconnect Button.
void sendDisconnect(){ // order disconnect to App
uint8_t setVal[3];
setVal[0] = connectHD;
setVal[1] = 0x01;
setVal[2] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal,3);
pTxCharacteristic->notify();
}
7. Use when you want to acquire control authority by using the set password for ESP32
It can be changed to any name through definition of the passName and passNameLen for processing the password in the sketch and the app send datas starting with passName as password value. This is to enhance security.
#define passName F("PAS:")
#define pnLength 4
void requestPass(bool val){ // order disconnect to App
uint8_t setVal[3+pnLength];
String temp = passName;
setVal[0] = connectHD;
setVal[1] = 0x02;
if (val) {
setVal[2] = pnLength;
for (int i = 0; i<pnLength;i++) setVal[i+3] = temp[i];
pTxCharacteristic->setValue((uint8_t*)&setVal,3+pnLength);
} else {
setVal[2] = val;
pTxCharacteristic->setValue((uint8_t*)&setVal,3);
}
pTxCharacteristic->notify();
}
8. Response for correct or not through password confirmation
void responsePass(bool val){
uint8_t setVal[3];
setVal[0] = connectHD;
setVal[1] = 0x03;
setVal[2] = val;
pTxCharacteristic->setValue((uint8_t*)&setVal,3);
pTxCharacteristic->notify();
}
9. Outputs the message in the form of a pop-up.
void send_message(String str, uint8_t head, uint8_t fn) {
char temp [21]; // 18
uint16_t len = str.length();
uint8_t repeat = 1;
uint8_t endLen = 0;
if (len > 17) {
repeat = (len/17)+1;
endLen = (len%17);
} else endLen = len;
for (int r = 0; r < repeat; r++) {
temp[0] = head;
temp[1] = fn;
uint8_t ed = 0;
if (repeat-r > 1) ed = 17;
else ed = endLen;
for (int i = 2; i < ed+2; i++) {
temp[i] = str[(r*17)+i-2];
}
if (repeat-r > 1) temp[ed+2] = echoHD;
else temp[ed+2] = footHD;
temp[ed+3] = '\0';
pTxCharacteristic->setValue(temp);
pTxCharacteristic->notify();
}
}
void sendMessage(String str) {
send_message(str, connectHD, 0x04);
}
10. Set the function to continuously transmit the changed value of the Joystick according to the specified interval.
*** The Gravity Sensor always works in an active Mode.
true: Active Joystick Mode
false: Passive Joystick Mode (transmits only values when the slide bar is touched up)
void setActiveJoystick(bool val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x01;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
11. Set transmission interval of ACTIVE JOYSTIC MODE. (Unit: Millisecond)
void setJoystickInterval(uint16_t val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x02;
setVal[2] = val;
setVal[3] = val >> 8;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
12. Set the range where the strength of the joystick becomes 0. (enter a value from 0 to 35)
void setJoyZeroRange(uint8_t val){
if (val > 35) val = 35;
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x03;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
13. Set whether or not to use a function that converts the Strength value of Joystick or Gravity Sensor into a value between 0 and 10.
void setJoyStrengthAdjust(bool val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x04;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
14. Set the group for the left arrow keypad.
// 1: independent 2: group1(up-down) / group2 (left-right) 4: group4(only 1)
void setLeftPadMode(uint8_t val){
if (val == 0 || val > 2) val = 4;
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x05;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
15. Set the 4-way control of the joystick.
// 0: Left & Right False 1: Left 4 Way 2: Right 4Way 3: Both 4 Way
void setJoyWay4(uint8_t val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x06;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
16. Set whether or not to display the group button.
// 0: none 1: group1 2: group1&2
void setDisplayAddButton(uint8_t val){
if (val > 2) val = 2;
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x07;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
17. Set the operation function when clicking the label displaying the current connection status.
0: No function.
1: Show only connection information when it clicked.
2: Show connection information and password when it clicked so that the connection and password can be set.
// 0: not use, 1: show only name, 2: show name & pass, defalult: 2
void setSSIDLabelMode(uint8_t val) {
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x08;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
* BLE is no need to pair with Mobile Phone and the Pin Number of BT05 only used for btween master and slave of BT05.
* If pairing is attempted, an error message is displayed.
18. Set the activation interval of the gravity sensor and transmission of the tilt value.
// defalt interval : 180 millis, 180, 180 * 6 = 1080
void useGravityAndInterval(uint16_t val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x09;
setVal[2] = val;
setVal[3] = val >> 8;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
19. Set labels for buttons.
//F("BT1,BT2,BT3,BT4,BT5,BT6")
void setAddButtonLabel(String str){
send_message(str, optionHD, 0x0A);
}
20. Set the toggle/push properties of the left 4 buttons or 20 buttons of all.
//F("0000") // left pad: up-left-right-down
//F("00000000001111110000") // 0000:left, 0000:right, 001111110000:center
void setLeftPadOrAllToggle(String str){
send_message(str, optionHD, 0x0B);
}
21. Set the toggle/push properties of the right 4 buttons.
// F("0000") // right pad: top-left-right-bottom
void setRightPadToggle(String str){
send_message(str, optionHD, 0x0C);
}
22. Set the toggle/push properties of the middle 12 buttons.
// F("000000000000") // center: view-menu-bt1-bt2-bt3-bt4-bt5-bt6-LB-LT-RB-RT
void setButtonToggle(String str){ // must after setLeftPadMode(uint8_t val)
send_message(str, optionHD, 0x0D);
}
23. Dialog Message: radio button
// option = "A,B,C" -> index = 1(A) ~ 3(C)
void dialogRadio(uint8_t id, String title, String option, uint8_t selection){
String str = F("RAD:");
str += String(id); str += ':';
str += title; str += ':';
str += option; str += ':';
str += String(selection);
send_message(str, optionHD, 0x0E);
} // Return -> RAD:0:1(index) , RAD:id:option index
24. Dialog Message: Checkbox
// option = "A,B,C"
void dialogCheckbox(uint8_t id, String title, String option){
String str = F("CKB:");
str += String(id); str += ':';
str += title; str += ':';
str += option;
send_message(str, optionHD, 0x0E);
} // Return -> CKB:0:1,3(index) , CKB:id:option index
25. Dialog Message: Select button
// option = "A,B"
void dialogChoose(uint8_t id, String message, String option){
String str = F("CHD:");
str = String(id); str += ':';
str += message; str += ':';
str += option;
send_message(str, optionHD, 0x0E);
} // Return -> CHD:0:B , CHD:id:option str
26. Dialog Message: List Window
// option = "A,B,C,D,E,F,G,H"
void dialogList(uint8_t id, String title, String option){
String str = F("LSD:");
str += String(id); str += ':';
str += title; str += ':';
str += option;
send_message(str, optionHD, 0x0E);
} // Return -> LSD:0:F , LSD:id:option str
27. Dialog Message: Input text
void dialogInput(uint8_t id, String title){
String str = F("STR:");
str += String(id); str += ':';
str += title;
send_message(str, optionHD, 0x0E);
} // Return -> STR:0:str , STR:id:str
1.ESP32_BLE_JOYSTICK.zip
Main sketch
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool rx_received = false; // flag to excute the function when receiving data from BLE
std::string rxValue; // Set global variables to save the received data from BLE
unsigned long int disconnectTime = 0;
bool needAppCheck = false;
bool appConnection = false;
#include "protocol.h"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
disconnectTime = millis(); needAppCheck = true;
Serial.println(F("connected"));
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
delay(500);
pServer->startAdvertising(); // restart advertising
Serial.println(F("start advertising"));
}
};
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;
}
};
#define btName "ESP32ble"
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
void setup() {
Serial.begin(115200);
BLEDevice::init(btName); //("ESP32");// 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_RX,
BLECharacteristic::PROPERTY_NOTIFY
);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic (
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_WRITE
);
pRxCharacteristic->setCallbacks(new MyCallbacks());
pService->start(); // Start the service
pServer->getAdvertising()->start(); // Start advertising
Serial.println(F("Waiting a client connection to notify..."));
}
void pin_control(uint8_t value) {
if (value != 0) {
switch (value) {
case 11: Serial.println(F("button 1 : on"));
break;
case 10: Serial.println(F("button 1 : off"));
break;
case 21: Serial.println(F("button 2 : on"));
break;
case 20: Serial.println(F("button 2 : off"));
break;
case 31: Serial.println(F("button 3 : on"));
break;
case 30: Serial.println(F("button 3 : off"));
break;
case 41: Serial.println(F("button 4 : on"));
break;
case 40: Serial.println(F("button 4 : off"));
break;
case 51: Serial.println(F("button 5 : on"));
break;
case 50: Serial.println(F("button 5 : off"));
break;
case 61: Serial.println(F("button 6 : on"));
break;
case 60: Serial.println(F("button 6 : off"));
break;
case 71: Serial.println(F("button 7 : on"));
break;
case 70: Serial.println(F("button 7 : off"));
break;
case 81: Serial.println(F("button 8 : on"));
break;
case 80: Serial.println(F("button 8 : off"));
break;
case 91: Serial.println(F("button 9 : on"));
break;
case 90: Serial.println(F("button 9 : off"));
break;
case 101: Serial.println(F("button 10 : on"));
//uint8_t id, String title, String option
dialogList(0, F("MENU"), F("select,tomato,banana,mango"));
break;
case 100: Serial.println(F("button 10 : off"));
break;
case 111: Serial.println(F("button 11 : on"));
break;
case 110: Serial.println(F("button 11 : off"));
break;
case 121: Serial.println(F("button 12 : on"));
break;
case 120: Serial.println(F("button 12 : off"));
break;
case 131: Serial.println(F("button 13 : on"));
break;
case 130: Serial.println(F("button 13 : off"));
break;
case 141: Serial.println(F("button 14 : on"));
break;
case 140: Serial.println(F("button 14 : off"));
break;
case 151: Serial.println(F("button 15 : on"));
break;
case 150: Serial.println(F("button 15 : off"));
break;
case 161: Serial.println(F("button 16 : on"));
break;
case 160: Serial.println(F("button 16 : off"));
break;
case 171: Serial.println(F("button 17 : on"));
break;
case 170: Serial.println(F("button 17 : off"));
break;
case 181: Serial.println(F("button 18 : on"));
break;
case 180: Serial.println(F("button 18 : off"));
break;
case 191: Serial.println(F("button 19 : on"));
break;
case 190: Serial.println(F("button 19 : off"));
break;
case 201: Serial.println(F("button 20 : on"));
break;
case 200: Serial.println(F("button 20 : off"));
break;
case 210: Serial.println(F("button 21 : off"));
break;
case 220: Serial.println(F("button 22 : off"));
break;
}
}
}
// headingVal = false;
// Top
// 6 7 0
// \ | /
// Left 5 -- -- 1 Right
// / | \
// 4 3 2
// Bottom
bool headingVal = true;
void joystick_control(uint8_t id, uint16_t heading, uint8_t strength) {
//sendJoyEcho(id, heading, strength);
if (id == 0) Serial.print(F("L: "));
else if (id == 1) Serial.print(F("R: "));
else Serial.print(F("C: "));
if (headingVal) {
Serial.print(heading); Serial.print('/'); Serial.println(strength);
} else {
if (heading <= 23) heading = 359;
int8_t stickDir = (heading-23)/45;
Serial.println(stickDir);
}
}
bool gotEnd = false;
String message = "";
void BLE_read() {
if (rx_received == true) {
if (appConnection) {
String temp = "";
if (rxValue[0] == joyHD) { // 0xF3
if (rxValue[5] == footHD) { // verification of Received value
uint16_t value = rxValue[3];
value = value << 8 | rxValue[2];
joystick_control(rxValue[1], value, rxValue[4]); // execute the code with values
}
} else if (rxValue[0] == pinHD) { // 0xF0
uint8_t cn = 0;
while (rxValue[cn] == pinHD) { // need depend on left arrow pad mode
if (rxValue[cn+2] == footHD) { // verification of Received value
pin_control(rxValue[cn+1]); // execute the code with values
}
cn = cn+3;
}
} else {
String hd = connectHDstr;
if (rxValue[0] == hd[0] && rxValue[1] == hd[1] && rxValue[2] == hd[2]) {
for (int i = hd.length(); i < rxValue.length(); i++) {
if (rxValue[i] != '\n') message += rxValue[i];
else { gotEnd = true; break; }
}
if (gotEnd) {
if (rxValue[3] == hd[3]) CheckF8(message);
else CheckF9(message);
gotEnd = false; message = "";
}
} else {
for (int i = 0; i < rxValue.length(); i++) Serial.print(rxValue[i]);
}
}
} else CheckF8("");
rx_received = false;
}
}
void loop() {
BLE_read();
orderDisconnect();
if(Serial.available()) { // if there is data in the serial buffer
String temp = Serial.readStringUntil('\n'); // option of serial monitor: NEW LINE
Serial.println(temp);
if(temp == "1"){
} else if(temp == "2"){
sendJoyState(0, 45, 10);
} else if(temp == "3"){
sendJoyEcho(1, 45, 10);
} else if(temp == "4"){
setActiveJoystick(true);
} else if(temp == "5"){
setActiveJoystick(false);
}
}
}
Protocol sketch
//////////////////////////////////////////////////////////// COMMON
///////////////////////////////////////// ESSENTIAL
#define pinHD 0xF0
#define joyHD 0xF3
#define strHD 0xF4
#define footHD 0xF1
#define echoHD 0xE1
#define connectHD 0xF8
#define connectHDstr F("%%F8")
#define optionHD 0xF9
uint8_t pin[] = {pinHD, 0, 0}; //F0, pin state, F1
void sendPinState(uint8_t pin_val){
pin[1] = pin_val;
pin[2] = footHD;
pTxCharacteristic->setValue((uint8_t*)&pin, 3);
pTxCharacteristic->notify();
}
void sendPinEcho(uint8_t pin_val){
pin[1] = pin_val;
pin[2] = echoHD;
pTxCharacteristic->setValue((uint8_t*)&pin, 3);
pTxCharacteristic->notify();
}
uint8_t joystick[] = {joyHD, 0, 0, 0, 0, 0}; //F3, slide number, 16bit value, F1
// id 0:Left Joystick, 1:Right Joystick 2:Gravity Sensor(change only text)
void sendJoyState(uint8_t id, uint16_t heading, uint8_t strength) {
joystick[1] = id;
joystick[2] = heading;
joystick[3] = heading >> 8;
joystick[4] = strength;
joystick[5] = footHD;
pTxCharacteristic->setValue((uint8_t*)&joystick, 6);
pTxCharacteristic->notify();
}
void sendJoyEcho(uint8_t id, uint16_t heading, uint8_t strength) {
joystick[1] = id;
joystick[2] = heading;
joystick[3] = heading >> 8;
joystick[4] = strength;
joystick[5] = echoHD;
pTxCharacteristic->setValue((uint8_t*)&joystick, 6);
pTxCharacteristic->notify();
}
void sendText(String str) {
char temp [21]; // 18
uint16_t len = str.length();
uint8_t repeat = 1;
uint8_t endLen = 0;
if (len > 18) {
repeat = (len/18)+1;
endLen = (len%18);
} else endLen = len;
for (int r = 0; r < repeat; r++) {
temp[0] = strHD;
uint8_t ed = 0;
if (repeat-r > 1) ed = 18;
else ed = endLen;
for (int i = 1; i < ed+1; i++) {
temp[i] = str[(r*18)+i-1];
}
if (repeat-r > 1) temp[ed+1] = echoHD;
else temp[ed+1] = footHD;
temp[ed+2] = '\0';
pTxCharacteristic->setValue(temp);
pTxCharacteristic->notify();
}
}
///////////////////////////////////////// CONNECTION
void sendDisconnect(){ // order disconnect to App
uint8_t setVal[3];
setVal[0] = connectHD;
setVal[1] = 0x01;
setVal[2] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal,3);
pTxCharacteristic->notify();
}
//#define passName F("PAS:")
//#define pnLength 4
//
//void requestPass(bool val){ // order disconnect to App
// uint8_t setVal[3+pnLength];
// String temp = passName;
// setVal[0] = connectHD;
// setVal[1] = 0x02;
// if (val) {
// setVal[2] = pnLength;
// for (int i = 0; i<pnLength;i++) setVal[i+3] = temp[i];
// pTxCharacteristic->setValue((uint8_t*)&setVal,3+pnLength);
// } else {
// setVal[2] = val;
// pTxCharacteristic->setValue((uint8_t*)&setVal,3);
// }
// pTxCharacteristic->notify();
//}
//
//void responsePass(bool val){
// uint8_t setVal[3];
// setVal[0] = connectHD;
// setVal[1] = 0x03;
// setVal[2] = val;
// pTxCharacteristic->setValue((uint8_t*)&setVal,3);
// pTxCharacteristic->notify();
//}
void send_message(String str, uint8_t head, uint8_t fn) {
char temp [21]; // 18
uint16_t len = str.length();
uint8_t repeat = 1;
uint8_t endLen = 0;
if (len > 17) {
repeat = (len/17)+1;
endLen = (len%17);
} else endLen = len;
for (int r = 0; r < repeat; r++) {
temp[0] = head;
temp[1] = fn;
uint8_t ed = 0;
if (repeat-r > 1) ed = 17;
else ed = endLen;
for (int i = 2; i < ed+2; i++) {
temp[i] = str[(r*17)+i-2];
}
if (repeat-r > 1) temp[ed+2] = echoHD;
else temp[ed+2] = footHD;
temp[ed+3] = '\0';
pTxCharacteristic->setValue(temp);
pTxCharacteristic->notify();
}
}
void sendMessage(String str) {
send_message(str, connectHD, 0x04);
}
///////////////////////////////////////// OPTION
///////////////////////// SET FUNCION
void setActiveJoystick(bool val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x01;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
// SEND SLIDE GET VALUE INTERVAL
void setJoystickInterval(uint16_t val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x02;
setVal[2] = val;
setVal[3] = val >> 8;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
void setJoyZeroRange(uint8_t val){
if (val > 35) val = 35;
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x03;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
void setJoyStrengthAdjust(bool val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x04;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
// 1: independent 2: group1(up-down) / group2 (left-right) 4: group4(only 1)
void setLeftPadMode(uint8_t val){
if (val == 0 || val > 2) val = 4;
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x05;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
// 0: Left & Right False 1: Left 4 Way 2: Right 4Way 3: Both 4 Way
void setJoyWay4(uint8_t val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x06;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
// 0: none 1: group1 2: group1&2
void setDisplayAddButton(uint8_t val){
if (val > 2) val = 2;
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x07;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
// 0: not use, 1: show only name, 2: show name & pass, defalult: 2
void setSSIDLabelMode(uint8_t val) {
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x08;
setVal[2] = val;
setVal[3] = 0x00;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
// defalt interval : 180 millis, 180, 180 * 6 = 1080
void useGravityAndInterval(uint16_t val){
uint8_t setVal[4];
setVal[0] = optionHD;
setVal[1] = 0x09;
setVal[2] = val;
setVal[3] = val >> 8;
pTxCharacteristic->setValue((uint8_t*)&setVal, 4);
pTxCharacteristic->notify();
}
//F("BT1,BT2,BT3,BT4,BT5,BT6")
void setAddButtonLabel(String str){
send_message(str, optionHD, 0x0A);
}
//F("0000") // left pad: up-left-right-down
void setLeftPadOrAllToggle(String str){
send_message(str, optionHD, 0x0B);
}
// F("0000") // right pad: top-left-right-bottom
void setRightPadToggle(String str){
send_message(str, optionHD, 0x0C);
}
// F("000000000000") // center: view-menu-bt1-bt2-bt3-bt4-bt5-bt6-LB-LT-RB-RT
void setButtonToggle(String str){ // must after setLeftPadMode(uint8_t val)
send_message(str, optionHD, 0x0D);
}
///////////////////////// PERFORM DIALOG
// option = "A,B,C" -> index = 1(A) ~ 3(C)
void dialogRadio(uint8_t id, String title, String option, uint8_t selection){
String str = F("RAD:");
str += String(id); str += ':';
str += title; str += ':';
str += option; str += ':';
str += String(selection);
send_message(str, optionHD, 0x0E);
} // Return -> RAD:0:1(index) , RAD:id:option index
// option = "A,B,C"
void dialogCheckbox(uint8_t id, String title, String option){
String str = F("CKB:");
str += String(id); str += ':';
str += title; str += ':';
str += option;
send_message(str, optionHD, 0x0E);
} // Return -> CKB:0:1,3(index) , CKB:id:option index
// option = "A,B"
void dialogChoose(uint8_t id, String message, String option){
String str = F("CHD:");
str = String(id); str += ':';
str += message; str += ':';
str += option;
send_message(str, optionHD, 0x0E);
} // Return -> CHD:0:B , CHD:id:option str
// option = "A,B,C,D,E,F,G,H"
void dialogList(uint8_t id, String title, String option){
String str = F("LSD:");
str += String(id); str += ':';
str += title; str += ':';
str += option;
send_message(str, optionHD, 0x0E);
} // Return -> LSD:0:F , LSD:id:option str
void dialogInput(uint8_t id, String title){
String str = F("STR:");
str += String(id); str += ':';
str += title;
send_message(str, optionHD, 0x0E);
} // Return -> STR:0:str , STR:id:str
//////////////////////////////////////////////////////////// INDIVIDUAL
void iniSet() {
setAddButtonLabel(F("BT1,BT2,BT3,BT4,BT5,BT6"));
setDisplayAddButton(2); // 0: none 1: group1 2: group1&2
setLeftPadMode(1); // 1: independent 2: group1(up-down) / group2 (left-right) 4: group4(only 1)
setLeftPadOrAllToggle(F("00000000001111110000"));
setJoyWay4(false);
setActiveJoystick(true); setJoystickInterval(100);
setJoyStrengthAdjust(true);
setSSIDLabelMode(0); // btName Label long click function disabled
//useGravityAndInterval(180);
}
void btDisconnect() {
sendDisconnect();
delay(10);
pServer->disconnect(pServer->getConnId());
}
void orderDisconnect() {
if (needAppCheck) {
if (!appConnection) {
if (millis() - disconnectTime > 3000) {
needAppCheck = false;
btDisconnect();
Serial.println(F("enter app check disconnect"));
}
}
}
}
void CheckF8(String temp) {
if (temp == "") {
String hd = connectHDstr;
if (rxValue[0] == hd[0] && rxValue[1] == hd[1] && rxValue[2] == hd[2] && rxValue[3] == hd[3]) {
for (int i = hd.length(); i < rxValue.length(); i++) {
if (rxValue[i] != '\n') temp += rxValue[i];
else break;
}
if (temp == F("connect")) { delay(100); needAppCheck = false; appConnection = true; iniSet(); }
} else { btDisconnect(); needAppCheck = false; }
} else {
if (temp == F("disconnect")) appConnection = false;
else if (temp.startsWith(F("NA"))) { sendMessage(F("This funtion is not available.")); }
else Serial.println(temp);
}
}
void CheckF9(String temp) {
Serial.println(temp);
if (temp.startsWith(F("RAD"))) { }
else if (temp.startsWith(F("CKB"))) { }
else if (temp.startsWith(F("CHD"))) { }
else if (temp.startsWith(F("LSD"))) { }
else if (temp.startsWith(F("STR"))) { }
}
Example of using Protocol to set ADUPAD app
- ADUPAD app settings: label/toggle properties, use of the timer for checkHD(0xF5), Slide Mode settings, etc.
void iniSet() {
setAddButtonLabel(F("BT1,BT2,BT3,BT4,BT5,BT6"));
setDisplayAddButton(2); // 0: none 1: group1 2: group1&2
setLeftPadMode(1); // 1: independent 2: group1(up-down) / group2 (left-right) 4: group4(only 1)
setLeftPadOrAllToggle(F("00000000001111110000"));
setJoyWay4(false);
setActiveJoystick(true); setJoystickInterval(100); // Active Joystick Mode
setJoyStrengthAdjust(true); // value of Joystick adjusted into 0 ~ 10
setSSIDLabelMode(0); // not use change the name for blutooth of ESP32
//useGravityAndInterval(180); // use or not the Gravity sensor with the interval
}
- Example of code processing for each function of a button that changes according to the leftPadMode and menu-related button.
void pin_control(uint8_t value) {
if (value != 0) {
switch (value) {
case 11: if (leftPadMode > 1) Serial.println(F("Up DIR"));
else Serial.println(F("button 1 : on"));
break;
case 10: if (leftPadMode > 1) Serial.println(F("Up off"));
else Serial.println(F("button 1 : off"));
break;
case 21: if (leftPadMode > 1) Serial.println(F("Left DIR"));
else Serial.println(F("button 2 : on"));
break;
case 20: if (leftPadMode > 1) Serial.println(F("Left off"));
else Serial.println(F("button 2 : off"));
break;
case 31: if (leftPadMode > 1) Serial.println(F("Right DIR"));
else Serial.println(F("button 3 : on"));
break;
case 30: if (leftPadMode > 1) Serial.println(F("Right off"));
else Serial.println(F("button 3 : off"));
break;
case 41: if (leftPadMode > 1) Serial.println(F("Down DIR"));
else Serial.println(F("button 4 : on"));
break;
case 40: if (leftPadMode > 1) Serial.println(F("Down off"));
else Serial.println(F("button 4 : off"));
break;
case 91: Serial.println(F("button 9 : on"));
//uint8_t id, String title, String option
dialogList(0, F("LEFT PAD"), F("SELECT,TOGGLE,PUSH")); // dialog message
break;
case 90: Serial.println(F("button 9 : off"));
break;
case 101: Serial.println("button 10 : on");
//uint8_t id, String title, String option
dialogList(2, F("RIGHT PAD"), F("SELECT,TOGGLE,PUSH")); // dialog message
break;
}
}
}
- Example of modifying the function "CheckF9()" that handles dialog messages.
void CheckF9(String temp) {
Serial.println(temp);
if (temp.startsWith(F("RAD"))) { } // radio button
else if (temp.startsWith(F("CKB"))) { } // Checkbox
else if (temp.startsWith(F("CHD"))) { } // Select button
else if (temp.startsWith(F("LSD"))) { // List Window
int ed = temp.indexOf(':');
temp.remove(0, ed+1);
ed = temp.indexOf(':');
String ID = temp.substring(0, ed);
uint8_t id = ID.toInt();
temp.remove(0, ed+1);
if (id == 0) {
if (temp.startsWith(F("PUS"))) { leftPadMode = 0; setLeftPadOrAllToggle(F("0000")); }
else if (temp.startsWith(F("TOG"))) {
setLeftPadOrAllToggle(F("1111")); // set toggle for left button
dialogList(1, F("LEFT PAD MODE AT TOGGLE"), F("SELECT,INDIVIDUAL,TWO GROUP,ONLY ONE")); // dialog message
}
} else if (id == 1) {
if (temp.startsWith(F("IND"))) leftPadMode = 1; // independant
else if (temp.startsWith(F("TWO"))) leftPadMode = 2; // 2 group: up/down, left/right
else if (temp.startsWith(F("ONL"))) leftPadMode = 4; // 4psc button is 1 group
setLeftPadMode(leftPadMode);
} else if (id == 2) {
if (temp.startsWith(F("PUS"))) { rightPadMode = 0; setRightPadToggle(F("0000")); } // set push for right button
else { rightPadMode = 1; setRightPadToggle(F("1111")); }
}
}
else if (temp.startsWith(F("STR"))) { } // Input text
}
1-1.ESP32_BLE_JOYSTICK_MENU.zip
Change name for the bluetooth of ESP32 on ADUPAD App
- Define array variable to save the name for the bluetooth of ESP32.
#include "SPIFFS.h" // ESP32 SPIFSPIFFS 라이브러리
char btName[11] = "ESP32"; // 10 char
- SPIFFS Setting
bool saveConfig() { // Write NAME
String value = btName;
Serial.println(value);
File configFile = SPIFFS.open(F("/config.txt"), "w");
if (!configFile) {
Serial.println(F("Failed to open config file for writing"));
return false;
} else {
configFile.println(value); // save at config.txt including '\n'
configFile.close();
return true;
}
}
bool loadConfig() { // Read NAME
File configFile = SPIFFS.open(F("/config.txt"), "r");
if (!configFile) {
Serial.println(F("Failed to open config file"));
return false;
} else {
String temp = configFile.readStringUntil('\r');
for (int i = 0; i < temp.length(); i++) btName[i] = temp[i];
btName[temp.length()] = '\0';
configFile.close();
Serial.println(btName);
return true;
}
}
void setup() {
if (SPIFFS.exists(F("/config.txt"))) loadConfig(); // if there is "/config.txt", read
else saveConfig(); // if there is not, save a new config.txt
}
// SPIFFS initialization code at serial monitor
void loop() {
if(Serial.available()) { // Serial monitor
String temp = Serial.readStringUntil('\n');
Serial.println(temp);
if(temp == "1"){
esp_bt_controller_disable();
Serial.println(F("format..."));
SPIFFS.format();
Serial.println(F("done. can start upload."));
while(1); // stanby for uploading
}
}
}
- Save parsing data for name and remove the data for password and send a message applyed in Arduino.
void changeBTname(String str) {
bool save = false;
String temp = str.substring(0, 1);
bool delPass = temp.toInt(); // if true, there is data for password which is not used in this sketch
str.remove(0, 1);
if (delPass) str.remove(0, 6); // remove pass
if (str != btName) {
if (temp != "") {
for (int i = 0; i < str.length(); i++) btName[i] = str[i];
btName[str.length()] = '\0';
save = true;
}
}
if (save) {
saveConfig();
temp = F("<p>NAME: ");
temp += btName;
temp += F("</p>Value's Saved.");
if (delPass) temp += F("<br/>Function of Set pass is not provided.<br/>");
temp += F(" The module will reboot.<br/>try to reconnect.");
sendMessage(temp);
sendDisconnect(); delay(500); ESP.restart();
} else {
temp = F("There's no change.");
if (delPass) temp += F("<br/>Function of Set pass is not provided.");
sendMessage(temp);
}
}
2.ESP32_BLE_JOYSTICK_NAME.zip
SPIFFS_FORMAT_ESP32.zip
App Login
Obtain control authority using the password for BLE of ESP32.
- Define array variable to save the name for the bluetooth of ESP32 and the set password for login.
char btName[11] = "ESP32"; // 10 char
char pass[7] = "123456";
- Password handling code: response message, input prohibition setting, etc.
bool waitPass = false;
uint8_t passAllow = 5;
#define passWaitTime 120000
#define banResetTime 600000
void banReset() { // if wrong password is entered 5 times, banned inputting for banResetTime
if (millis() - disconnectTime > banResetTime) passAllow = 5;
}
void orderDisconnect() {
if (!appConnection) {
if (millis() - disconnectTime > 3000) { // If "%%F8connect" is not confirmed within disconnectTime after receiving the first data through BLE
needAppCheck = false;
btDisconnect();
Serial.println(F("enter app check disconnect"));
}
} else if (waitPass) { // if there's no input a pass within passWaitTime, terminate the connection
if (millis() - disconnectTime > passWaitTime) {
Serial.println("time over");
needAppCheck = false; waitPass = false; appConnection = false;
sendMessage(F("It's disconnected by time expires.<br/>Try to reconnect."));
sendDisconnect();
}
}
}
void CheckF8(String temp) {
if (temp == "") {
String hd = connectHDstr;
if (rxValue[0] == hd[0] && rxValue[1] == hd[1] && rxValue[2] == hd[2] && rxValue[3] == hd[3]) {
for (int i = hd.length(); i < rxValue.length(); i++) {
if (rxValue[i] != '\n') temp += rxValue[i];
else break;
}
if (waitPass) {
if (temp.startsWith(passName)) {
temp.remove(0, pnLength); bool ok = true;
if (temp.length() == 6) {
for (int i = 0; i < 6; i++) { if (temp[i] != pass[i]) { ok = false; break; } }
}
if (ok) { passOK = true; waitPass = false; needAppCheck = false; iniSet(); send_State(); }
else { passAllow--; disconnectTime = millis(); } // wrong password & timer set
if (!passAllow) { // if wrong password is entered 5 times
waitPass = false; appConnection = false;
sendMessage(F("You've got wrong password 5 times.<br/>Try to reconnect 10 Min later."));
sendDisconnect();
} else responsePass(ok);
} else if (temp == F("connect")) { requestPass(usePass); disconnectTime = millis(); } // request pass & timer set
else if (temp == F("disconnect")) { waitPass = false; needAppCheck = false; appConnection = false; }
} else {
if (temp == F("connect") || temp == F("autoCon")) {
needAppCheck = false; appConnection = true;
if (usePass) {
delay(100); requestPass(usePass);
waitPass = true; needAppCheck = true; disconnectTime = millis();
} else { iniSet(); send_State(); }
}
}
} else { btDisconnect(); needAppCheck = false; }
} else {
if (temp.startsWith(F("autoCon"))) { needAppCheck = false; }
else if (temp == F("disconnect")) { appConnection = false; passOK = false; }
else if (temp.startsWith(F("NA"))) { temp.remove(0, 2); changeBTname(temp); }
else Serial.println(temp);
}
}
3.ESP32_BLE_JOYSTICK_PASS.zip
[Arduino/ADUPAD] - ADUPAD - Arduino wireless remote control PAD application
'Arduino > ADUPAD' 카테고리의 다른 글
ESP32 Bluetooth remote control with ADUPAD (0) | 2022.12.04 |
---|---|
ESP32/NodeMcu WiFi remote control with ADUPAD (0) | 2022.12.03 |
Arduino BLE remote control with ADUPAD and BT05 (0) | 2022.11.25 |
Arduino Bluetooth remote control with ADUPAD and HC-06 (0) | 2022.11.24 |
Arduino WiFi remote control with ADUPAD and ESP-01 (0) | 2022.11.21 |
ADUPAD - Arduino wireless remote control PAD application (0) | 2022.10.01 |