BLE stops working if lots of data is sent or too much delay

Hello there,

Lately I've been working on a sketch to drive a few LED's using the TLC59711 IC. As seen in the code below I have an BLE.poll() called in the main loop as much as possible. The trouble begins when I leave the TLCChain.write() also in the main loop, besides the BLE poll: BLE stops responding, it doesn't appear as BLE device in any Android BLE apps or to the Android OS itself. Sometimes it appears, but fails to connect. Rarely, it connects and works for about one minute. If I remove the TLCChain.write() call from the main loop BLE works as expected.
If I have a looping animation (basically a lot of TLCChain.write()) BLE will again stop responding after I start the animation.

I made the write in a different test non-blocking by using the non-blocking blink tehnique but if I have the TLCChain.write() being called with a delay less than about 300ms the BLE again stops working.

I'm guessing that BLE needs a lot of polling? But even so, in the sketch, if no animation is running the only instructions that are ran in the loop() besides the if statement conditionals are BLE.poll() and TLCChain.write(), so there is not much to do if I need TLC written as much as possible.

I made two more tests besides the attached sketch:

  1. Moved the BLE.poll() to a interrupt routine on Timer 3@2ms interval: no change
  2. Moved the BLE.poll() to a FreeRTOS task with highest priority and left everything else in the idle task (loop() function): no change

Using Nano 33 IOT, with Nina firmware up-to-date at version 1.4.8

Any ideas about what is going on and if so, any solution that allows me to write TLC as much as possible?

Thank you.

#include <Adafruit_TLC59711.h>
#include <ArduinoBLE.h>
#include <SPI.h>

// How many chips do you have chained?
#define TLC_COUNT 2
#define TLC_DO 11
#define TLC_CLK 13
#define TLC_LED_COUNT 24  // Must be smaller than (TLC_COUNT * 12)

#define BUCK 6  // this is the buck used to driver last LED

const int LED[TLC_LED_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; // LED mapping, in this case useless

#define ats(step) (30000 / step)
#define atf ats(1)
const uint16_t anim1[24][TLC_LED_COUNT + 1] = {
    {off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off},
    {ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), ats(20), 10},
    {ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), ats(19), 20},
    {ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), ats(18), 40},
    {ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), 60},
    {ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), ats(14), 80},
    {ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), 110},
    {ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), ats(9), 140},
    {ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), 180},
    {ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), ats(3), 200},
    {ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), 255},
    {atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, 255},
    {off, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, 255},
    {off, off, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, 255},
    {off, off, off, off, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, 255},
    {off, off, off, off, off, off, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, 255},
    {off, off, off, off, off, off, off, off, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, 255},
    {off, off, off, off, off, off, off, off, off, off, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, atf, 255},
    {off, off, off, off, off, off, off, off, off, off, off, off, ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), ats(1), 255},
    {off, off, off, off, off, off, off, off, off, off, off, off, off, off, ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), ats(6), 180},
    {off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), ats(12), 110},
    {off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, ats(16), ats(16), ats(16), ats(16), ats(16), ats(16), 60},
    {off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, ats(19), ats(19), ats(19), ats(19), 20},
    {off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off, off}};

typedef struct animation_type {
    const uint16_t frameCount;
    const uint16_t (*frames)[TLC_LED_COUNT + 1];
    const uint8_t speed;
    const bool looping;
    const bool inverse;
} animation_t;

animation_t animations[1] = {
    {.frameCount = 24, .frames = anim1, .speed = 20, .looping = true, .inverse = false}
};

animation_t* activeAnimation = &animations[1];
Adafruit_TLC59711 TLCChain = Adafruit_TLC59711(TLC_COUNT, TLC_CLK, TLC_DO);

BLEService bleService("0000180a-000-1000-8000-000000000000");
BLEByteCharacteristic bleCharacteristic("0000180a-000-1000-8000-000000000001", BLERead | BLEWrite);
BLEStringCharacteristic buttonsCharacteristic("0000180a-000-1000-8000-000000000002", BLERead, 500);

uint16_t currentFrameIndex = 0;
uint8_t readChar;
bool running = false, bleConnected = false;

uint8_t lastBleCommand;
bool bleCommandUpdated = false;

void onBleWritten(BLEDevice central, BLECharacteristic characteristic) {
    lastBleCommand = characteristic.value()[0];
    bleCommandUpdated = true;
}

void blePeripheralConnectHandler(BLEDevice central) {
    // central connected event handler
    Serial.print("Connected event, central: ");
    Serial.println(central.address());
}

void blePeripheralDisconnectHandler(BLEDevice central) {
    // central disconnected event handler
    Serial.print("Disconnected event, central: ");
    Serial.println(central.address());
}

void setup() {
    Serial.begin(9600);
    if (!BLE.begin()) {
        while (1)
            Serial.println("starting BLE failed!");
    }

    BLE.setLocalName("LEDControl");
    // set the UUID for the service this peripheral advertises:
    BLE.setAdvertisedService(bleService);
    bleService.addCharacteristic(bleCharacteristic);
    bleService.addCharacteristic(buttonsCharacteristic);
    BLE.addService(bleService);

    BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
    BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);

    bleCharacteristic.setEventHandler(BLEWritten, onBleWritten);
    bleCharacteristic.writeValue(0);

    // The string format is: <name>:<color - optional>:<key>,<name2>:<color2>:<key2>, ....
    // Name: any string
    // Color: a hexadecimal color, from a color picker similar to https://www.google.com/search?q=color+picker or any standard color from the two lists below
    //           https://github.com/callstack/react-native-paper/blob/main/src/styles/DefaultTheme.tsx
    //           https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
    //           hex value: https://www.google.com/search?q=color+picker
    // Key: a single ASCII character.
    buttonsCharacteristic.writeValue("Anim1::a");
    BLE.advertise();

    TLCChain.begin();
    TLCChain.write();
}

void loop() {
    BLE.poll();

    if (bleCommandUpdated) {
        currentFrameIndex = 0;
        activeAnimation = &animations[lastBleCommand - 'a'];

        if (activeAnimation->inverse) {
            currentFrameIndex = activeAnimation->frameCount - 1;
        }

        running = true;
        bleCommandUpdated = false;
    }

    if (running) {
        for (size_t iLed = 0; iLed < TLC_LED_COUNT; iLed++) {
            TLCChain.setPWM(LED[iLed], activeAnimation->frames[currentFrameIndex][iLed]);
        }
        TLCChain.write();
        analogWrite(BUCK, activeAnimation->frames[currentFrameIndex][TLC_LED_COUNT]);

        if (activeAnimation->inverse) {
            currentFrameIndex--;
            if (currentFrameIndex == 0) {
                if (!activeAnimation->looping) {
                    running = false;
                }
                currentFrameIndex = activeAnimation->frameCount - 1;
            }
        } else {
            currentFrameIndex++;
            if (currentFrameIndex >= activeAnimation->frameCount) {
                if (!activeAnimation->looping) {
                    running = false;
                }
                currentFrameIndex = 0;
            }
        }
        delay(activeAnimation->speed);
    } else {
        TLCChain.write(); // Send whatever
    }
}

I'm not sure if this is the same problem as I've had. I'm driving a string of NeoPixels on the IoT and also using BlueTooth. What I found is that if I put the ble.poll() alongside my write for the NeoPixels the BlueTooth would quickly stop working. Like you're experiencing it wouldn't show up on a scan.

To work around the problem I check first if there was a BLE connection. If there is a connection I do not update the NeoPixels. Of course this means I can not write to the NeoPixels during the connection. However, it isn't a problem in may case as I am only controlling a few parameters with BLE.

You can find my code here if it helps: https://github.com/tjpetz/XmasLights2021/blob/main/XmasLights2021.ino

Sounds exactly like my problem, I do need however to send data while the connection is active and before that (it should send data regularly regardless of BLE status).
But I see that you do send data while bluetooth is connected by calling FastLED.show(); every 500 ms if runLights flag is set at the end of those functions. Either way, you send it at above the threshold at which if fails for me (300ms)

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