luksal/ESP32-DMX

Data shifts at random times

sasodoma opened this issue · 9 comments

Hello, I have been testing your library for a while and finally got a DMX controller today. I made a simple script to read all the 192 channels that the controller supports and it generally works great. Most of the reads come through just fine but a lot of them are randomly shifted, some channels get left out or get written to a wrong address. I have ruled out hardware problems by also trying a USB to DMX converter and the Freestyler software, same results. I also connected the RX pin of a separate USB to serial converter and observed the raw data as it was coming in. There were no shifts there either. Any idea what might be the problem?

Here is some output:
https://pastebin.com/raw/9iGFAZ4z

Here is my code:

#include <dmx.h>

unsigned long readcycle = 0;
int dataChanged = 0;
char string[8];
uint8_t channels[12][16];

void setup() {
  Serial.begin(115200);
  DMX::Initialize();
}

void loop()
{
  if (millis() - readcycle > 1000)
  {
    readcycle = millis();
    for (int i = 0; i < 12; i++) {
      for (int j = 0; j < 16; j++) {
        if (channels[i][j] != DMX::Read((i * 16) + j + 1)) {
          dataChanged = 1;
          channels[i][j] = DMX::Read((i * 16) + j + 1);
        }
      }
    }
    if ( dataChanged ) {
      dataChanged = 0;
      if (DMX::IsHealthy()) {
        Serial.println(": ok - ");
      }
      else {
        Serial.println(": fail - ");
      }
      for (int i = 0; i < 12; i++) {
        for (int j = 0; j < 16; j++) {
          sprintf(string, "%03d ", channels[i][j]);
          Serial.print(string);
        }
        Serial.println(" ");
      }
      Serial.println(" ");
    }
  }
}

Unfortunately, you get values from the library at least 192 times per second, in the worst case even 384 times per second.

Every call of the DMX::Read function blocks the further access of the other library functions to the dmx buffer. So it could be that the interrupts here can not be executed in time, so that the input buffer of the UART interface overflows.

If you need larger blocks of data from the dmx buffer, I would recommend that you write a function (and submit it as a pull request to the library) that uses memcpy to copy all the required area completely and block access for the interrupts only once.

Thank you for your answer. Unfortunately, this doesn't seem to be the source of the problem. Even if I replace the first two for loops with something like this:

int i = 4, j = 4;
if (channels[i][j] != (tempRead = DMX::Read((i * 16) + j + 1))) {
  dataChanged = 1;
  channels[i][j] = tempRead;
}

the random shifts still occur and don't seem to get any rarer. I will have to investigate further. However, having a function to copy large blocks of data at once does sound useful, and while it may be slightly above my skillset, I might take it as an opportunity to learn.

Same problem here... data are randomly shifted...

I'll try to investigate the problem...

   Giampaolo

I'll try to investigate the problem...

have you tried to move your code to another core?

I'll try to investigate the problem...

have you tried to move your code to another core?

Hi,

it seems that the UART is randomly missing some chars when there is WiFi/BLE activity... I've reduced the problem by using a queue instead of locking with semaphore... but this does not solve it.

I may be wrong, but probably the right solution is to write a low level uart driver...

         Giampaolo

I am facing this same issue when using wifi with the library, did anyone find a solution for this?

no... not really...

so far I'm just checking whether a full 512-byte DMX frame has been received or not... but this is a bad hack as nothing prevents a DMX master to address a subset of the channels; as stated before the only "real" solution is probably to review the low level uart driver.

  Giampaolo

Can you please explain how are you checking whether a full 512-byte DMX frame is received?

Hi,

here is part of my code... it's bad formatted... sorry...

  Giampaolo

`
// ------------------------------------------------------------------------------------------------------------------------
// dmx1_event_task
// ------------------------------------------------------------------------------------------------------------------------

#ifdef CONFIG_DMX1_ENABLED

void dmx1_event_task(void pvParameters)
{
uart_event_t event;
// bool gpio5_toggle=false;
uint8_t
dtmp = (uint8_t*) malloc(DMX1_BUF_SIZE);

// WARNING! RTS controllato manualmente per avere il ricevitore sempre attivo
// nel ns caso lo RTS controlla la abilitazione del trasmettitore RS485 CONFIG_DMX1_EN_GPIO (se la uscita è posta a 1) o del ricevitore (se la uscita è posta a 0)
// il pilotaggio è invertito! 1: RTS output low (active); 0: RTS output high (block)
uart_set_rts(DMX1_UART_NUM, 1); // RTS line LOW aka RTS active aka RS485 RX enabled

while(1){

// wait for data in the dmx_queue
if (xQueueReceive(dmx1_rx_queue, (void * )&event, (portTickType)portMAX_DELAY)){

     bzero(dtmp, DMX1_BUF_SIZE);

     switch(event.type){

        case UART_DATA:

          // read the received data
             uart_read_bytes(DMX1_UART_NUM, dtmp, event.size, portMAX_DELAY);

          // check if break detected
             if (dmx1_state == DMX1_BREAK){

             // if not 0, then RDM or custom protocol
                if (dtmp[0] == 0){
                   dmx1_state = DMX1_DATA;
                // reset dmx adress to 0
                   dmx1_rx_curraddr = 0;
                   }
                }

          // check if in data receive mode
             if (dmx1_state == DMX1_DATA){

             // copy received bytes to dmx data array
                for(int i=0; i<event.size; i++){
                   if (dmx1_rx_curraddr < DMX1_FRAME_LEN){
                      dmx1_data[dmx1_rx_curraddr++] = dtmp[i];
                      }else{
                      break;
                      } 
                   }
                }

             break;

        case UART_BREAK:

          // break detected
          // clear queue und flush received bytes
          // ESP_LOGI(TAG, "DMX BRK L=%d...", dmx1_rx_curraddr);

          // WARNING! visto che ogni tanto perdo dei caratteri, per ora considero un frame valido solo se ho ricevuto dmx1_minframelen ( in genere posto a 1+512 bytes)... 
          // in teoria dovrebbe essere sufficiente aver ricevuto ALMENO (dmx1_payloadoffset + dmx1_payloadlen) bytes con dmx1_payloadlen=4 (al momento utilizzo 4 canali per R,G,B e W)
             if (dmx1_rx_curraddr >= dmx1_minframelen){
                uint32_t _dlxworddata = dmx1_data[dmx1_payloadoffset+3] << 24 | dmx1_data[dmx1_payloadoffset+2] << 16 | dmx1_data[dmx1_payloadoffset+1] << 8 | dmx1_data[dmx1_payloadoffset];
                xQueueSend(dmx1_queue, (void *)&_dlxworddata, 0);

            // WARNING! qui utilizzo la uscita SPI CS (GPIO5, non usata per gli shift register) come semplice uscita ai fini del debug per verificare a che frequenza "gira" il DMX e se vengono persi dei caratteri...
               #if 0 // defined(CONFIG_SPI_CS_GPIO) && (CONFIG_SPI_CS_GPIO == -1) && defined(SPI_CS_AS_DEBUG) && (SPI_CS_AS_DEBUG==1)
                gpio5_toggle = !gpio5_toggle;
                gpio_set_level(5, gpio5_toggle);
               #endif

                }

             uart_flush_input(DMX1_UART_NUM);
             xQueueReset(dmx1_rx_queue);
             dmx1_state = DMX1_BREAK;
             break;

        case UART_FRAME_ERR:
        case UART_PARITY_ERR:
        case UART_BUFFER_FULL:
        case UART_FIFO_OVF:
        default:

          // error recevied, going to idle mode
          // ESP_LOGE(TAG, "uart error...");
             uart_flush_input(DMX1_UART_NUM);
             xQueueReset(dmx1_rx_queue);
             dmx1_state = DMX1_IDLE;
             break;
        }
    }
}

}

#endif

`

Can you please explain how are you checking whether a full 512-byte DMX frame is received?