0

I am trying to control a strip of WS2801 LEDs through an arduino uno running firmata from Python on my laptop.

I've written some basic code based on adafruit's library here (from line 187):

from pymata_aio.pymata3 import PyMata3
from pymata_aio.constants import Constants
import time

leds = [(255, 255, 0), (255, 255, 0), (0, 255, 255)]

uno = PyMata3()

uno.set_pin_mode(2, Constants.OUTPUT)  # signal
uno.set_pin_mode(3, Constants.OUTPUT)  # clock


def show(leds):
    for pixel in leds:
        for channel in pixel:
            i = 0b1
            for x in range(0,8):
                if i & channel:
                    uno.digital_write(2, 1)
                else:
                    uno.digital_write(2, 0)
                uno.digital_write(3, 1)
                uno.digital_write(3, 0)

                i = i << 1

    uno.digital_write(3, 0)
    time.sleep(0.001)

When I run the code it seems to run ok, but only the first LED will light up. I think (too quick to be certain) that it flashes through all the colours - ie displaying each colour rather than passing them through.

I'm wondering if there is something wrong with the timing for the clock pin making it latch, but I'm grasping here.

0

You cannot do precise timing of I/O mediated by USB like that. Regardless of the serial baud rate in use, the USB mode utilized basically puts a 1ms minimum interval between any two distinct communication operations.

Providing there are no intervening reads, if firmata is coded to deliver its data to the host's serial API in buffers matching or exceeding the USB packet size rather than individual writes for each command, it is possible that "keeping the pipe full" may mean that commands are able to be executed without such gaps, but any little interruption could introduce a problematic delay.

The key problem with delay is this part of the data sheet:

CLK pin keeps low more than 500uS will make the WS2801 internal status register reset, and at this moment, the gray scale data in the data shift registers will be latched.

Basically, that says that you need a clock low pulse of more than a half millisecond to start the process, but by extension it also means that all of the subsequent clock pulses have to be shorter than that or you will inadvertently reset the process, and this is not something you can easily achieve reliability of with a USB bus of the type utilized here mediating your software manipulation of the clock line.

Essentially, don't try to bit-bang things with Firmata. You should send the desired intensity values across the USB as byte data, and then have software running on the Uno clock this data out in accordance with the WS2801 timing requirements.

Personally, I'd just write that as an ordinary sketch reading the Serial input - no need for firmata. You can still feed the data from python on the host if you like, or anything else that can push data over a serial port.

  • Thanks, that's great. I thought that would probably be the case, and I'd already thought I might have to make my own protocol for controlling the LEDs. I'm very new to programming, so its great to have my instincts confirmed. – angusprune May 20 '16 at 20:09
  • I'd offer you my protocol but it's designed to be used over raw USB rather than a serial connection. – Ignacio Vazquez-Abrams May 21 '16 at 0:30
  • Yes, raw USB can actually make things simpler as you can use the start of each packet as an alignment marker. With a plain serial port, you have to come up with some sort of in-band (or long-timeout) synchronization method, reserved code with escapes, or just escape everything by encoding it in readable hex, and use newlines... – Chris Stratton May 21 '16 at 0:36
  • @IgnacioVazquez-Abrams , that would be really useful. I'm kinda fumbling my way through this (and really enjoying it when I'm not frustrated and angry at it). So any help would be fantastic. – angusprune May 21 '16 at 11:08
  • @angusprune: I don't actually have the protocol formally documented anywhere, but here's the Python script that I use to control the device.USB_SET_LED_COUNT tells the device to allocate an internal buffer to hold the LED colors. USB_SET_LED_RGB takes an index and color and sets a buffer entry. USB_UPDATE_LEDS tells the device to transfer the colors from the buffer to the LEDs. – Ignacio Vazquez-Abrams May 22 '16 at 1:10
0

It's possible that time.sleep is causing a latching issue. You should instead use pymata-aio's sleep function:

uno.sleep(0.001)

The documentation for it can be found here: http://htmlpreview.github.io/?https://github.com/MrYsLab/pymata-aio/blob/master/documentation/html/pymata_aio.html#module-pymata_aio.pymata3

You could also try a different pin and see if you still have the issue?

  • I think Chris' answer hits the nail on the head. I'll have a go at writing a protocol to transfer the data rather than bit banging directly. – angusprune May 20 '16 at 20:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.