Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 15 of 15

Thread: Can't get Teensy LC to receive DMX

  1. #1
    Junior Member
    Join Date
    Nov 2019
    Posts
    7

    Can't get Teensy LC to receive DMX

    I'm trying to get a Teensy LC to receive DMX using the TeensyDMX library (and testing with TeensyDmx as well), but I'm stuck. I could've sword I've gotten it to work before, but now I just for the life of me can't get it to receive.

    I'm using a Maxim 3.3V transceiver. I'm getting the LC to send DMX, but not receive it. (I've wired DE/_RE to a Teensy pin).

    I know the hardware is OK; I've checked the output of the transceiver on the scope, and it seems fine. I have even connected the transceiver TTL output to an Arduino Leonardo, which happily accepts the signal as valid DMX using a different library.

    In my initial testing the LC would appear to freeze (serial output would stop abruptly) when connected to a valid DMX source. After some messing around with updating Teensyduino and the libraries, it seems that it doesn't freeze anymore, but it fails to decode the DMX signal.

    Both the libraries are advertised as compatible with the LC, so I'm a bit at loss as to why this isn't working. Any clues?

  2. #2
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    118
    Hi, Gard! Im going to have a look at and re-test Teensy LC support for TeensyDMX.

    Could you tell me more about your system? For example:
    • How many channels are in each DMX frame?
    • What is the transmission rate?
    • Can you describe the electrical setup, for example, what kind of connectors and terminations are there, what kind of cabling, and how far is the transmitter from the Teensy LC?
    • For one of the minimal example programs included with TeensyDMX, do you still see the issue, or is it only when you incorporate DMX into a larger program?
    • Can you provide code for the smallest program that can reproduce this issue?


    Thanks!

  3. #3
    Junior Member
    Join Date
    Nov 2019
    Posts
    7
    Hi Shawn, thanks a lot for your reply!

    At the moment I'm using an ELC Checker DMX tester as the transmitter. I have also tried with a Madrix Plexus Artnet node, with the same results. In the final installation, we'll probably be using an MA Lighting 8-port node. I initially used the "max" transmission settings on the Checker (out of habit), but changed to "normal" and "safe" with the same result. The timings are:

    Normal:
    Rate: 33 ms
    Break: 200s
    MAB: 20s
    Ch2ch: 0s

    Safe:
    Rate: 40 ms
    Break: 500s
    MAB: 100s
    Ch2ch: 10s

    Max:
    Rate: 22.6ms
    Break: 88s
    MAB: 8s
    Ch2ch: 0s

    In all cases, all 512 channels are being sent.


    The electrical setup consists of the Teensy LC and a MAX3483 on a prototyping board. The DMX signal is connected via screw terminals very close to the transceiver. There is a .1F decoupling cap at the MAX' power pins. The wires from the terminals to the transceiver are short (10mm approx) and of equal length for the true and the inverted signal. For bench testing I'm using hook-up wire (not twisted pair, not shielded) to connect to the transmitter (3 wires, A/B/GND), and I'm not terminating the line. The wires to the trasmitter are approx 10 cm long. The output from the MAX looks fine, and an Arduino with a different DMX library accepts the TTL output from the MAX.

    The issue is there with the included example programs as well. Currently I'm using a very stripped-down version of the included receiver test:

    Code:
    #include <cstring>
    #include <TeensyDMX.h>
    
    namespace teensydmx = ::qindesign::teensydmx;
    
    // Create the DMX receiver on Serial1.
    teensydmx::Receiver dmxRx{Serial1};
    
    // The last value on the channel, for knowing when to print a change
    // (Example 1).
    uint8_t lastValue = 0;
    
    
    void setup() {
      // Serial initialization, for printing things
      Serial.begin(115200);
      while (!Serial && millis() < 4000) {
        // Wait for initialization to complete or a time limit
      }
      Serial.println("Starting BasicReceive.");
    
      // Turn on the LED, for indicating activity
      pinMode(LED_BUILTIN, OUTPUT);
      digitalWriteFast(LED_BUILTIN, HIGH);
    
    
      pinMode(2, OUTPUT);
      digitalWriteFast(2, HIGH);
    
      // Start the receiver
      dmxRx.begin();
    
      // Print the first values
      lastValue = dmxRx.get(1);
      Serial.printf("Channel 1: %d\n", lastValue);
    
    }
    
    void loop() {
    
    
      Serial.println(dmxRx.get(1));
      delay(50);
      
    }

    At the moment it just prints zeroes, regardless of what DMX values I transmit. Yesterday, it would stop transmitting altogether when a DMX source was connected – that does not seem to be happening now.

    Curiously, using a transmitter example sketch works perfectly …

  4. #4
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    118
    Im curious, what is pin 2 connected to? What does setting it to HIGH do?

  5. #5
    Junior Member
    Join Date
    Nov 2019
    Posts
    7
    That's the MAX' DE/_RE pin, and setting it high … enables the line driver and disables the receiver. Yeah. That's a bit embarassing.

    I changed it back to LOW, as it should be, and I'm now back at the behaviour where the serial output abruptly stops when a DMX source is connected.

    FWIW, to ensure that numptiness like this is not causing furither issues, I connected the MAX TTL out straigt to the RX pin of a Leonardo and made it control its built-in LED over DMX, so the transceiver is working and is now, again, in RX mode …
    Last edited by GardG; 11-16-2019 at 08:06 PM.

  6. #6
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    118
    Curious, does release v3.1.1 work for you?

  7. #7
    Junior Member
    Join Date
    Nov 2019
    Posts
    7
    !!!!!!!

    It does!!!

    I tried a random selection of older versions with varying degrees of luck earlier, but not 3.1.1 apparently.

    Ace – I think this should get me through for this project. I don't think missing out on the changes between 3.2.0 and 3.1.1 will be an issue for now. Thanks a lot for the help

  8. #8
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    118
    Thank you for helping narrow that down. If v3.2.0 is the culprit then clearly Ive introduced a problem. Ill dive in.

  9. #9
    Junior Member
    Join Date
    Nov 2019
    Posts
    7
    So the application for this is to control some SK6812 strips and a Xenon strobe (trigger triac on pin 13) – I'm almost there (but with a bit of of code cleanup remaining!), using a mash-up of the included DMX receiver and flasher examples, just one last hurdle besides cleaning it all up – it's reacting a bit slowly. The LED strip seems to refresh approx once a second, and there is a bit of delay in the strobe – and it's not maintaining a steady flash rate, it for 500-1000ms every few seconds.

    I'll look at this tomorrow (it's getting late here), but are there any glaringly obvious mistakes in this code that would slow things down?



    Code:
    #include <cstring>
    
    #include <TeensyDMX.h>
    #include <Adafruit_NeoPixel.h>
    
    #define PIX_PIN     17
    #define PIX_COUNT   120
    #define PIX_BRIGHT  255
    
    namespace teensydmx = ::qindesign::teensydmx;
    teensydmx::Receiver dmxRx{Serial1};
    
    Adafruit_NeoPixel strip(PIX_COUNT, PIX_PIN, NEO_GRBW + NEO_KHZ800);
    
    // Buffer in which to store packet data
    uint8_t packetBuf[120]{0};
    // The last values received
    uint8_t rgb[120]{0};
    
    constexpr int kChannel = 122; // strobe channel
    constexpr unsigned long kDMXTimeout = 1000;  // 1s
    constexpr uint8_t kLEDPin = LED_BUILTIN;
    
    // Constants for flashing rate
    constexpr int32_t kPeriodMax = 500;
    constexpr int32_t kPeriodMin = 50;
    uint8_t buf[1]; // Buffer used for reading the DMX data.
    uint8_t lastValue; // The last value received on kChannel.
    elapsedMillis lastFrameTimer; // Keeps track of when the last frame was received.
    
    // Flashing state
    int32_t period = kPeriodMax;
    int64_t phi = 0;
    
    void setup() {
      
      pinMode(2, OUTPUT);
      digitalWriteFast(2, LOW);
     
      strip.begin();
      strip.show();
      strip.setBrightness(PIX_BRIGHT);
    
      pinMode(kLEDPin, OUTPUT);
      digitalWriteFast(kLEDPin, LOW); 
    
      // Start the receiver
      dmxRx.begin();
      lastFrameTimer = kDMXTimeout;
    
    }
    
    void loop() {
     
      // A return of -1 means no data, and a value < 120 means that there was data,
      // but the received packet wasn't large enough to contain all chs
      int read = dmxRx.readPacket(packetBuf, 1, 120);
      if (read == 120) {
        if (memcmp(packetBuf, rgb, 120) != 0) {
          memcpy(rgb, packetBuf, 120);
    
          for(int i = 0; i < 120; i=i+4){
            for(int p = 0; p < 4; p++){
              strip.setPixelColor(i+p, strip.Color(rgb[i], rgb[i+1], rgb[i+2], rgb[i+3]));
            }
          }
          strip.show();      
        }
      }
      
      int readS = dmxRx.readPacket(buf, kChannel, 1);
      int64_t t = millis();
      if (readS > 0) {
        // We've read everything we want to
        lastValue = buf[0];
        lastFrameTimer = 0;
      }
    
    
      if (lastFrameTimer <= kDMXTimeout) {
        if (lastValue > 0 && lastValue < 7) {
          digitalWriteFast(kLEDPin, LOW);
        } else if (lastValue > 8 && lastValue < 14) {
          digitalWriteFast(kLEDPin, HIGH);
        } else if (lastValue > 15) {
            
            // Use a wave equation to make the speed-ups and slow-downs smoother using
            // the offset, phi
            int32_t newPeriod = map(lastValue, 0, 255, kPeriodMax, kPeriodMin);
            if (newPeriod != period) {
              phi = (t*(period - newPeriod) + newPeriod*phi)/period;
              period = newPeriod;
            }
    
            int32_t v = (t - phi)%period;
            if (v < period/2) {
              digitalWriteFast(kLEDPin, HIGH);
            } else {
              digitalWriteFast(kLEDPin, LOW);
            }
            
        
        } else {
          digitalWriteFast(kLEDPin, LOW);
        }
      }
    }

    FWIW, I've tried commenting out the LED strip parts (including the initialisation of the strip), but the strobe is still slow. And the LED strip was reacting slowly before added in the strobe part.

    I'll take a closer look at this tomorrow, and possibly start a new thread, just thought I'd ask before signing off for the night, just in case someone can easily see where the slowness comes from.

    (I'm well aware the code is a bit of a mess!)

  10. #10
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    118
    I've released a new version, v4.0.0-alpha. Let me know if this fixes the problem.
    Last edited by shawn; 11-16-2019 at 10:28 PM. Reason: Adding a link around the release name.

  11. #11
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    118
    A few notes on your code:
    1. To confirm, you're making every 4 pixels the same?
    2. Move the `strip.Color` call outside the inner loop because it doesn't depend on `p`.
    3. Call `readPacket` just once. For the first check, do `read >= 120` and for the second, you can dispense with `readS` and just do `read == 122` instead of `readS > 0`. Of course, you'd need the value of `packetBuf[121]` instead of `buf[0]`, and to increase the size of `packetBuf` to 122 instead of 120. This will avoid one internal lock by calling `readPacket` once instead of twice.
    4. Your strobe code will turn the light off when the value is 0, 7, 8, 14, or 15.
    5. For the strobe, do you want to map the values 15-255 to a period of 500-50ms, or do you want to map 0-255 to 500-50ms? Your current code does the second.
    6. I'm curious how long the strip-related code takes. Maybe collect some statistics around that block and print every second or so or something? Eg. measure the time before and after that block.
    7. DMX is only transmitted at up to around 44Hz, so that should rate limit how fast the strip is written to, but maybe try adding some code that doesn't allow strip update faster than some rate.
    8. I'm curious what a small delay at the end of the loop would do, eg. 20ms. And then alternately comment out the strip code and the strobe code, to see the effect. (For mathematical posterity, this value should divide "most" of the possible period values for the strobe since the strobe timer values are being polled in a loop and not hooked up to a timer.)

    Those are just some initial core notes.

    (And don't forget to try the new release; see the previous post, in case you're reading this and miss it.)
    Last edited by shawn; 11-17-2019 at 12:11 AM.

  12. #12
    Junior Member
    Join Date
    Nov 2019
    Posts
    7
    Thanks a lot for the feedback!

    - Every 4 pixels should be the same – I'm using 144 LED/m strip for the sake of output power, and grouping them together in bunches of 4 to save DMX channels.
    - The strobe trigger triac is connected to pin 13 through a high pass filter that makes it trigger on a rising edge. It's intended to work as follows:

    DMX 0-7: off
    DMX 8-14: single flash
    DMX 15-255: flash rate slow to fast

    I tried installing 4.0.0A, but it doesn't seem to work – haven't checked very closely, but the test code from my earlier post seems to only return zeroes. It does not seem to freeze, though.

    I made some changes to the code above, but nothing seems to have changed. Here are the current versions:


    Strobe only, no pixels:
    Code:
    #include <cstring>
    
    #include <TeensyDMX.h>
    
    namespace teensydmx = ::qindesign::teensydmx;
    teensydmx::Receiver dmxRx{Serial1};
    
    // Buffer in which to store packet data
    uint8_t packetBuf[122]{0};
    
    constexpr int kChannel = 122; // strobe channel
    constexpr unsigned long kDMXTimeout = 1000;  // 1s
    constexpr uint8_t kLEDPin = LED_BUILTIN;
    
    // Constants for flashing rate
    constexpr int32_t kPeriodMax = 500;
    constexpr int32_t kPeriodMin = 50;
    uint8_t buf[1]; // Buffer used for reading the DMX data.
    uint8_t lastValue; // The last value received on kChannel.
    elapsedMillis lastFrameTimer; // Keeps track of when the last frame was received.
    
    // Flashing state
    int32_t period = kPeriodMax;
    int64_t phi = 0;
    
    void setup() {
      
      pinMode(2, OUTPUT);
      digitalWriteFast(2, LOW);
    
      pinMode(kLEDPin, OUTPUT);
      digitalWriteFast(kLEDPin, LOW); 
    
      // Start the receiver
      dmxRx.begin();
      lastFrameTimer = kDMXTimeout;
    }
    
    void loop() {
      // return of -1 means no data
      int read = dmxRx.readPacket(packetBuf, 1, 122);
      
      int64_t t = millis();
      if (read == 122) {
        lastValue = packetBuf[121];
        lastFrameTimer = 0;
      }
    
      if (lastFrameTimer <= kDMXTimeout) {
        if (lastValue > 0 && lastValue < 8) {
          digitalWriteFast(kLEDPin, LOW);
        } else if (lastValue > 8 && lastValue < 15) {
          digitalWriteFast(kLEDPin, HIGH);
        } else if (lastValue > 15) {
            
            // Use a wave equation to make the speed-ups and slow-downs smoother
            int32_t newPeriod = map(lastValue, 15, 255, kPeriodMax, kPeriodMin);
            if (newPeriod != period) {
              phi = (t*(period - newPeriod) + newPeriod*phi)/period;
              period = newPeriod;
            }
    
            int32_t v = (t - phi)%period;
            if (v < period/2) {
              digitalWriteFast(kLEDPin, HIGH);
            } else {
              digitalWriteFast(kLEDPin, LOW);
            }
            
        } else {
          digitalWriteFast(kLEDPin, LOW);
        }
      }
    }

    Pixels only, no strobe:
    Code:
    #include <cstring>
    
    #include <TeensyDMX.h>
    #include <Adafruit_NeoPixel.h>
    
    #define PIX_PIN     17
    #define PIX_COUNT   120
    #define PIX_BRIGHT  40
    
    namespace teensydmx = ::qindesign::teensydmx;
    teensydmx::Receiver dmxRx{Serial1};
    
    Adafruit_NeoPixel strip(PIX_COUNT, PIX_PIN, NEO_GRBW + NEO_KHZ800);
    
    // Buffer in which to store packet data
    uint8_t packetBuf[122]{0};
    // The last values received
    uint8_t rgb[122]{0};
    
    void setup() {
      pinMode(2, OUTPUT);
      digitalWriteFast(2, LOW);
    
      strip.begin();
      strip.show();
      strip.setBrightness(PIX_BRIGHT);
    
      // Start the receiver
      dmxRx.begin();
    }
    
    void loop() {
     
      // return of -1 means no data
      int read = dmxRx.readPacket(packetBuf, 1, 122);
    
    
      // check if enough data for all LEDs was received
      if (read >= 120) {
        if (memcmp(packetBuf, rgb, 120) != 0) {
          memcpy(rgb, packetBuf, 120);
          // group LEDs in 4's
          for(int i = 0; i < 199; i=i+4){
            for(int p = 0; p < 4; p++){
              strip.setPixelColor(i+p, rgb[i], rgb[i+1], rgb[i+2], rgb[i+3]);
            }
          }
        }
    
      strip.show(); // refresh strip only if enough data was received
      }
    }

    Full version:
    Code:
    #include <cstring>
    
    #include <TeensyDMX.h>
    #include <Adafruit_NeoPixel.h>
    
    #define PIX_PIN     17
    #define PIX_COUNT   120
    #define PIX_BRIGHT  40
    
    namespace teensydmx = ::qindesign::teensydmx;
    teensydmx::Receiver dmxRx{Serial1};
    
    Adafruit_NeoPixel strip(PIX_COUNT, PIX_PIN, NEO_GRBW + NEO_KHZ800);
    
    // Buffer in which to store packet data
    uint8_t packetBuf[122]{0};
    // The last values received
    uint8_t rgb[122]{0};
    
    constexpr int kChannel = 122; // strobe channel
    constexpr unsigned long kDMXTimeout = 1000;  // 1s
    constexpr uint8_t kLEDPin = LED_BUILTIN;
    
    // Constants for flashing rate
    constexpr int32_t kPeriodMax = 500;
    constexpr int32_t kPeriodMin = 50;
    uint8_t buf[1]; // Buffer used for reading the DMX data.
    uint8_t lastValue; // The last value received on kChannel.
    elapsedMillis lastFrameTimer; // Keeps track of when the last frame was received.
    
    // Flashing state
    int32_t period = kPeriodMax;
    int64_t phi = 0;
    
    void setup() {
    
      Serial.begin(9600);
      
      pinMode(2, OUTPUT);
      digitalWriteFast(2, LOW);
    
      
      strip.begin();
      strip.show();
      strip.setBrightness(PIX_BRIGHT);
      
    
      pinMode(kLEDPin, OUTPUT);
      digitalWriteFast(kLEDPin, LOW); 
    
      // Start the receiver
      dmxRx.begin();
      lastFrameTimer = kDMXTimeout;
    
    }
    
    void loop() {
     
      // return of -1 means no data
      int read = dmxRx.readPacket(packetBuf, 1, 122);
    
    
      // check if enough data for all LEDs was received
      if (read >= 120) {
        if (memcmp(packetBuf, rgb, 120) != 0) {
          memcpy(rgb, packetBuf, 120);
          // group LEDs in 4's
          for(int i = 0; i < 199; i=i+4){
            for(int p = 0; p < 4; p++){
              strip.setPixelColor(i+p, rgb[i], rgb[i+1], rgb[i+2], rgb[i+3]);
            }
          }
        }
    
      strip.show(); // refresh strip only if enough data was received
      }
    
      
      int64_t t = millis();
      if (read == 122) {
        lastValue = packetBuf[121];
        lastFrameTimer = 0;
      }
    
    
      if (lastFrameTimer <= kDMXTimeout) {
        if (lastValue > 0 && lastValue < 8) {
          digitalWriteFast(kLEDPin, LOW);
        } else if (lastValue > 8 && lastValue < 15) {
          digitalWriteFast(kLEDPin, HIGH);
        } else if (lastValue > 15) {
            
            // Use a wave equation to make the speed-ups and slow-downs smoother
            int32_t newPeriod = map(lastValue, 15, 255, kPeriodMax, kPeriodMin);
            if (newPeriod != period) {
              phi = (t*(period - newPeriod) + newPeriod*phi)/period;
              period = newPeriod;
            }
    
            int32_t v = (t - phi)%period;
            if (v < period/2) {
              digitalWriteFast(kLEDPin, HIGH);
            } else {
              digitalWriteFast(kLEDPin, LOW);
            }
            
        
        } else {
          digitalWriteFast(kLEDPin, LOW);
        }
      }
    }

  13. #13
    Junior Member
    Join Date
    Nov 2019
    Posts
    7
    OK, that was another rather embarrasing mistake from my side. I had moved my test sending over to a lighting console, and had made some mistakes in the network setup there, so the signal only reached the DMX node at 5 Hz. I fixed that, and now it's super smooth. No wonder it felt a bit more responsive with the Checker than the console. I really need to get some proper sleep some time soon.

    Well, seems it works! With 3.1.1 at least – 4.0.0 still doesn't seem to do much.

    Thanks a lot for the help!

  14. #14
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    118
    I think I found the problem. See the new v4.0.0-alpha.1 release for a fix.

    I'm using bit 9 (the R8 bit of UARTx_C3) to validate that the first stop bit is high, and if it's low, I accumulate one framing error. On the Teensy LC, this bit always seems to return 0, even for the 8N2 mode (which uses bit 9 as the first stop bit). This check was changed to always return true for Teensy LC.

    Also, some side notes:
    1. Your code still sets the LED to LOW if the value is 0, 8, or 15. 8 does not do "single flash" and 15 does not do a slow rate.
    2. For clarity, may I suggest renaming `kChannel` and `lastChannel`?
    3. The `buf` variable can be deleted.
    Last edited by shawn; 11-17-2019 at 10:24 PM.

  15. #15
    Senior Member
    Join Date
    Mar 2017
    Location
    Oakland, CA, USA
    Posts
    118
    For posterity, Ill post the reason why R8 is zero on a Teensy LC. Its because theres a 2-stop-but mode and the check is not necessary, as for Teensy 3.2 and below. Same for Teensy 3.5 and 3.6. The latest fixes for all the Teensy boards are in v3.2.2. Release v4.0.0-alpha.1 has the fix for Teensy LC but not for Teensy 3.5 or 3.6.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •