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:
- Moved the
BLE.poll()
to a interrupt routine on Timer 3@2ms interval: no change - 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
}
}