8.3.3.1. BLE Cellbotics server¶
This is based on Neil Kolban’s example for IDF. It was ported to the Arduino ESP32 by Evandro Copercini, with updates by chegewara, then extended by Bryan A. Jones.
8.3.3.1.1. Includes¶
8.3.3.1.2. Macros¶
8.3.3.1.2.1. UUIDs¶
See here for generating UUIDs.
The UUID for the CellBot service.
Characteristic for resetHardware.
Characteristic for pinMode.
Characteristic for digitalWrite
Characteristic for digitalRead
Characteristic for ledcSetup
Characteristic for ledcAttachPin
Characteristic for ledcDetachPin
Characteristic for ledcWrite
Define this to return a textual description of each function executed to the Bluetooth client, and also print these descriptions to the serial port.
8.3.3.1.2.1.1. Hardware¶
The only blue LED on the ESP32.
The only pushbutton on the ESP32.
Configure a pushbutton and LED to enable BLE pairing.
Configure the on-board pushbutton as the pairing enable button.
Configure the on-board LED controlled by the ESP32 to flash quickly while pairing.
8.3.3.1.3. Variables¶
8.3.3.1.4. Reset¶
Reset the device to a power-on like state in terms of the I/Os affected by the characteristics above. Also used as an e-stop when Bluetooth disconnects.
TODO.
8.3.3.1.5. Characteristic callbacks¶
Provide return values and checks used by all the following characteristics.
A buffer for messages; a C string.
A string holding read data to return to the client.
The value read from a characteristic.
On a read, return buf
.
Get a write value and check its length.
bool checkLength(size_t sz_expected_length, BLECharacteristic* pCharacteristic) {
value = pCharacteristic->getValue();
if (value.length() != sz_expected_length) {
snprintf(buf, sizeof(buf), "Error: message length was %u, but expected %u.\n", value.length(), sz_expected_length);
ret.assign(buf);
return false;
}
The default response is an empty string.
Any write to this characteristic invokes reset_hardware
.
A write to this characteristic invokes pinMode
; results are reported by a read of this characteristic.
class PinModeCallback: public InvokeArduinoCallback {
void onWrite(BLECharacteristic* pCharacteristic) {
/// value[0] value[1]
/// void pinMode(uint8_t pin, uint8_t mode)
uint8_t u8_pin;
uint8_t u8_mode;
if (checkLength(2, pCharacteristic)) {
u8_pin = static_cast<uint8_t>(value[0]);
u8_mode = static_cast<uint8_t>(value[1]);
pinMode(u8_pin, u8_mode);
#ifdef VERBOSE_RETURN
snprintf(buf, sizeof(buf), "pinMode(%u, %u)", u8_pin, u8_mode);
Serial.println(buf);
ret.assign(buf);
#endif
}
}
};
A write to this characteristic invokes digitalWrite
; results are reported by a read of this characteristic.
class DigitalWriteCallback: public InvokeArduinoCallback {
void onWrite(BLECharacteristic* pCharacteristic) {
/// value[0] value[1]
/// void digitalWrite(uint8_t pin, uint8_t val);
uint8_t u8_pin;
uint8_t u8_val;
if (checkLength(2, pCharacteristic)) {
u8_pin = static_cast<uint8_t>(value[0]);
u8_val = static_cast<uint8_t>(value[1]);
digitalWrite(u8_pin, u8_val);
#ifdef VERBOSE_RETURN
snprintf(buf, sizeof(buf), "digitalWrite(%u, %u)", u8_pin, u8_val);
Serial.println(buf);
ret.assign(buf);
#endif
}
};
};
A write to this characteristic invokes digitalWrite
; results are reported by a read of this characteristic.
Although digitialRead
returns an int
, store it in a char
, since we assume it’s a one-bit value.
Same as above, for ledcSetup
.
This wraps:
Extract function parameters.
Call the function.
d_ret = ledcSetup(u8_channel, d_freq, u8_resolution_bits);
ret.resize(8);
ret.assign(reinterpret_cast<char*>(&d_ret), 8);
#ifdef VERBOSE_RETURN
snprintf(buf, sizeof(buf), "%lf = ledcSetup(%u, %lf, %u)", d_ret, u8_channel, d_freq, u8_resolution_bits);
Serial.println(buf);
ret.replace(8, 92, buf);
#endif
}
};
};
Same as above, for ledcAttachPin
.
class ledcAttachPinCallback: public InvokeArduinoCallback {
void onWrite(BLECharacteristic* pCharacteristic) {
/// value[0] value[1]
/// void ledcAttachPin(uint8_t pin, uint8_t channel)
uint8_t u8_pin;
uint8_t u8_channel;
if (checkLength(2, pCharacteristic)) {
u8_pin = static_cast<uint8_t>(value[0]);
u8_channel = static_cast<uint8_t>(value[1]);
ledcAttachPin(u8_pin, u8_channel);
#ifdef VERBOSE_RETURN
snprintf(buf, sizeof(buf), "ledcAttachPin(%u, %u)", u8_pin, u8_channel);
Serial.println(buf);
ret.assign(buf);
#endif
}
};
};
Same as above, for ledcAttachPin
.
class ledcDetachPinCallback: public InvokeArduinoCallback {
void onWrite(BLECharacteristic* pCharacteristic) {
/// value[0]
/// void ledcDetachPin(uint8_t pin)
uint8_t u8_pin;
if (checkLength(2, pCharacteristic)) {
u8_pin = static_cast<uint8_t>(value[0]);
ledcDetachPin(u8_pin);
#ifdef VERBOSE_RETURN
snprintf(buf, sizeof(buf), "ledcDetachPin(%u)", u8_pin);
Serial.println(buf);
ret.assign(buf);
#endif
}
};
};
Same as above, for ledcWrite
.
This wraps:
Extract function parameters.
Since the data isn’t aligned, use memcpy.
Call the function.
On a server disconnect, reset and update paired status.
Now that the board isn’t connected, allow pairing.
8.3.3.1.6. Functions¶
Define the name visible when pairing this device.
Define the primary service for this server.
Define characteristics for this server
//***************************************
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
RESET_HARDWARE_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new ResetHardwareCallback());
pCharacteristic = pService->createCharacteristic(
PIN_MODE_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new PinModeCallback());
pCharacteristic = pService->createCharacteristic(
DIGITAL_WRITE_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new DigitalWriteCallback());
pCharacteristic = pService->createCharacteristic(
DIGITAL_READ_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new DigitalReadCallback());
pCharacteristic = pService->createCharacteristic(
LEDC_SETUP_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new LedcSetupCallback());
pCharacteristic = pService->createCharacteristic(
LEDC_WRITE_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new LedcWriteCallback());
pCharacteristic = pService->createCharacteristic(
LEDC_ATTACH_PIN_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new ledcAttachPinCallback());
pCharacteristic = pService->createCharacteristic(
LEDC_DETACH_PIN_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new ledcDetachPinCallback());
Complete Bluetooth config.
Functions that help with iPhone connections issue. Why is this done twice?
Time in 0.1 s intervals since the pair pushbutton was pressed.
Look for a paring pushbutton press only if we’re not connected.
Set the timer to 30 s.
Blink while pairing.
Turn the LED off and pairing off for the last blink.