Всем привет. Нашел в загашнике 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-команды блютусине и вообще в два порта писать) - возможно, вы подскажете в чем может быть трабла? Сообщайте пожалуйста если нужна дополнительная инфорамция. Заранее спасибо. Код (C++): // 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); } }
а какой протокол? Адресные светодиоды бывают разные. Скорее всего при посылке данных на ленту отключаются все прерывания, а данные приходят во время работы функции посылки данных на ленту. Посмотрите скорость и число св.диодов в функции управления лентой. похоже у вас время работы функции вписывается в паузу между байтами. Ну и посылайте управляющие сигналы с паузой между ними, большими времени работы функции упр. светодиодами.
ирония? одобряю я в курсе что скетч наговнокожен и его можно улучшить (например, вместо конвертации char->ascii code сразу принимать байты и не иметь оверхеда) ... но опыта в arduino мало от слова "совсем нет", поэтому любые советы принимаются
Делал скетч чтобы он был совместим с софтом для работы с амбилайтом (https://www.adafruit.com, http://lightpack.tv). Протокол там крайне неоптимален и выглядит следующим образом: [заголовок 6 байт][3 байта RGB для первого светодиода][3 байта RGB для второго светодиода]...[3 байта RGB для последнего светодиода] В моем случае на ленту из 120 лампочек происходит следующее: 1) Из serial по bluetooth считывается 366 байт (заголовок + значения rgb для каждой лампочки) 2) Одним циклом устанавливаются значения всех ламп Меня смущает тот момент что через usb/uart все отлично работает - по логике прерывание должно и на этот интерфейс влиять? Или нет? Однако, очень может быть что вы правы и в тот момент когда идет цикл записи в ленту данные теряются. Можете посоветовать как можно протестировать этот момент и/или организовать код таким образом чтобы исключить данный косяк? Заранее спасибо.
Нет! Я его даже не смотрел! Вы просто напомнили мне одного очень хорошего человека, который куда-то пропал с этого форума Честно, честно! Код не сморю, не до этого.
с виду по ссылкам никакого кода нет, так что не уверен- но скорее всего NRZ 800 кГц (это в лучшем случае), тогда посылка 366 байт занимает 366*8/800000=4 мс. Вот и выбирайте частоту uart чтобы время передачи одного бита было более этих 4 мс, ну грубо это 600 бод, можно попробовать 1200. в железном uart'е буфер на байт- это, с учетом стоп и старт бита, получается буфер на 10 бит, позволяет практически без потерь работать на 19200 (uart получает следующий байт даже когда предыдущий не считан). В софтовом сериале буфера нет.
если вы имели ввиду спецификацию самой ленты то это ws2812b: https://www.adafruit.com/datasheets/WS2812B.pdf хм, я подключал bluetooth-плату (HC-05) к контактам rx/tx ардуины нано и отключал usb - правильно ли я понимаю что в этом случае она начинает работать с железным uart? байты тоже терялись так что я в смущении
я уже догадался, NRZ800 - это ее родной протокол тут много тонкостей, в которых можно закопаться и не откопаться, поскольку вы используете библиотечные функции с асмом (а иначе ws2812 не зажечь), а там хрен знает какие регистры на что задействованы. Соответсвенно - попробуйте на 600 (1200) бод, либо попробуйте на максимальной скорости, но повторяйте посылки, чтобы хотя бы одна (c гарантией!!!) влезла в паузу между выполнениями функции ws 28121, это надо просто посчитать исходя из времени выполнения этой ws 2812 функции (можете считать ее 4 мс для вашего случая) и паузы между посылками, при этом надо будет ввести в посылку контрольную сумму, по которой отбраковывать битые посылки.
К сожалению, не могу изменить протокол взаимодействия с софтом для включения ambilight-подсветки (ну кроме как написать свою версию), поэтому будем считать что ambilight не будет работать без usb А для кастомного протокола попробую передавать чексуммы и спамить на большой скорости раз это единственный вариант (честно говоря, думал что arduino более гибкая в этом плане). Огромное спасибо за консультацию. Для тех кто случайно набредет на тему: если что то результат своих потуг (скетчи, драйвера, софт) выложу тут - https://github.com/vkfont/led-strip