Лаги при bluetooth-соединении
- Войдите на сайт для отправки комментариев
Ср, 16/03/2016 - 22:00
Всем привет.
Нашел в загашнике 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. // https://github.com/FastLED/FastLED #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); } }
В loop вместо if(...available) поставьте while (...available) - как минимум. Иначе - неизбежные потери данных на больших скоростях UART.
мимо пробегал, интересно стало а почему так?
Аааа а почему там вообще if стоит )) оно и понятно ))
Подозреваю что для таких вещей вообще нужно по прерываниям работать...
Видимо потому что опрашивается не один serial а два
Ну, есть Serial.SerialEvent который как бы для этого предназначен. Но, во первых, один фиг цикл loop на время выполнения блокируется так что я че-то не вижу преимуществ. Во вторых, hardware serial уже занят usb, поэтому bluetooth приходится эмулировать через software serial а там и такого нет.
Оффтоп: до этого ардуиной не занимался, просто делаю "проект выходного дня". Похоже на квест: найди библиотеку актуальную которая обновлялась хотя бы раз за последний год, прочитай мануалы и удивись что официальный Serial.flush() не смотря на название не очищает буфер и тп... Жалко времени свободного мало, так бы позависал : )