Нашел в загашнике aruino nano и адресную светодиодную ленту - захотелось запилить ambilight подсветку. Чтобы не придумывать велосипед - взял уже готовый софт (http://lightpack.tv/downloads.php) и запилил скетч реализующий этот протокол. Через USB все работает отлично и без нареканий.
Однако, там же в загашнике валялся bt-модуль hc-05 - решил и его приспособить чтобы, значится, еще и с телефона светомузыку играть :)
Подключил как serial и обнаружил неожиданную проблему: через bluetooth теряются пакеты.
1) Ресивер лежит в 15 см от трансмиттера (скорее всего дело не в помехах)
2) Пробовал разные скорости (9600 до 115200, устанавливая идентичные скорости и в софте, и в скетче, и в самом модуле через AT-команды) - говорю это для того чтобы отмести самые очевидные вопросы про несоответствие baud rate
3) Ни с одной библиотекой не срослось (подключал к HardwareSerial вместо usb, использовал идущую в комплекте с IDE SoftwareSerial, скачивал отдельую NewSoftSerial)
Результат один - если посылать данные с интервалом то все отлично проходит, как только слать друг за другом байты то даже в первых 100 байтах начинаются жуткие лаги и потери символов.
Собственно, вывод тут один: виноваты чьи-то руки из задницы :)
Возможно, переполняется какой-нибудь буфер и теряются пакеты? Возможно, уже есть готовые рецепты исправления которые я не знаю. К сожалению я не силен в особенностях arduino и микроконтроллеров в целом, а для кого-то проблема может быть сразу очевидна.
Поэтому, предлагаю на ваш суд мой скромный скетч (один из, этот эмулирует программно serial чтобы иметь возможность посылать AT-команды блютусине и вообще в два порта писать) - возможно, вы подскажете в чем может быть трабла? Сообщайте пожалуйста если нужна дополнительная инфорамция. Заранее спасибо.
- Код: Выделить всё
// This sketch provide ability to control LED using USB and Bluetooth
// (with latency and packet losts but its work)
//
// 1) can understand Adalight protocol (Adalight, Prismatic etc software)
// 2) can switch custom leds using (to use in own plugins/implementations)
//
// Adalight protocol specification:
// A 'magic word' (along with LED count & checksum) precedes each block
// of LED data; this assists the microcontroller in syncing up with the
// host-side software and properly issuing the latch (host I/O is
// likely buffered, making usleep() unreliable for latch). You may see
// an initial glitchy frame or two until the two come into alignment.
// The magic word can be whatever sequence you like, but each character
// should be unique, and frequent pixel values like 0 and 255 are
// avoided -- fewer false positives. The host software will need to
// generate a compatible header: immediately following the magic word
// are three bytes: a 16-bit count of the number of LEDs (high byte
// first) followed by a simple checksum value (high byte XOR low byte
// XOR 0x55). LED data follows, 3 bytes per LED, in order R, G, B,
// where 0 = off and 255 = max brightness.
// <a href="https://github.com/FastLED/FastLED" title="https://github.com/FastLED/FastLED" rel="nofollow">https://github.com/FastLED/FastLED</a>
#include <FastLED.h>
#include <SoftwareSerial.h>
/* === feel free to change this values for your board */
#define STRIP_TYPE WS2812B // change to type you need
#define NUM_LEDS 120 // leds count in strip
#define LED_PIN 6 // pin stip data output
#define USB_SERIAL_RATE 115200 // usb connection speed
#define BT_SERIAL_RATE 115200 // bt connection speed (default is 9600)
#define BT_RX_PIN 11 //
#define BT_TX_PIN 10 // bluetooth module rx/tx pins
/* ==== */
#define SEED 0x55
#define STATE_WAITING 0
#define STATE_HEADER 1
#define STATE_PAYLOAD 2
#define PROTO_DEFAULT 0
#define PROTO_CUSTOM 1
#define HEADERSIZE 3
String magic = "Ada";
CRGB leds[NUM_LEDS];
SoftwareSerial btSerial(BT_RX_PIN, BT_TX_PIN);
typedef struct Storage{
uint8_t state;
String buffer;
int16_t expectedPayloadSize;
uint8_t proto;
};
Storage usb;
Storage bt;
// flash message on USB connection
void flash() {
LEDS.showColor(CRGB(255, 0, 0));
delay(500);
LEDS.showColor(CRGB(0, 255, 0));
delay(500);
LEDS.showColor(CRGB(0, 0, 255));
delay(500);
LEDS.clear(true);
}
// parse adalight-compatible packet
void parseDefaultPacket(Storage *storage) {
// reset all leds in strip first
memset(leds, 0, NUM_LEDS * sizeof(struct CRGB));
// iterate over payload and set next led in row
uint16_t ptr = 0;
for (uint16_t i = 0; i < NUM_LEDS; i++) {
leds[i].r = (byte)storage->buffer[ptr++];
leds[i].g = (byte)storage->buffer[ptr++];
leds[i].b = (byte)storage->buffer[ptr++];
}
}
// parse custom user packet
void parseCustomPacket(Storage *storage) {
// iterate over payload and set only required LEDs
uint16_t i = 0;
while (i < storage->buffer.length()) {
byte hi = (byte)storage->buffer[i++];
byte lo = (byte)storage->buffer[i++];
uint16_t addr = (hi << 8) + lo;
if (addr >= NUM_LEDS || addr < 0) {
i += 3; // skip incorrect values
} else {
leds[addr].r = (byte)storage->buffer[i++];
leds[addr].g = (byte)storage->buffer[i++];
leds[addr].b = (byte)storage->buffer[i++];
}
}
}
// switch port state (waiting for packet, waiting for packet header, waiting for data for led's)
void updateState(Storage *storage, uint8_t flag) {
storage->state = flag;
storage->buffer = "";
}
// process incoming serial byte according current state
void handleIncomingByte(Storage *storage, char input) {
storage->buffer += input;
// new packet incoming
if (storage->buffer.endsWith(magic)) {
return updateState(storage, STATE_HEADER);
}
switch (storage->state) {
// test header
case STATE_HEADER:
// time to check packet
if (storage->buffer.length() >= HEADERSIZE) {
byte hi = (byte)storage->buffer[0];
byte lo = (byte)storage->buffer[1];
byte chk = (byte)storage->buffer[2];
if((hi ^ lo ^ SEED) != chk) {
return updateState(storage, STATE_WAITING);
}
uint8_t len = (hi << 8) + lo;
if (len == (NUM_LEDS - 1)) {
storage->expectedPayloadSize = (len + 1) * 3;
storage->proto = PROTO_DEFAULT;
} else {
storage->expectedPayloadSize = len * 5;
storage->proto = PROTO_CUSTOM;
}
return updateState(storage, STATE_PAYLOAD);
}
break;
case STATE_PAYLOAD:
if (storage->buffer.length() >= storage->expectedPayloadSize) {
switch (storage->proto) {
case PROTO_DEFAULT:
parseDefaultPacket(storage);
break;
case PROTO_CUSTOM:
parseCustomPacket(storage);
break;
}
FastLED.show();
return updateState(storage, STATE_WAITING);
}
break;
case STATE_WAITING:
default:
if (storage->buffer.length() > magic.length()) {
storage->buffer = storage->buffer.substring(storage->buffer.length() - magic.length());
}
}
}
// execute code on startup
void setup() {
FastLED.addLeds<STRIP_TYPE, LED_PIN, GRB>(leds, NUM_LEDS);
flash();
updateState(&usb, STATE_WAITING);
updateState(&bt, STATE_WAITING);
Serial.begin(USB_SERIAL_RATE);
btSerial.begin(BT_SERIAL_RATE);
Serial.println(magic);
}
// receive packets in endless loop
void loop() {
if (Serial.available()) {
char input = Serial.read();
handleIncomingByte(&usb, input);
}
if (btSerial.available()) {
char input = btSerial.read();
handleIncomingByte(&bt, input);
}
}