0

Connecting multiple dotStar strips to a single Raspberry Pi
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Connecting multiple dotStar strips to a single Raspberry Pi

by abhi3188 on Wed Nov 08, 2017 5:31 pm

Hi,

Is it possible to connect multiple DotStar strips to a single raspberry pi? Normally I would just try this out but since they are a little expensive I wanted to find out before making a purchase.

Essentially if I have 4 strips, can I plug all 4 strips into my raspberry pi(each having its own data and clock pin) and then control each of them independently? I would ofcourse power each of the strips separately.

I'm imagining the code would look something like this:

Code: Select all | TOGGLE FULL SIZE
strip1 = Adafruit_DotStar(numpixels, datapin1, clockpin1);
strip2 = Adafruit_DotStar(numpixels, datapin2, clockpin2);
strip3 = Adafruit_DotStar(numpixels, datapin3, clockpin3);
strip4 = Adafruit_DotStar(numpixels, datapin4, clockpin4);

strip1.setPixelColor(10, color);
strip2.setPixelColor(11, color2);

strip1.show();
strip2.show();


Thanks

abhi3188
 
Posts: 39
Joined: Tue Sep 09, 2014 9:49 pm

Re: Connecting multiple dotStar strips to a single Raspberry

by adafruit_support_rick on Thu Nov 09, 2017 11:51 am

Yeah, that will work. But do you really need to have the strips separate? Why not just connect them as a single strip and control the pixel segments individually?

adafruit_support_rick
 
Posts: 35089
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Connecting multiple dotStar strips to a single Raspberry

by abhi3188 on Thu Nov 09, 2017 12:03 pm

I guess I'm trying to split it into modular chunks so each strip is easily replaceable if something goes wrong. Also the layout is such that there would be breaks in the strips and it wouldn't be possible to lay it out as one long strip.

However when you suggested that I control each segment individually, would the implementation you imagine be something like storing the start pixel and end pixel value of each segment and then only triggering animations between those two values of the strip you are addressing?

Also, are you aware of a node library that would let me control the strips over any gpio pins, like the python library? The dotstar library I found requires SPI which basically would restrict usage to only the MOSI & SCLK on the pi, which would mean one strip per pi.

Thanks

abhi3188
 
Posts: 39
Joined: Tue Sep 09, 2014 9:49 pm

Re: Connecting multiple dotStar strips to a single Raspberry

by adafruit_support_rick on Thu Nov 09, 2017 4:10 pm

The adafruit dotstar python library can use any pins - you're not limited to SPI:
https://github.com/adafruit/Adafruit_DotStar_Pi

Be aware that this software control of the pins is a quite a bit slower than hardware (SPI) control.

abhi3188 wrote:However when you suggested that I control each segment individually, would the implementation you imagine be something like storing the start pixel and end pixel value of each segment and then only triggering animations between those two values of the strip you are addressing?

Yes, that's it exactly.

abhi3188 wrote:I guess I'm trying to split it into modular chunks so each strip is easily replaceable if something goes wrong. Also the layout is such that there would be breaks in the strips and it wouldn't be possible to lay it out as one long strip.

well, you can have up to around 2 meters of wires between strip segments. Would that work? If you need longer distances, you can actually use individual dotstars as repeaters - that is, you can have up to 2 meters between individual dotstars. Just write 0,0,0 to the repeaters, and they'll stay dark.

adafruit_support_rick
 
Posts: 35089
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Connecting multiple dotStar strips to a single Raspberry

by nowens on Thu Mar 15, 2018 4:59 am

I've programmed two strips to drive a matrix of four Dotstar 16x16 part 2735, from a Raspberry Pi 3-B. Originally I drove all four on one chain but false colours were appearing on the last matrix, so I tried splitting the chain into two.

Code: Select all | TOGGLE FULL SIZE
from dotstar import Adafruit_DotStar as ds

datapin1   = 10
clockpin1  = 11
strip1     = ds(0, datapin1, clockpin1)
datapin2   = 23
clockpin2  = 24
strip2     = ds(0, datapin2, clockpin2)

strip1.begin()
strip2.begin()

# ... (build the data and drive the strips)


I get the following error at the second begin():

Code: Select all | TOGGLE FULL SIZE
SystemError: error return without exception set


Can I create two instances of Adafruit_Dotstar, or am I doing it wrong?

nowens
 
Posts: 7
Joined: Tue Jun 27, 2017 3:59 am

Re: Connecting multiple dotStar strips to a single Raspberry

by adafruit_support_rick on Fri Mar 16, 2018 11:29 am

Should work. Pin 23 or 24 may already be in use by some other process. Try it with a different pair of pins.

adafruit_support_rick
 
Posts: 35089
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Connecting multiple dotStar strips to a single Raspberry

by nowens on Sun Mar 18, 2018 9:49 am

Thanks, I think you're on the right lines; disabling SPI means 10 and 11 don't work, but without SPI, none of the pins work. Possibly I can't have a bufferless mode without SPI? I'll play with the drivers and see what I can do. Using a lower bitrate on SPI might be the answer; numpy can give me 200 frames a second and the Pi is barely cooking. If I don't get anywhere I'll raise another question on the forum - sorry for tagging onto this one, it was a bit cheeky.
Nick Owens

nowens
 
Posts: 7
Joined: Tue Jun 27, 2017 3:59 am

Re: Connecting multiple dotStar strips to a single Raspberry

by adafruit_support_rick on Sun Mar 18, 2018 5:35 pm

I didn't mean to disable SPI. Just use different pins than 23 and 24.
Your code is set up for software spi anyway.

adafruit_support_rick
 
Posts: 35089
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Connecting multiple dotStar strips to a single Raspberry

by nowens on Mon Mar 19, 2018 7:46 am

I've tried lots of pins and I get the error with everything but 10 and 11. The example strandtest program gives the same error. If I turn off SPI, pins 10 and 11 don't work either. I haven't had much time to put it all together so unless you have any sudden ideas, thanks for the help and I'll raise another question later if I need to.

nowens
 
Posts: 7
Joined: Tue Jun 27, 2017 3:59 am

Re: Connecting multiple dotStar strips to a single Raspberry

by nowens on Tue Mar 27, 2018 1:09 pm

The answer for me at least seems to be to slow the data rate down. 4Mbits/s works fine; 32Mbits/s was crazy, 8 was a bit noisy, 2 was getting too slow. That's one chain of 1024 Dotstar LEDs on pins 10 and 11 using hardware SPI. Now busy fixing the maths, not your problem!

nowens
 
Posts: 7
Joined: Tue Jun 27, 2017 3:59 am

Re: Connecting multiple dotStar strips to a single Raspberry

by nowens on Wed Mar 28, 2018 5:57 am

I fixed the maths, it wasn't the maths. I get a checkerboard pattern if I use raw writes with no pixel buffer, even if I deliberately specify a bytearray or numpy dtype('B').

Code: Select all | TOGGLE FULL SIZE
from dotstar import Adafruit_DotStar as ds
strip     = ds(0,4000000)
strip.begin()

while True:
   
    #build arrays red, green, blue (32x32) plus LEDref(1x1024) as a co-ordinate to index mapper

    datastream[0::4] = 0xFF
    datastream[1::4] = blue.flat[LEDref[::]]
    datastream[2::4] = red.flat[LEDref[::]]
    datastream[3::4] = green.flat[LEDref[::]]

    strip.show(datastream)


However if I use setPixelColor it works, and it's only marginally slower.

Code: Select all | TOGGLE FULL SIZE
from dotstar import Adafruit_DotStar as ds
strip     = ds(1024,4000000)
strip.begin()

while True:

    #build arrays red, green, blue (32x32) plus LEDref(32x32) as a co-ordinate to index mapper
    # LEDref is now 32x32 for my own sanity

    for col in range(0,31):
    for row in range(0,31):
        strip.setPixelColor(LEDref[col,row],red[col,row],green[col,row],blue[col,row])


nowens
 
Posts: 7
Joined: Tue Jun 27, 2017 3:59 am

Re: Connecting multiple dotStar strips to a single Raspberry

by adafruit_support_rick on Fri Mar 30, 2018 11:36 am

I don't really understand why the first method doesn't work. I'm not python expert, but it looks like you're simply building the same array that setPixelColor does. The clue might be in why the setPixelColor version is slower? Or do you think it's slower simply because of setPixelColor itself? That is, show() is the same speed for both?
I don't see anything in the library code to suggest why your method wouldn't work.

adafruit_support_rick
 
Posts: 35089
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: Connecting multiple dotStar strips to a single Raspberry

by nowens on Fri Mar 30, 2018 2:02 pm

I wish I knew; perhaps there is an issue with the raw write. Certainly I printed 'datastream' and it's all in the right order and as uint8, i.e. bytes. setPixelColor requires the library to copy the data into its own array, but it's only costing me about 5-10% less frames a second.
Here's the full code now it's working in case it helps someone. The Pi does all the maths and can run on its own but it listens for a TCP connection from a PC program that sets its parameters with a string delimited by # . You could equally set it to choose random colours, etc. and tweak them as it runs.

Code: Select all | TOGGLE FULL SIZE
import numpy as np
from tcpcom import TCPServer
import time
import os
import RPi.GPIO as GPIO
import random as rn
from dotstar import Adafruit_DotStar as ds

strip     = ds(1024,4000000)

# Alternate ways of declaring strip:
# strip   = Adafruit_DotStar(numpixels)           # Use SPI (pins 10=MOSI, 11=SCLK)
# strip   = Adafruit_DotStar(numpixels, 32000000) # SPI @ ~32 MHz
# strip   = Adafruit_DotStar()                    # SPI, No pixel buffer
# strip   = Adafruit_DotStar(32000000)            # 32 MHz SPI, no pixel buf
# See image-pov.py for explanation of no-pixel-buffer use.
# Append "order='gbr'" to declaration for proper colors w/older DotStar strips)

strip.begin()

np.set_printoptions(threshold=np.nan)

IP_PORT = 22000
scancount = 0
numberofseeds = 24
colour1 = 0
colour2 = 0
colour3 = 0
colour4 = 0
colour1blue = 0
colour2blue = 0
colour3blue = 0
colour4blue = 0
colour1red = 0
colour2red = 0
colour3red = 0
colour4red = 0
colour1green = 0
colour2green = 0
colour3green = 0
colour4green = 0

saturation = 0.0
brightness = 0.0
harmonics = 3
oldharmonics = 3
speed = 10.0
angmo = np.array(np.zeros(numberofseeds),dtype=float)
angle = np.array(np.zeros(numberofseeds),dtype=float)
ampangle = np.array(np.zeros(numberofseeds),dtype=float)
ampangmo = np.array(np.zeros(numberofseeds),dtype=float)
angacc = np.array(np.zeros(numberofseeds),dtype=float)
seedrow = np.array(np.zeros(numberofseeds),dtype=int)
seedcol = np.array(np.zeros(numberofseeds),dtype=int)
# make an array of 32 x 32 complex numbers set to zero
curseed=np.array(np.zeros((32,32)),dtype=complex)
freqs=np.array(np.zeros((32,32)),dtype=complex)
LEDref = np.array(
    [[752,751,720,719,688,687,656,655,624,623,592,591,560,559,528,527,496,495,464,463,432,431,400,399,368,367,336,335,304,303,272,271]
    ,[753,750,721,718,689,686,657,654,625,622,593,590,561,558,529,526,497,494,465,462,433,430,401,398,369,366,337,334,305,302,273,270]
    ,[754,749,722,717,690,685,658,653,626,621,594,589,562,557,530,525,498,493,466,461,434,429,402,397,370,365,338,333,306,301,274,269]
    ,[755,748,723,716,691,684,659,652,627,620,595,588,563,556,531,524,499,492,467,460,435,428,403,396,371,364,339,332,307,300,275,268]
    ,[756,747,724,715,692,683,660,651,628,619,596,587,564,555,532,523,500,491,468,459,436,427,404,395,372,363,340,331,308,299,276,267]
    ,[757,746,725,714,693,682,661,650,629,618,597,586,565,554,533,522,501,490,469,458,437,426,405,394,373,362,341,330,309,298,277,266]
    ,[758,745,726,713,694,681,662,649,630,617,598,585,566,553,534,521,502,489,470,457,438,425,406,393,374,361,342,329,310,297,278,265]
    ,[759,744,727,712,695,680,663,648,631,616,599,584,567,552,535,520,503,488,471,456,439,424,407,392,375,360,343,328,311,296,279,264]
    ,[760,743,728,711,696,679,664,647,632,615,600,583,568,551,536,519,504,487,472,455,440,423,408,391,376,359,344,327,312,295,280,263]
    ,[761,742,729,710,697,678,665,646,633,614,601,582,569,550,537,518,505,486,473,454,441,422,409,390,377,358,345,326,313,294,281,262]
    ,[762,741,730,709,698,677,666,645,634,613,602,581,570,549,538,517,506,485,474,453,442,421,410,389,378,357,346,325,314,293,282,261]
    ,[763,740,731,708,699,676,667,644,635,612,603,580,571,548,539,516,507,484,475,452,443,420,411,388,379,356,347,324,315,292,283,260]
    ,[764,739,732,707,700,675,668,643,636,611,604,579,572,547,540,515,508,483,476,451,444,419,412,387,380,355,348,323,316,291,284,259]
    ,[765,738,733,706,701,674,669,642,637,610,605,578,573,546,541,514,509,482,477,450,445,418,413,386,381,354,349,322,317,290,285,258]
    ,[766,737,734,705,702,673,670,641,638,609,606,577,574,545,542,513,510,481,478,449,446,417,414,385,382,353,350,321,318,289,286,257]
    ,[767,736,735,704,703,672,671,640,639,608,607,576,575,544,543,512,511,480,479,448,447,416,415,384,383,352,351,320,319,288,287,256]
    ,[768,799,800,831,832,863,864,895,896,927,928,959,960,991,992,1023,0,31,32,63,64,95,96,127,128,159,160,191,192,223,224,255]
    ,[769,798,801,830,833,862,865,894,897,926,929,958,961,990,993,1022,1,30,33,62,65,94,97,126,129,158,161,190,193,222,225,254]
    ,[770,797,802,829,834,861,866,893,898,925,930,957,962,989,994,1021,2,29,34,61,66,93,98,125,130,157,162,189,194,221,226,253]
    ,[771,796,803,828,835,860,867,892,899,924,931,956,963,988,995,1020,3,28,35,60,67,92,99,124,131,156,163,188,195,220,227,252]
    ,[772,795,804,827,836,859,868,891,900,923,932,955,964,987,996,1019,4,27,36,59,68,91,100,123,132,155,164,187,196,219,228,251]
    ,[773,794,805,826,837,858,869,890,901,922,933,954,965,986,997,1018,5,26,37,58,69,90,101,122,133,154,165,186,197,218,229,250]
    ,[774,793,806,825,838,857,870,889,902,921,934,953,966,985,998,1017,6,25,38,57,70,89,102,121,134,153,166,185,198,217,230,249]
    ,[775,792,807,824,839,856,871,888,903,920,935,952,967,984,999,1016,7,24,39,56,71,88,103,120,135,152,167,184,199,216,231,248]
    ,[776,791,808,823,840,855,872,887,904,919,936,951,968,983,1000,1015,8,23,40,55,72,87,104,119,136,151,168,183,200,215,232,247]
    ,[777,790,809,822,841,854,873,886,905,918,937,950,969,982,1001,1014,9,22,41,54,73,86,105,118,137,150,169,182,201,214,233,246]
    ,[778,789,810,821,842,853,874,885,906,917,938,949,970,981,1002,1013,10,21,42,53,74,85,106,117,138,149,170,181,202,213,234,245]
    ,[779,788,811,820,843,852,875,884,907,916,939,948,971,980,1003,1012,11,20,43,52,75,84,107,116,139,148,171,180,203,212,235,244]
    ,[780,787,812,819,844,851,876,883,908,915,940,947,972,979,1004,1011,12,19,44,51,76,83,108,115,140,147,172,179,204,211,236,243]
    ,[781,786,813,818,845,850,877,882,909,914,941,946,973,978,1005,1010,13,18,45,50,77,82,109,114,141,146,173,178,205,210,237,242]
    ,[782,785,814,817,846,849,878,881,910,913,942,945,974,977,1006,1009,14,17,46,49,78,81,110,113,142,145,174,177,206,209,238,241]
    ,[783,784,815,816,847,848,879,880,911,912,943,944,975,976,1007,1008,15,16,47,48,79,80,111,112,143,144,175,176,207,208,239,240]]
    ,dtype=int)
Amps = 0.0
AmpsPerBit = 0.00007843137
datastream = np.empty(4096,dtype=np.dtype('B'))
blue = np.array(np.zeros((32,32)),dtype=np.dtype('B'))
red = np.array(np.zeros((32,32)),dtype=np.dtype('B'))
green = np.array(np.zeros((32,32)),dtype=np.dtype('B'))
LastTime = 0.0


def pickseed(row,col,count):
    global ampangle,ampangmo,angmo,angacc,seedrow,seedcol,angle,curseed
    ampangle[count] = rn.uniform(0-(np.pi), (np.pi))
    ampangmo[count] = rn.uniform(-0.0025, 0.0025)
    angmo[count] = rn.uniform(0-(np.pi/10), (np.pi/10))
    angacc[count] = rn.uniform(-0.0025, 0.0025)
    seedrow[count] = row
    seedcol[count] = col
    angle[count] = rn.uniform(-1, 1)
    curseed[seedrow[count],seedcol[count]] = np.sin(ampangle[count])*np.cos(angle[count]) + 1j * np.sin(ampangle[count])*np.sin(angle[count])
   
def pickseeds():
    global harmonics
    for i in range(numberofseeds):
        pickseed(rn.randint(1, harmonics),rn.randint(1, harmonics),i)
   

def measure_temp():
    temp = os.popen("vcgencmd measure_temp").readline()
    return (temp.replace("temp=",""))


# routine run on TCP server status change
def onStateChanged(state, msg):
    global colour1,colour2,colour3,colour4,saturation,brightness,harmonics,speed,oldharmonics
    global colour1red,colour2red,colour3red,colour4red
    global colour1green,colour2green,colour3green,colour4green
    global colour1blue,colour2blue,colour3blue,colour4blue
    global scancount,angmo,angle,ampangle,ampangmo,curseed,seedrow,seedcol,numberofseeds,angacc,freqs
   
    if state == "LISTENING":
        print "Server:-- Listening..."
    elif state == "CONNECTED":
        print "Server:-- Connected to", msg
    elif state == "MESSAGE":
        #parse the message
        dummy,scol1,scol2,scol3,scol4,ssat,sbri,shar,sspe = msg.split("#",8)
        colour1 = int('0x'+scol1[2:8],16)
        colour2 = int('0x'+scol2[2:8],16)
        colour3 = int('0x'+scol3[2:8],16)
        colour4 = int('0x'+scol4[2:8],16)
        colour1blue = colour1 & 0xFF
        colour2blue = colour2 & 0xFF
        colour3blue = colour3 & 0xFF
        colour4blue = colour4 & 0xFF
        colour1red = (colour1 & 0xFF00) >> 8
        colour2red = (colour2 & 0xFF00) >> 8
        colour3red = (colour3 & 0xFF00) >> 8
        colour4red = (colour4 & 0xFF00) >> 8
        colour1green = (colour1 & 0xFF0000) >> 16
        colour2green = (colour2 & 0xFF0000) >> 16
        colour3green = (colour3 & 0xFF0000) >> 16
        colour4green = (colour4 & 0xFF0000) >> 16
        saturation = float(ssat) / 5.0
        brightness = float(sbri) / 5.0
        harmonics = int(shar)
        speed = float(sspe) / 100.0
        if harmonics <> oldharmonics:
            curseed = np.array(np.zeros((32,32)),dtype=complex)
            pickseeds()
            oldharmonics = harmonics
    elif state == "PORT_IN_USE":
        print "Server:-- Port in use", msg
    elif state == "TERMINATED":
        print "Server:--Connection terminated", msg

pickseeds()

#Start and run the server in the background
server = TCPServer(IP_PORT, stateChanged = onStateChanged)

while True:
    # trigger a frame
    freqs = np.fft.fft2(curseed)
    freqs.real = (freqs.real * saturation + brightness)
    freqs.imag = (freqs.imag * saturation + brightness)
    chroma1 = np.maximum(0,freqs.real)
    chroma2 = np.maximum(0,freqs.imag)
    chroma3 = np.maximum(0,0-freqs.real)
    chroma4 = np.maximum(0,0-freqs.imag)
    blue = np.minimum(0xFF,((colour1blue * chroma1)
        + (colour2blue * chroma2)
        + (colour3blue * chroma3)
        + (colour4blue * chroma4))).astype(np.dtype('B'))
    red = np.minimum(0xFF,((colour1red * chroma1)
        + (colour2red * chroma2)
        + (colour3red * chroma3)
        + (colour4red * chroma4))).astype(np.dtype('B'))
    green = np.minimum(0xFF,((colour1green * chroma1)
        + (colour2green * chroma2)
        + (colour3green * chroma3)
        + (colour4green * chroma4))).astype(np.dtype('B'))
   
    # display the frame
    for col in range(0,31):
        for row in range(0,31):
            strip.setPixelColor(LEDref[col,row],red[col,row],green[col,row],blue[col,row])
   
    Amps = ((blue.sum() + red.sum() + green.sum()) * AmpsPerBit)

    if Amps < 5:
        strip.show()
   

    # Rotate
    for i in range(numberofseeds):
        angmo[i] += angacc[i]
        angle[i] += (np.cos(angmo[i])*speed)
        ampangle[i] += ampangmo[i]
        curseed[seedrow[i],seedcol[i]] = np.sin(ampangle[i])*np.cos(angle[i]) + 1j * np.sin(ampangle[i])*np.sin(angle[i])
   
    scancount += 1
    if scancount >= 200:
        scancount = 0
        print measure_temp(), time.time() - LastTime, Amps
        LastTime = time.time()
        for i in range(numberofseeds):
            if rn.randint(0,10) == 9:
                ampangmo[i] = rn.uniform(-0.0025, 0.0025)
                angmo[i] = rn.uniform(0-(np.pi/10), (np.pi/10))
                angacc[i] = rn.uniform(-0.0025, 0.0025)


nowens
 
Posts: 7
Joined: Tue Jun 27, 2017 3:59 am

Re: Connecting multiple dotStar strips to a single Raspberry

by nowens on Thu Sep 27, 2018 3:45 am

Oops. Error in the chroma lines making colours 3 and 4 biased the wrong way by the brightness value. Now all I have to do is bias the random choice of array element so it chooses 1,1 less (which makes diagonal stripes).

freqs.real = (freqs.real * saturation)
freqs.imag = (freqs.imag * saturation)
chroma1 = np.maximum(0,freqs.real + brightness)
chroma2 = np.maximum(0,freqs.imag + brightness)
chroma3 = np.maximum(0,brightness - freqs.real)
chroma4 = np.maximum(0,brightness - freqs.imag)

nowens
 
Posts: 7
Joined: Tue Jun 27, 2017 3:59 am

Please be positive and constructive with your questions and comments.