Nano33 BLE Sense connection and control of BLE Relay Module

Hello all,
I have a new Nano 33 BLE Sense here and my goal is to use it to control a BLE connected Drok DSD Relay module. The Drok IOS app for the iPhone to control the relays and it works fine. The characteristic control word also provided by DROK is:

Channel 1 ON: A00101A2 Channel 1 OFF: A00100A1
Channel 2 ON: A00201A3 Channel 2 OFF: A00200A2
Channel 3 ON: A00301A4 Channel 3 OFF: A00300A3
Channel 4 ON: A00401A5 Channel 4 OFF: A00400A4

I’m also able to use this info with the Bluetooth LE Explorer PC program along with a generic USB Bluetooth module to connect to the DSD relay module and control the relays with program by writing the various control words to the DSD relay module “SimpleKeyState” Characteristic.

I’ve tried following the code and instructions from several example Arduino BLE programs found on the internet to get the Nano 33 to connect to the relay module all to no avail.

What am I missing? Is it that the Nano 33 BLE Sense is not capable of connecting to a BLE Slave device for this purpose?

Any info to help me understand this BLE communications would be greatly appreciated.

The DSD relay device info is as listed below:

BT Address: b4:52:a9:b0:17:df
Number of Services: 4

BT 4.2 Secure Connection: False
Device Connected: True
Service Name: GenericAccess

Service UUID: 00001800-0000-1000-8000-00805f9b34fb

Characteristic Name: DeviceName - User Description: - Handle: 2 - Value: DSD Relay

Characteristic Name: Appearance - User Description: - Handle: 4 - Value: 00-00

Characteristic Name: PeripheralPrivacyFlag -User Description: - Handle: 6 - Value: 00

Characteristic Name: ReconnectionAddress - User Description: - Handle: 8 - Value: Read Not Permitted

Characteristic Name: PeripheraIPreferredConnectionParameters —User Description: — Handle: 10 –
Value: 50-00-A0-00-00-00-E8-03
Service Name: GenericAttribute

Service UUID: 00001801-0000-1000-8000-00805f9b34fb

Characteristic Name: ServiceChanged - User Description: - Handle: 13 - Value: Read Not Permitted

Service Name: Devicelnformation

Service UUID: 0000180a-0000-1000—8000-00805f9b34fb

Characteristic Name:Systemld - User Description: - Handle: 17 - Value: DF-17-B0-00-00-A9-52-B4

Characteristic Name: ModelNumberString - User Description: - Handle: 19 — Value: SH-HC-08

Characteristic Name: SerialNumberString - User Description: - Handle: 21 — Value: Serial Number

Characteristic Name: FirmwareRevisionString - User Description: - Handle: 23 - Value: Firmware Revision

Characteristic Name: HardwareRevisionString - User Description: - Handle: 25 — Value: V1.12

Characteristic Name: SoftwareRevisionString - User Description: - Handle: 27 - Value: SHV1.5

Characteristic Name: ManufacturerNameString - User Description: — Handle: 29 - Value: www.sihaicorp.com

Characteristic Name: leee11073_20601RegulatoryCertificationDataList — User Description: — Handle: 31 — Value: FE-OO-65-78-70-65-72-69-6D-65-6E-74-61-6C

Characteristic Name: Pnpld — User Description: — Handle: 33 — Value: O1-0D-00-00-00-10-01

Service Name: 65504

Service UUID: 0000ffe0-0000-1000-8000-00805f9b34fb

Characteristic Name: SimpleKeyState ~ User Description: Characteristic1 - Handle: 36 - Value: A0-04-00-A4

SH-BT04A.pdf (369 KB)

Welcome to the forum

Could you please provide a link to the documentation of the relay module and provide the name and company of the iOS app. I tried DROK but could not find anything related?

Hi Klaus, thanks for your reply. The company website is:

The free IOS App for the relay module is called DSD TECH Bluetooth in the Apple App store.

The main document of interest from DSD TECH is SH-BT04A.pdf, which DSD emailed to me in response to my query directly to them asking about programming assistance. I'm trying to see if there's someway I can attach that document to a response or post here... Ok - I managed to add the PDF as an attachment to the original post...

There is link for a minimal amount of documentation here there:

that does however have the relay command hex words defined.

Probably my biggest problem is that I am a terrible programmer and rely heavily on example code from others far more knowledgeable than I to build upon and learn. Unfortunately, I've been unable to locate example code where the Nano 33 BLE is the master trying to control a peripheral device. Most examples are based around the Nano 33 BLE being a slave to a master such as an iPhone or Android it seems.

Thanks,
Bud

To figure out how your relay module works, I wrote a sketch that simulates your relay module and used the iOS app from DSD to write to it. Here is what I found:

  • The app looks for an advertised service with the UUID 0xFFE0 and writes to a characteristic with the UUID 0xFFE1.
  • The commands/control words are given in the wrong byte order. You will note I changed them in my central example below.
  • The app ignores the name of the relay board and works with boards that use the same service UUID but a different name.

I have created a small central example based on the library example and tested it with my simulation sketch.

File → Examples → ArduinoBLE → Central → LEDControl

Can you test the example with your board and see whether you can control relay 1 with a button connected to pin 2 of the Arduino Nano 33 BLE?

/*
  DSD SH-BT04A Control

  This example scans for BLE peripherals until one with the advertised service
  UUID is found. Once discovered and connected, it will remotely control the BLE Peripheral's
  relay, when the button is pressed or released.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>

//----------------------------------------------------------------------------------------------------------------------
// BLE UUIDs
//----------------------------------------------------------------------------------------------------------------------

// see User Guide

#define BLE_UUID_SH_BT04A_SERVICE                 "FFE0"
#define BLE_UUID_SH_BT04A_COMMAND                 "FFE1"

// SH_BT04A Control Instructions

#define SH_BT04A_COMMAND_CHANNEL_1_ON             0xA20101A0
#define SH_BT04A_COMMAND_CHANNEL_1_OFF            0xA10001A0

#define SH_BT04A_COMMAND_CHANNEL_2_ON             0xA30102A0
#define SH_BT04A_COMMAND_CHANNEL_2_OFF            0xA20002A0

#define SH_BT04A_COMMAND_CHANNEL_3_ON             0xA40103A0
#define SH_BT04A_COMMAND_CHANNEL_3_OFF            0xA30003A0

#define SH_BT04A_COMMAND_CHANNEL_4_ON             0xA50104A0
#define SH_BT04A_COMMAND_CHANNEL_4_OFF            0xA40004A0

#define DEVICE_NAME                               "DSD Relay"

// variables for button
const int buttonPin = 2;
int oldButtonState = LOW;

void setup()
{
  Serial.begin( 9600 );
  while ( !Serial );

  // configure the button pin as input
  pinMode( buttonPin, INPUT );

  // initialize the BLE hardware
  BLE.begin();

  Serial.println( "BLE Central - LED control" );

  // start scanning for peripherals
  BLE.scanForUuid( BLE_UUID_SH_BT04A_SERVICE );
}

void loop()
{
  // check if a peripheral has been discovered
  BLEDevice peripheral = BLE.available();

  if ( peripheral )
  {
    // discovered a peripheral, print out address, local name, and advertised service
    Serial.print( "Found " );
    Serial.print( peripheral.address() );
    Serial.print( " '" );
    Serial.print( peripheral.localName() );
    Serial.print( "' " );
    Serial.print( peripheral.advertisedServiceUuid() );
    Serial.println();

    if ( peripheral.localName() != DEVICE_NAME )
    {
      return;
    }

    // stop scanning
    BLE.stopScan();

    controlLed( peripheral );

    // peripheral disconnected, start scanning again
    BLE.scanForUuid( BLE_UUID_SH_BT04A_SERVICE );
  }
}

void controlLed( BLEDevice peripheral )
{
  // connect to the peripheral
  Serial.println( "Connecting ..." );

  if ( peripheral.connect() )
  {
    Serial.println( "Connected" );
  }
  else
  {
    Serial.println( "Failed to connect!" );
    return;
  }

  // discover peripheral attributes
  Serial.println( "Discovering attributes ..." );
  if ( peripheral.discoverAttributes() )
  {
    Serial.println( "Attributes discovered" );
  }
  else
  {
    Serial.println( "Attribute discovery failed!" );
    peripheral.disconnect();
    return;
  }

  // retrieve the LED characteristic
  BLECharacteristic commandCharacteristic = peripheral.characteristic( BLE_UUID_SH_BT04A_COMMAND );

  if ( !commandCharacteristic )
  {
    Serial.println( "Peripheral does not have relay command characteristic!" );
    peripheral.disconnect();
    return;
  }
  else if ( !commandCharacteristic.canWrite() )
  {
    Serial.println( "Peripheral does not have a writable relay command characteristic!" );
    peripheral.disconnect();
    return;
  }

  while ( peripheral.connected() )
  {
    // while the peripheral is connected

    // read the button pin
    int buttonState = digitalRead( buttonPin );

    if ( oldButtonState != buttonState )
    {
      // button changed
      oldButtonState = buttonState;

      if ( buttonState )
      {
        Serial.println( "button pressed" );

        // button is pressed, write command to turn the relay on
        commandCharacteristic.writeValue( ( uint32_t )SH_BT04A_COMMAND_CHANNEL_1_ON );
      }
      else
      {
        Serial.println( "button released" );

        // button is released, write command to turn the relay off
        commandCharacteristic.writeValue( ( uint32_t )SH_BT04A_COMMAND_CHANNEL_1_OFF );
      }
    }
  }

  Serial.println( "Peripheral disconnected" );
}

I can help you change the code to some better logic and extend it for all relays. First I need to know whether the relay board works as expected from what I read in the documentation.

As of now the example does not have a disconnect. Once the Nano 33 BLE is connected, you might have to reset the relay board if you want to connect again with the phone app.

Thanks Klaus!
I have to admit this code gets much farther than anything I've tried to date!
It starts off well and says it is connected; however, the blue LED on the DSD relay module continues to flash indicating no connection. When the iPhone app connects the relay module blue LED stops flashing and goes to steady on.

The serial monitor window also indicates a failure at the Discovering Attributes phase and shows the following:

BLE Central - LED control
Found b4:52:a9:b0:17:df 'DSD Relay' ffe0
Connecting ...
Connected
Discovering attributes ...
Attribute discovery failed!
Found b4:52:a9:b0:17:df 'DSD Relay' ffe0Connecting ...
Connected
Discovering attributes ...
Attribute discovery failed!
.
.
.

Seems like it might be close though, maybe; when I reset the NANO 33 BLE and restart the serial monitor I can see the blue LED on the DSD relay module 'stutter' occasionally as the code loops trying to connect. One time the LED even went solid (indicating connected) for an extended period of time while the serial monitor indicated Discovering attributes.

Hi again Klaus -
Well an interesting development... I left this running on my bench and went to eat some lunch. When I returned I noted that one of the relays was turned on (red LED on one of them was illuminated). Hmmm... On reviewing the serial monitor I saw this...
Found b4:52:a9:b0:17:df 'DSD Relay' ffe0

Connecting ...
Connected
Discovering attributes ...
Attributes discovered
button pressed
Peripheral disconnected
Found b4:52:a9:b0:17:df 'DSD Relay' ffe0
Connecting ...
Connected
Discovering attributes ...

So, the code after perhaps an hour somehow found the attributes it was seeking and determined the button was pressed (at present no button attached, so probably the bit port is just pulled high internally...)

Curious eh?

buddude:
(at present no button attached, so probably the bit port is just pulled high internally...)

The pin can only be in one state internally. When the pin voltage floats it can be influenced many factors e.g. someone touching the pin.

Do you have any questions how they sketch works?

Do you need any help implementing the additional logic?

How do you want to control the relays? e.g. button press, time, sensor data ...

Hi Klaus,
Got it, I wasn't sure what the digital input structure of the NANO 33 BLE SENSE is, the documentation indicates there is a pullup on the DI ports, but when looking at the pin with a meter it's clear it does not. There's other chatter on the internet about this... So, I added switch to ground with a 10K pullup on D2. The program seems to work, intermittently getting connected and intermittently turning the relay on or off in response to the switch. I sprinkled in some delays (1000mS) and that seemed to improve things. I then commented out all the serial print commands since the program seems to hang up waiting for the com port and serial monitor; of course I lose all diagnostic feedback, but that's how it will be in real life, so worth a shot to see if it improved things.

To be sure I am a million miles ahead of where I was thanks to your input and am very slowly getting a feel for the BLE code interface; which is as unintuitive as it could be to me.

I'll continue to experiment more tomorrow with it to see if I can make it more reliable and get back to you with some more questions. One step at a time. For the moment I just want to be able to control the individual relays with the switches on the NANO 33 BLE and it seems strait forward on how to add additional switches to command the additional relays based on your example.

Thank you again Klaus for all your help and patience!
Bud

buddude:
I sprinkled in some delays (1000mS) and that seemed to improve things.

Please do not do that.The use of the delay function is a very bad idea in general but especially with communication stacks. On most Arduinos it wastes CPU cycles that could be used for something else. On the Arduino Nano 33 BLE it uses the processor sleep mode to waste time. You could use it for power saving when you do not communicate, but when you are connected you should not sleep for long. The BLE stack needs to process data continuously when it becomes available or when it is requested.

You can however control the timing of certain functions without the use of delay. Have a look at the following example

File -> Examples -> 02.Digital -> BlinkWithoutDelay

This will teach you how to limit the execution of parts of your code e.g. to 10 times a second.

The goal should be to ensure the loop function gets executed as often as possible. (Right now the example does not do that. Most official Arduino examples have this flaw.) This will allow you to extend your code over time without the need to adjust all the delays in your code.

Right now the issue likely comes from the fact that the buttons is not debounced. This will cause the write to the characteristic to be faster then the BLE communication will allow to happen. If you limit the button read e.g. to 10 times a second the issue likely goes away.

OK Klaus -
Understood - delay(mS) function = bad programing technique.
It seems that the entire issue was that of the DI2 not being firmly asserted / floating and then switch bounce.

I restored the code to that of your original example with no changes, but with the now well controlled switch and 10K pullup on the DI port and it works perfectly and responsively. Wow! There’s a lesson there… Ensure DI ports are well asserted and do not believe web pages inferring they are internally pulled up…

Without the debounce the code was rather erratic, but did work. So, on your recommendation I also implemented a debounce code segment that is similar or based up to the BlinkWithoutDelay code. Initially I used a debounceDelay of 50mS which did work, but proved slightly erratic (which manifests as losing the BLE connection…). So I increased the value up to 100mS (as you suggested as well) and it works very reliably.

Commenting out the while ( !Serial ); line in the setup allows the code to run without need of firing off the serial monitor.

I also added in the onboard RGB LED controls so that I could illuminate the BLUE LED when connecting to the BLE device and see if it loses connection, a handy diagnostic indicator, eventually I may relegate that to an external LED if and when I ever package this thing.

My current code is attached below and is working reliably now. Tomorrow I’ll add additional code for the other switches to test them as well.

As always, thank you for your patience and help, once past the frustration this is starting to be fun again :slight_smile:

/*
  DSD SH-BT04A Control

  This example scans for BLE peripherals until one with the advertised service
  UUID is found. Once discovered and connected, it will remotely control the BLE Peripheral's
  relay, when the button is pressed or released.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>

//----------------------------------------------------------------------------------------------------------------------
// BLE UUIDs
//----------------------------------------------------------------------------------------------------------------------

// see User Guide

#define BLE_UUID_SH_BT04A_SERVICE                 "FFE0"
#define BLE_UUID_SH_BT04A_COMMAND                 "FFE1"

// SH_BT04A Control Instructions

#define SH_BT04A_COMMAND_CHANNEL_1_ON             0xA20101A0
#define SH_BT04A_COMMAND_CHANNEL_1_OFF            0xA10001A0

#define SH_BT04A_COMMAND_CHANNEL_2_ON             0xA30102A0
#define SH_BT04A_COMMAND_CHANNEL_2_OFF            0xA20002A0

#define SH_BT04A_COMMAND_CHANNEL_3_ON             0xA40103A0
#define SH_BT04A_COMMAND_CHANNEL_3_OFF            0xA30003A0

#define SH_BT04A_COMMAND_CHANNEL_4_ON             0xA50104A0
#define SH_BT04A_COMMAND_CHANNEL_4_OFF            0xA40004A0

#define DEVICE_NAME                               "DSD Relay"

//variables for RGB LED
#define RED 22     
#define BLUE 24     
#define GREEN 23
#define LED_PWR 25

// variables for button
const int buttonPin = 2;
int buttonState;                     // the current reading from the input pin
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 100;    // the debounce time; increase if the output flickers

void setup()
{
  Serial.begin( 9600 );
  while ( !Serial );

  // configure the button pin as input
  pinMode( buttonPin, INPUT );

  // intitialize the digital Pin as an output
  pinMode(RED, OUTPUT);
  pinMode(BLUE, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(LED_PWR, OUTPUT);
  digitalWrite(RED, HIGH); // turn the RED LED off by making the voltage LOW
  digitalWrite(BLUE, HIGH); // turn the BLUE LED off by making the voltage LOW
  digitalWrite(GREEN, HIGH); // turn the GREEN LED off by making the voltage LOW
  digitalWrite(LED_PWR, HIGH); // turn the LED_PWR off by making the voltage LOW

  // initialize the BLE hardware
  BLE.begin();

  Serial.println( "BLE Central - LED control" );
  Serial.print( "Debounce Delay = " );
  Serial.println( debounceDelay );
  
  // start scanning for peripherals
  BLE.scanForUuid( BLE_UUID_SH_BT04A_SERVICE );
}

void loop()
{
  // check if a peripheral has been discovered
  BLEDevice peripheral = BLE.available();

  if ( peripheral )
  {
    // discovered a peripheral, print out address, local name, and advertised service
    Serial.print( "Found " );
    Serial.print( peripheral.address() );
    Serial.print( " '" );
    Serial.print( peripheral.localName() );
    Serial.print( "' " );
    Serial.print( peripheral.advertisedServiceUuid() );
    Serial.println();

    if ( peripheral.localName() != DEVICE_NAME )
    {
      return;
    }

    // stop scanning
    BLE.stopScan();

    controlLed( peripheral );

    // peripheral disconnected, start scanning again
    BLE.scanForUuid( BLE_UUID_SH_BT04A_SERVICE );
  }
}

void controlLed( BLEDevice peripheral )
{
  // connect to the peripheral
  Serial.println( "Connecting ..." );

  if ( peripheral.connect() )
  {
    digitalWrite(BLUE, LOW);
    Serial.println( "Connected" );
  }
  else
  {
    Serial.println( "Failed to connect!" );
    return;
  }

  // discover peripheral attributes
  Serial.println( "Discovering attributes ..." );
  if ( peripheral.discoverAttributes() )
  {
    Serial.println( "Attributes discovered" );
  }
  else
  {
    Serial.println( "Attribute discovery failed!" );
    peripheral.disconnect();
    return;
  }

  // retrieve the LED characteristic
  BLECharacteristic commandCharacteristic = peripheral.characteristic( BLE_UUID_SH_BT04A_COMMAND );

  if ( !commandCharacteristic )
  {
    Serial.println( "Peripheral does not have relay command characteristic!" );
    peripheral.disconnect();
    return;
  }
  else if ( !commandCharacteristic.canWrite() )
  {
    Serial.println( "Peripheral does not have a writable relay command characteristic!" );
    peripheral.disconnect();
    return;
  }

  while ( peripheral.connected() )
  {
    // while the peripheral is connected

    // read the button pin
    int reading = digitalRead( buttonPin );
    
    // check to see if you just pressed the button
    // (i.e. the input went from LOW to HIGH), and you've waited long enough
    // since the last press to ignore any noise:

    // If the switch changed, due to noise or pressing:
    if (reading != lastButtonState) {
      // reset the debouncing timer
      lastDebounceTime = millis();
    }

       if ((millis() - lastDebounceTime) > debounceDelay) {
          // whatever the reading is at, it's been there for longer than the debounce
          // delay, so take it as the actual current state:

          // if the button state has changed:
        if (reading != buttonState) {
              buttonState = reading;

          if ( buttonState ){ 
             Serial.println( "button pressed" );

             // button is pressed, write command to turn the relay on
             commandCharacteristic.writeValue( ( uint32_t )SH_BT04A_COMMAND_CHANNEL_1_ON );
          }
          else
          {
             Serial.println( "button released" );

             // button is released, write command to turn the relay off
             commandCharacteristic.writeValue( ( uint32_t )SH_BT04A_COMMAND_CHANNEL_1_OFF );
          }
       }
     }
       // save the reading. Next time through the loop, it'll be the lastButtonState:
       lastButtonState = reading;
  }
  Serial.println( "Peripheral disconnected" );
  digitalWrite(BLUE, HIGH);
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.