#1 Програмування Arduino » Лаги при bluetooth-соединении » 2016-03-17 07:51:59

wiznet
відповідей: 4

Всем привет.

Нашел в загашнике 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);
  }
}

Підвал форуму