Go Down

Topic: BLE very weak signal (Read 4644 times) previous topic - next topic

Klaus_K

Is there a way that Arduino keeps a buffer and keeps recording, let's say 20 consecutive samples, which I can later read from my central device? For example, if I can set read mode as 'latest' and read the last 20 samples from characteristic?
Storing the values in the Arduino is no issue. You could use a circular buffer to store the values. It is easy to implement.

https://en.wikipedia.org/wiki/Circular_buffer

The more difficult issue is, how you read the data from the characteristic.

However, the frequency at which I can call the data from the central device is limited in my case.
If you cannot keep up with reading data, how can you catch up with the last 20 values while new values are created? You need to find a way to manage the data. This depends a bit on what your application needs.

There are a few things that can help.

Combine multiple values in one characteristic. This reduces the overhead for the read. There is a limit of how many bytes one transfer can send.

Reduce the datatype. This would allow to combine more values into the read operation. e.g. you can send two 16-bit values instead of one 32-bit.

If your sensor data is created slow enough for your central device to catch up, you could read the data in a "burst" style. e.g. read multiple values - delay - read multiple values ...

You can also think about data pre-processing and sending values where you can lose values for your application without any issues. For example, the BLE standard for bicycle sensor uses this technique. Instead of sending the current revolutions per time interval it sends the absolute number of revolutions every time. So, instead of 10-11-10 it sends 10-21-31.

You can combine these.

RakhiAg

Combine multiple values in one characteristic. This reduces the overhead for the read. There is a limit of how many bytes one transfer can send.

Reduce the datatype. This would allow to combine more values into the read operation. e.g. you can send two 16-bit values instead of one 32-bit.


Thanks a lot for your answer Klaus_K. I have very little knowledge about all of this, so I understood parts of what you said. Kindly bear with me.

Can you tell me how do I combine multiple values in 1 characteristic? Basically, I am sending 4 quaternion values as 4 characteristics. Is there a way to send an array as a single characteristic perhaps?

How to reduce the datatype?

My end goal is to read quaternion values from the central device. Possibly all 4 quaternion values in single characteristic. In your code, you have defined characteristics as 'BLEFloatCharacteristic'. I am using the same format and defining 4 characteristics for 4 quaternion values. Because of this, it's taking too long to read from the central.

Your help is much appreciated. Thanks again.

Klaus_K

Can you tell me how do I combine multiple values in 1 characteristic? Basically, I am sending 4 quaternion values as 4 characteristics. Is there a way to send an array as a single characteristic perhaps?
Here is an example for the peripheral
Code: [Select]
/*
  This example creates a BLE peripheral with a service containing a characeristic with multiple values combined.
  The yellow LED shows the BLE module is initialized.
  The green LED shows RSSI of zero. The more it blinks the worse the connection.

  The circuit:
  - Arduino Nano 33 BLE Sense board.

  You can use a generic BLE central app, like LightBlue (iOS and Android) or
  nRF Connect (Android), to interact with the services and characteristics
  created in this sketch.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>

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

#define BLE_UUID_SENSOR_DATA_SERVICE              "2BEEF31A-B10D-271C-C9EA-35D865C1F48A"
#define BLE_UUID_MULTI_SENSOR_DATA                "4664E7A1-5A13-BFFF-4636-7D0A4B16496C"

#define NUMBER_OF_SENSORS 4

union multi_sensor_data
{
  struct __attribute__( ( packed ) )
  {
    float values[NUMBER_OF_SENSORS];
  };
  uint8_t bytes[ NUMBER_OF_SENSORS * sizeof( float ) ];
};

union multi_sensor_data multiSensorData;


//----------------------------------------------------------------------------------------------------------------------
// BLE
//----------------------------------------------------------------------------------------------------------------------

BLEService sensorDataService( BLE_UUID_SENSOR_DATA_SERVICE );
BLECharacteristic multiSensorDataCharacteristic( BLE_UUID_MULTI_SENSOR_DATA, BLERead | BLENotify, sizeof multiSensorData.bytes );


const int BLE_LED_PIN = LED_BUILTIN;
const int RSSI_LED_PIN = LED_PWR;


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

  pinMode( BLE_LED_PIN, OUTPUT );
  pinMode( RSSI_LED_PIN, OUTPUT );

  if ( setupBleMode() )
  {
    digitalWrite( BLE_LED_PIN, HIGH );
  }

  for ( int i = 0; i < NUMBER_OF_SENSORS; i++ )
  {
    multiSensorData.values[i] = i;
  }
}


void loop()
{
  #define UPDATE_INTERVALL 50
  static long previousMillis = 0;

  // listen for BLE peripherals to connect:
  BLEDevice central = BLE.central();

  if ( central )
  {
    Serial.print( "Connected to central: " );
    Serial.println( central.address() );

    while ( central.connected() )
    {
      unsigned long currentMillis = millis();
      if ( currentMillis - previousMillis > UPDATE_INTERVALL )
      {
        previousMillis = currentMillis;

        Serial.print( "Central RSSI: " );
        Serial.println( central.rssi() );

        if ( central.rssi() != 0 )
        {
          digitalWrite( RSSI_LED_PIN, LOW );

          for ( int i = 0; i < NUMBER_OF_SENSORS; i++ )
          {
            multiSensorData.values[i] = multiSensorData.values[i] + 0.1;
          }

          multiSensorDataCharacteristic.writeValue( multiSensorData.bytes, sizeof multiSensorData.bytes );
        }
        else
        {
          digitalWrite( RSSI_LED_PIN, HIGH );
        }
      }
    }

    Serial.print( F( "Disconnected from central: " ) );
    Serial.println( central.address() );
  }
}



bool setupBleMode()
{
  if ( !BLE.begin() )
  {
    return false;
  }

  // set advertised local name and service UUID:
  BLE.setDeviceName( "Arduino Nano 33 BLE" );
  BLE.setLocalName( "Arduino Nano 33 BLE" );
  BLE.setAdvertisedService( sensorDataService );

  // BLE add characteristics
  sensorDataService.addCharacteristic( multiSensorDataCharacteristic );

  // add service
  BLE.addService( sensorDataService );

  // set the initial value for the characeristic:
  multiSensorDataCharacteristic.writeValue( multiSensorData.bytes, sizeof multiSensorData.bytes );

  // start advertising
  BLE.advertise();

  return true;
}

Here is an example for the central
Code: [Select]
/*
  This example creates a BLE central that scans for a peripheral with a service containing a multi value characteristic.

  The circuit:
  - Arduino Nano 33 BLE or Arduino Nano 33 IoT board.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>

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

#define BLE_UUID_SENSOR_DATA_SERVICE              "2BEEF31A-B10D-271C-C9EA-35D865C1F48A"
#define BLE_UUID_MULTI_SENSOR_DATA                "4664E7A1-5A13-BFFF-4636-7D0A4B16496C"

#define NUMBER_OF_SENSORS 4

union multi_sensor_data
{
  struct __attribute__( ( packed ) )
  {
    float values[NUMBER_OF_SENSORS];
  };
  uint8_t bytes[ NUMBER_OF_SENSORS * sizeof( float ) ];
};

union multi_sensor_data multiSensorData;


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

  BLE.begin();

  BLE.scanForUuid( BLE_UUID_SENSOR_DATA_SERVICE );
}


void loop()
{
#define UPDATE_INTERVALL 10
  static long previousMillis = 0;

  unsigned long currentMillis = millis();
  if ( currentMillis - previousMillis > UPDATE_INTERVALL )
  {
    previousMillis = currentMillis;

    BLEDevice peripheral = BLE.available();

    if ( peripheral )
    {
      if ( peripheral.localName() != "Arduino Nano 33 BLE" )
      {
        return;
      }

      BLE.stopScan();

      explorePeripheral( peripheral );

      BLE.scanForUuid( BLE_UUID_SENSOR_DATA_SERVICE );
    }
  }
}


bool explorePeripheral( BLEDevice peripheral )
{
  if ( !peripheral.connect() )
  {
    return false;
  }
  Serial.println( "BLE connected" );

  if ( !peripheral.discoverAttributes() )
  {
    peripheral.disconnect();
    return false;
  }
  Serial.println( "BLE attributes discovered" );

  BLECharacteristic multiSensorDataCharacteristic = peripheral.characteristic( BLE_UUID_MULTI_SENSOR_DATA );
  if ( !multiSensorDataCharacteristic )
  {
    peripheral.disconnect();
    return false;
  }
  Serial.println( "BLE characteristic found" );

  if ( !multiSensorDataCharacteristic.canSubscribe() )
  {
    peripheral.disconnect();
    return false;
  }
  Serial.println( "BLE characteristic can subscribe" );

  if ( !multiSensorDataCharacteristic.subscribe() )
  {
    peripheral.disconnect();
    return false;
  }
  Serial.println( "BLE characteristic subscribed" );

  while ( 1 ) // need to add logic to leave
  {
#define BLE_POLL_INTERVALL 5
    static long previousMillis = 0;
    unsigned long currentMillis = millis();
    if ( currentMillis - previousMillis > BLE_POLL_INTERVALL )
    {
      BLE.poll();

      if ( multiSensorDataCharacteristic.valueUpdated() )
      {
        Serial.println( "BLE new data" );

        multiSensorDataCharacteristic.readValue( multiSensorData.bytes, sizeof multiSensorData.bytes );
        for ( int i = 0; i < NUMBER_OF_SENSORS; i++ )
        {
          Serial.print( "Sensor (" );
          Serial.print( i );
          Serial.print( "): " );
          Serial.println( multiSensorData.values[i] );
        }
      }
    }
  }

  peripheral.disconnect();
  return true;
}

How to reduce the datatype?
That is up to you and depends on your data. For instance you can convert a float (32-bit) into a 16-bit integer with a fixed divider. e.g. 1.2513 x 100 -> 125 => BLE => 125 / 100 = 1.25. You will loose some precision and range but you can send more data values.

RakhiAg

It worked!!! You were right, speed increased drastically when I combined the values in single characteristic instead of reading multiple characteristics. Thanks a lot!

Go Up