Skip to content

Commit

Permalink
Merge pull request #43 from everevian/nobra
Browse files Browse the repository at this point in the history
NobraControl integration
  • Loading branch information
MauAbata authored Oct 24, 2022
2 parents 0ee4770 + a7e5057 commit 44c5f25
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 6 deletions.
19 changes: 19 additions & 0 deletions doc/nobra.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Nobra Integration

Basic integration for Nobra devices is included with the Edge-O-Matic-3000.

### Setup
1. Turn on your Nobra device.
2. A green light indicates 'AzureFang' is enabled.
3. If the light is not green, press the outer two buttons on the control box at the same time and power cycle the device.
4. Turn the main control knob so that the Nobra device is vibrating slightly.
5. With your EOM3k, push the knob and select "Network Settings > Bluetooth Pair".
6. Select "NobraControl" to pair.
7. The EOM3k is now controlling your Nobra device.

### Configuration
The Nobra's main intensity knob essentially takes over the EOM3k's clamping of motor speed. This is why step 4 in Setup suggests to turn the intensity up until it's vibrating slightly - otherwise, it can appear the 'AzureFang' connection is not working.

Put another way, you want to configure your EOM3k's "Motor Max Speed" to be the highest it will go (255). This lets you fully adjust the intensity on the NobraControl device.

So the basic procedure after the 'AzureFang' connection is established is to enter manual mode, turn the EOM3k to max, and configure the NobraControl knobs to a suitable maximum intensity. Then you are ready for the automated modes.
29 changes: 29 additions & 0 deletions include/drivers/Nobra.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef __drivers_Nobra_h
#define __drivers_Nobra_h

#include "drivers/Device.h"

#define CMD_MAX_LEN 40

namespace BluetoothDriver {
class Nobra : public Device {
public:
Nobra(const char *name, NimBLEClient *client, NimBLERemoteCharacteristic *remote) :
Device(name, client), remote(remote) {};

static Device* detect(NimBLEAdvertisedDevice *device, NimBLEClient *client, NimBLERemoteService *service);

bool setSpeed(uint8_t speed) override;

protected:
char mapSpeed(uint8_t speed);
bool send(const char *cmd);
bool sendf(const char *fmt, ...);

private:
NimBLERemoteCharacteristic *remote;
char lastSentSpeed;
};
}

#endif
11 changes: 8 additions & 3 deletions src/BluetoothDriver.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "BluetoothDriver.h"
#include "drivers/Lovense.h"
#include "drivers/Nobra.h"

static const char *TAG = "BluetoothDriver";

Expand All @@ -9,7 +10,8 @@ namespace BluetoothDriver {
* driver's detect routine. Thanks!
*/
static const DeviceDetectionCallback DEVICE_DRIVERS[] = {
Lovense::detect,
Nobra::detect,
Lovense::detect
};

void registerDevice(Device *device) {
Expand Down Expand Up @@ -97,8 +99,11 @@ namespace BluetoothDriver {
return nullptr;
}

client->connect(device);

if (!client->connect(device)) {
ESP_LOGE(TAG, "Failed to connect to device (%s).", device->toString().c_str());
return nullptr;
}

BLEUUID serviceUUID = device->getServiceUUID();
if (serviceUUID.toString() == "") {
ESP_LOGE(TAG, "No serviceUUID advertised.");
Expand Down
4 changes: 1 addition & 3 deletions src/BluetoothServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ void BluetoothServer::disconnect() {
void BluetoothServer::begin() {
Serial.println("BLEDevice::init");
NimBLEDevice::init(Config.bt_display_name);

Serial.println("Create server");
this->server = BLEDevice::createServer();

this->server = NimBLEDevice::createServer();
Serial.println("Create service");
this->service = this->server->createService(SERVICE_UUID);

Expand Down
73 changes: 73 additions & 0 deletions src/drivers/Nobra.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "drivers/Nobra.h"
#include <cmath>

using namespace BluetoothDriver;

static const char* TAG = "BluetoothDriver::Nobra";

char Nobra::mapSpeed(uint8_t speed) {
const uint8_t nobraSpeedResolution = 15;
float proportion = float(speed) / 255.0;
uint8_t nobraSpeed = round(nobraSpeedResolution * proportion);
if (nobraSpeed == 0) {
return 'p';
}
char output = 'a' - 1 + nobraSpeed;
return output;
}

bool Nobra::setSpeed(uint8_t speed) {
char speedCommand = mapSpeed(speed);

bool isShiftingGears = lastSentSpeed != speedCommand;
if (!isShiftingGears) {
return false;
}

return this->sendf("%c", speedCommand);
}

bool Nobra::send(const char* cmd) {
if (!this->isConnected()) {
ESP_LOGE(TAG, "Client was disconnected.");
return false;
}

std::string cmdStr(cmd);
if (!this->remote->writeValue(cmdStr, false)) {
ESP_LOGE(TAG, "Write failed, disconnect.");
this->disconnect();
return false;
}

ESP_LOGD(TAG, "- remote = %s", this->remote->getUUID().toString().c_str());
ESP_LOGI(TAG, "Send command: \"%s\"", cmd);
return true;
}

bool Nobra::sendf(const char* fmt, ...) {
char cmd[CMD_MAX_LEN + 1] = "";
va_list args;
va_start(args, fmt);
vsniprintf(cmd, CMD_MAX_LEN, fmt, args);
va_end(args);
return send(cmd);
}

Device* Nobra::detect(NimBLEAdvertisedDevice* device, NimBLEClient* client, NimBLERemoteService* service) {
std::vector<NimBLERemoteCharacteristic*> *chars = service->getCharacteristics(true);
NimBLERemoteCharacteristic *writeChar = nullptr;

for (NimBLERemoteCharacteristic* c : *chars) {
if (c->canWriteNoResponse()) {
writeChar = c;
}
}
const char *deviceName = device->getName().c_str();
bool writeCharacteristicFound = writeChar != nullptr;
bool looksLikeNobra = std::string(deviceName).find("NobraControl") != std::string::npos;
if (writeCharacteristicFound && looksLikeNobra) {
Nobra *nobraDevice = new Nobra(deviceName, client, writeChar);
return (BluetoothDriver::Device *)nobraDevice;
}
}

0 comments on commit 44c5f25

Please sign in to comment.