Минимизируйте размер программы ESP32, подключающейся к Bluetooth-устройству и запрашивающей HTTP-сервер одновременно.
Я пытаюсь расширить это программа для платы ESP32, которая подключается к кубику Рубика по Bluetooth, чтобы теперь отправлять POST-запросы на сервер с данными кубика.
Я добавил
//**Makes http Server Post with Data**
HTTPClient http;
http.begin(F("http://mydomain/RubiksCube"));
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int httpCode = http.POST("data=" + data);
http.end();
в notifyCallback() для отправки сообщения при получении данных Bluetooth, и
WiFi.begin("[ssid]", "[password]");
в настройке()
Я столкнулся с проблемой, что мой измененный скетч использует 1522194 байта (116%) места для хранения программы. Максимум 1310720 байт. Есть ли способ оптимизировать программу, чтобы она подошла к используемой мной плате?
Исходный скетч использует 972 334 байта (74%) места для хранения программы. Я могу уменьшить свой скетч до 95%, удалив
WiFi.begin("[ssid]", "[password]");
Я подозреваю, что есть способ сделать это (или что-то другое) в реализации более низкого уровня или, по крайней мере, удалить ненужные функции библиотеки. Есть ли какой-нибудь более упрощенный подход к этой задаче?
Вот весь модифицированный скетч
/**
* ESP32-SmartCube
* Copyright (c) 2019 Playful Technology
*
* ESP32 sketch to connect to Xiaomi "Smart Magic Cube" Rubik's Cube via Bluetooth LE
* and decode notification messages containing puzzle state.
* Prints output to serial connection of the last move made, and when the cube is fully
* solved, triggers a relay output
*/
// ВКЛЮЧАЕТ В СЕБЯ
// библиотека ESP32 для Bluetooth LE
#include "BLEDevice.h"
#include <WiFi.h>
#include <HTTPClient.h>
// КОНСТАНТЫ
// MAC-адрес кубика Рубика
// Это можно обнаружить, запустив сканирование ДО касания куба. Затем поверните любое лицо
// разбудить куб и посмотреть, какое новое устройство появится
static BLEAddress *pServerAddress = new BLEAddress("c4:a7:2f:2a:69:a3");
// Удаленный сервис, к которому мы хотим подключиться
static BLEUUID serviceUUID("0000aadb-0000-1000-8000-00805f9b34fb");
// Характеристики удаленного сервиса, который мы хотим отслеживать
static BLEUUID charUUID("0000aadc-0000-1000-8000-00805f9b34fb");
// Следующие константы используются для расшифровки данных, представляющих состояние куба
// см. https://github.com/cs0x7f/cstimer/blob/master/src/js/bluetooth.js
const uint8_t decryptionKey[] = {176, 81, 104, 224, 86, 137, 237, 119, 38, 26, 193, 161, 210, 126, 150, 81, 93, 13, 236, 249, 89, 235, 88, 24, 113, 81, 214, 131, 130, 199, 2, 169, 39, 165, 171, 41};
// На этом выводе будет отправлен ВЫСОКИЙ импульс, когда куб будет решен
const byte relayPin = 33;
// Это массив данных, представляющий решенный куб
const byte solution[16] = {0x12,0x34,0x56,0x78,0x33,0x33,0x33,0x33,0x12,0x34,0x56,0x78,0x9a,0xbc,0x00,0x00};
// ГЛОБАЛЬНЫЕ
// Нашли ли мы куб с правильным MAC-адресом для подключения?
static boolean deviceFound = false;
// Подключены ли мы сейчас к кубу?
static boolean connected = false;
// Свойства устройства, найденного при сканировании
static BLEAdvertisedDevice* myDevice;
// BT характеристика подключенного устройства
static BLERemoteCharacteristic* pRemoteCharacteristic;
// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
/**
* Return the ith bit from an integer array
*/
int getBit(uint8_t* val, int i) {
int n = ((i / 8) | 0);
int shift = 7 - (i % 8);
return (val[n] >> shift) & 1;
}
/**
* Return the ith nibble (half-byte, i.e. 16 possible values)
*/
uint8_t getNibble(uint8_t val[], int i) {
if(i % 2 == 1) {
return val[(i/2)|0] % 16;
}
return 0|(val[(i/2)|0] / 16);
}
// ВЫЗОВЫ
/**
* Callbacks for devices found via a Bluetooth scan of advertised devices
*/
class AdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
// Обратный вызов onResult вызывается для каждого рекламируемого устройства, найденного при сканировании
void onResult(BLEAdvertisedDevice advertisedDevice) {
// Выводим MAC-адрес этого устройства
Serial.print(" - ");
Serial.print(advertisedDevice.getAddress().toString().c_str());
// Соответствует ли это устройство MAC-адресу, который мы ищем?
if(advertisedDevice.getAddress().equals(*pServerAddress)) {
// Остановить поиск других устройств
advertisedDevice.getScan()->stop();
// Создаем новое устройство на основе свойств рекламируемого устройства
myDevice = new BLEAdvertisedDevice(advertisedDevice);
// Установить флаг
deviceFound = true;
Serial.println(F(" - Connecting!"));
}
else {
Serial.println(F("... MAC address does not match"));
}
}
};
/**
* Callbacks for device we connect to
*/
class ClientCallbacks : public BLEClientCallbacks {
// Вызывается при установке нового соединения
void onConnect(BLEClient* pclient) {
// цифровая запись (LED_BUILTIN, HIGH);
connected = true;
}
// Вызывается при потере соединения
void onDisconnect(BLEClient* pclient) {
// цифровая запись (LED_BUILTIN, LOW);
connected = false;
}
};
/**
* Called whenever a notication is received that the tracked BLE characterisic has changed
*/
static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
// РАСШИФРОВАТЬ ДАННЫЕ
// Ранние кубы Bluetooth использовали незашифрованный формат данных, который отправлял индексы угла/ребра как
// простая необработанная строка, например, pData для решенного куба будет 12345678333333333123456789abc000041414141
// Однако более новые кубы, например Giiker i3, шифруют данные с помощью циклического ключа, так что это же состояние может быть
// 706f6936b1edd1b5e00264d099a4e8a19d3ea7f1 затем d9f67772c3e9a5ea6e84447abb527156f9dca705 и т.д.
// Чтобы узнать, зашифрованы ли данные, мы сначала читаем предпоследний байт характеристических данных.
// Как и в двух вышеприведенных примерах, если это 0xA7, мы знаем, что он зашифрован
bool isEncrypted = (pData[18] == 0xA7);
// Если он *зашифрован*...
if(isEncrypted) {
// Разделить последний байт на два 4-битных значения
int offset1 = getNibble(pData, 38);
int offset2 = getNibble(pData, 39);
// Получить пару значений смещения из ключа дешифрования
for (int i=0; i<20; i++) {
// Применяем смещение к каждому значению в данных
pData[i] += (decryptionKey[offset1 + i] + decryptionKey[offset2 + i]);
}
}
// Первые 16 байт представляют состояние куба — 8 углов (с 3 ориентациями) и 12 ребер (можно перевернуть)
String postData = "";
Serial.print("Current State: ");
for (int i=0; i<16; i++) {
// Serial.print(pData[i], HEX);
Serial.print(pData[i]);
Serial.print(" ");
postData.concat(pData[i]);
}
Serial.println("");
//**Создает сообщение HTTP-сервера с данными**
HTTPClient http;
http.begin(F("http://mydomain/RubiksCube"));
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int httpCode = http.POST("data=" + postData);
http.end();
// Байт 17 представляет последний сделанный поворот - первая половина байта - это лицо, а вторая половина байта - направление вращения
int lastMoveFace = getNibble(pData, 32);
int lastMoveDirection = getNibble(pData, 33);
char* faceNames[6] = {"Front", "Bottom", "Right", "Top", "Left", "Back"};
Serial.print("Last Move: ");
Serial.print(faceNames[lastMoveFace-1]);
Serial.print(lastMoveDirection == 1 ? " Face Clockwise" : " Face Anti-Clockwise" );
Serial.println("");
Serial.println("----");
if(memcmp(pData, solution, 16) == 0) {
Serial.println("Solved!");
digitalWrite(relayPin, HIGH);
delay(100);
digitalWrite(relayPin, LOW);
}
}
/*
* Connect to the BLE server of the correct MAC address
*/
bool connectToServer() {
Serial.print(F("Creating BLE client... "));
BLEClient* pClient = BLEDevice::createClient();
delay(500);
Serial.println(F("Done."));
Serial.print(F("Assigning callbacks... "));
pClient->setClientCallbacks(new ClientCallbacks());
delay(500);
Serial.println(F(" - Done."));
// Подключиться к удаляемому BLE-серверу.
Serial.print(F("Connecting to "));
Serial.print(myDevice->getAddress().toString().c_str());
Serial.print(F("... "));
pClient->connect(myDevice);
delay(500);
Serial.println(" - Done.");
// Получаем ссылку на нужный нам сервис на удаленном BLE-сервере.
Serial.print(F("Finding service "));
Serial.print(serviceUUID.toString().c_str());
Serial.print(F("... "));
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
delay(500);
if (pRemoteService == nullptr) {
Serial.println(F("FAILED."));
return false;
}
Serial.println(" - Done.");
delay(500);
// Получить ссылку на характеристику в сервисе удаленного BLE-сервера.
Serial.print(F("Finding characteristic "));
Serial.print(charUUID.toString().c_str());
Serial.print(F("... "));
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
Serial.println(F("FAILED."));
return false;
}
Serial.println(" - Done.");
delay(500);
Serial.print(F("Registering for notifications... "));
if(pRemoteCharacteristic->canNotify()) {
pRemoteCharacteristic->registerForNotify(notifyCallback);
Serial.println(" - Done.");
}
else {
Serial.println(F("FAILED."));
return false;
}
Serial.println("READY!");
}
/**
* Search for any advertised devices
*/
void scanForDevices(){
Serial.println("Scanning for Bluetooth devices...");
// Получить сканер и установить обратный вызов, который мы хотим использовать, чтобы получать информацию, когда мы
// обнаружено новое устройство. Указываем, что хотим активное сканирование и запускаем
// сканирование для запуска в течение 30 секунд.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->start(30);
}
// Начальная настройка
void setup() {
// Запускаем последовательное соединение, чтобы иметь возможность отслеживать данные отладки
Serial.begin(115200);
Serial.print("Initialising BLE...");
BLEDevice::init("");
delay(500);
Serial.println(F("Done."));
WiFi.begin("[ssid]", "[password]");
// relayPin будет установлен ВЫСОКИМ, когда куб будет собран
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW);
// ledPin будет установлен в HIGH при подключении куба
// pinMode(LED_BUILTIN, OUTPUT);
Serial.println("Connected");
}
// Основная функция цикла программы
void loop() {
// Если куб найден, подключаемся к нему
if (deviceFound) {
if(!connected) {
connectToServer();
}
}
else {
scanForDevices();
}
// Добавим небольшую задержку
delay(1000);
}
@Jack-Penn, 👍0
Обсуждение0
- HTTP POST от Arduino/ESP8266/ESP32 Как отправлять параметры (x-www-form-urlencoded)
- esp32 http client response только 200 не получил данные после этого
- Можно ли использовать WiFi и Bluetooth/BLE в одном проекте ESP32?
- ESP32 SerialBluetooth отключается в течение секунды после подключения, но не BLEDevice/BLEServer
- Проблема с ESP32 bluetooth
- Контрольный таймер задачи запускается во время инициализации Bluetooth
- ESP32 в Arduino-IDE с FS.h и SPIFFS
- Программаторы для этой платы отсутствуют - Программирование ESP32 Cam с помощью Ardunio IDE
Сколько флэш-памяти у вашего ESP32? Скорее всего это 4Мб, может хватило бы поменять схему разделов. Какую схему разделов вы выбрали?, @chrisl
Я не уверен, сколько flash у меня есть и как проверить. Я пытался загрузить скетч с размером флэш-памяти 4 МБ и схемой разделов по умолчанию 4 МБ с помощью spiffs., @Jack-Penn
Вы не используете SPIFFS, поэтому выберите схему разделов без него., @chrisl