firmata / ConfigurableFirmata

A plugin-based version of Firmata
GNU Lesser General Public License v2.1
149 stars 71 forks source link

Feature Request: Neopixels/ws2812 support #166

Open eadmaster opened 2 weeks ago

eadmaster commented 2 weeks ago

an implementation for standard firmata is here based on Adafruit_NeoPixel lib.

As alternative, FastLED also supports ws2812 leds.

Related: https://github.com/firmata/arduino/issues/167

pgrawehr commented 2 weeks ago

It shouldn't be too hard to include the linked code as module in ConfigurableFirmata. That would first need an API proposal at https://github.com/firmata/protocol, as the link above seems to make an unofficial protocol extension.

Also, I don't think it's needed at all. WS2812 uses SPI to communicate with the strip, so you can just use the current version of ConfigurableFirmata (which does support SPI) and use some WS2812 driver on top of it (such as this one).

eadmaster commented 2 weeks ago

WS2812 uses SPI to communicate with the strip, so you can just use the current version of ConfigurableFirmata (which does support SPI)

It seems a good alternative, but i still cannot get it to work this way:


import pyfirmata
import time

board = pyfirmata.Arduino('/dev/ttyUSB0')

# Initialize SPI on the Arduino
board.send_sysex(pyfirmata.START_SYSEX, [0x42, 0x00])  # SPI_BEGIN
board.send_sysex(pyfirmata.START_SYSEX, [0x42, 0x02, 0x00])  # SPI_SET_CLOCK, Clock Divider: 0 (4MHz)
board.send_sysex(pyfirmata.START_SYSEX, [0x42, 0x04, 0x00])  # SPI_SET_BIT_ORDER, MSBFIRST
board.send_sysex(pyfirmata.START_SYSEX, [0x42, 0x06, 0x00])  # SPI_SET_DATA_MODE, SPI_MODE0

def spi_write(data):
    message = [0x42, 0x08] + data  # SPI_TRANSFER command
    board.send_sysex(pyfirmata.START_SYSEX, message + [pyfirmata.END_SYSEX])

def set_pixel_color(red, green, blue):
    prefix = [0x00]  # NeoPixel start frame marker
    pixel_data = [red, green, blue]
    suffix = [0x00]  # NeoPixel end frame marker
    data = prefix + pixel_data + suffix
    spi_write(data)

# Example usage: setting the first pixel to red
print("red")
set_pixel_color(255, 0, 0) 
time.sleep(10)
board.exit()

EDIT: for context: a single WS2812 led is connected to pin "IO23" (SPI MOSI) on the Wemos D1 R32 board. ConfigurableFirmata v3.2.0 was flashed on the board with SPI enabled.

pgrawehr commented 2 weeks ago
board.send_sysex(pyfirmata.START_SYSEX, [0x42, 0x00])  # SPI_BEGIN
board.send_sysex(pyfirmata.START_SYSEX, [0x42, 0x02, 0x00])  # SPI_SET_CLOCK, Clock Divider: 0 (4MHz)
board.send_sysex(pyfirmata.START_SYSEX, [0x42, 0x04, 0x00])  # SPI_SET_BIT_ORDER, MSBFIRST
board.send_sysex(pyfirmata.START_SYSEX, [0x42, 0x06, 0x00])  # SPI_SET_DATA_MODE, SPI_MODE0

Where did you find these command sequences? According to https://github.com/firmata/protocol/blob/master/spi.md SPI_DATA is 0x68, not 0x42. Thus your command sequence should begin like

START_SYSEX, 0x68, 0x0, 0x0, END_SYSEX
START_SYSEX, 0x68, 0x01, 0x0 (depending on CS line to use), 0x8, ....

And also the pixel_data you send can't work that way, because you need use 7-bit data mode (the firmata protocol considers the MSB of every byte sent as start of a command character. For SPI transfer, that means that you have to convert every byte sent either into two bytes (with the first byte containing the top bit of the data byte) or, when the encoding bit is set (bit 3 of byte 4 of the DATA_MODE command) use packed encoding (pack 7 bytes into 8 transfer bytes).

eadmaster commented 2 weeks ago

sorry, still not working, here it is my updated code. The bitwise manipulation needed seems pretty tricky. So i've switched to this StandardFirmata fork which is much easier to use.

pgrawehr commented 2 weeks ago

sorry, still not working, here it is my updated code. The bitwise manipulation needed seems pretty tricky. I would expect that pyfirmata does that for you, but unfortunately, that doesn't seem to be maintained any more. I don't have control over that. You probably have to do some debugging (on both the server and the board side) to make sure the commands are properly understood. So i've switched to this StandardFirmata fork which is much easier to use. The commands for your particular case seem easier, but I doubt you'll get it to work nicely without debugging the commands either.