Getting back into the some more learning to do! Starting back with RGB

eagletalontimeagletalontim Posts: 1,384
edited 2019-02-10 - 08:03:04 in Propeller 1
Been quite some time since I have posted here and now I am quite rusty! Not sure what all has changed, but I had some old PCB's with the Prop soldered to them and started dabbling with SPIN code again. Never could grasp Assembly :(

A worker of mine is interested in doing an RGB or ARGB sound activated controller. After doing a bit of research, I have located an interesting IC...the MSGEQ7 which apparently is an amazing IC and can easily split the spectrum of an audio signal to 7 different spectrums. I really don't need all 7, but hey, if they are offered, why not use them. We would like to have the RGB change color based on the spectrum, not volume.

Problem I have is properly communicating with the MSGEQ7 from the Prop. I really suck at fully understanding datasheets and putting the information in them to proper use. I can see there are 2 pins that are Reset and Strobe which I believe are the signal needed from the Prop to get data out from the MSGEQ7.

So from what I gather is I need to do something like? :
        _clkmode = xtal1 + pll16x                                               'Standard clock mode * crystal frequency = 80 MHz
        _xinfreq = 5_000_000

  Pst    : "Parallax Serial Terminal"  ' to be used for testing
  adc : "adc0831_fast"

  LONG adcArray[6]      ' Array to hold spectrum data from ADC
  BYTE i

PUB main
  dira[reset_pin]~~      ' set to output
  dira[strobe_pin]~~    ' set to output
  outa[reset_pin] := 0  ' ensure pin is off
  outa[strobe_pin] := 0  ' ensure pin is off

  adc.start(cs, clk, data, sample_rate, @adcValue)
  pause(10)    '  Let ADC initialize

  ' initialize MSGEQ7
  outa[reset_pin] := 1
  outa[strobe_pin] := 1
  outa[strobe_pin] := 0
  outa[reset_pin] := 0

  i := 0
    outa[strobe_pin] := 1
    outa[strobe_pin] := 0
    pause(2)    '  allow time for ADC to update?
    adcArray[i] := adcValue
    if i == 7  ' array 0 to 6 for 7 spectrum data
      i := 0

So none of this code is tested at all since I have not bought the MSGEQ7 just yet. This is off the top of my head :P Maybe there is a better option out there for what I am trying to do :) I seen another project using the same IC, but there is no code to go by.


  • That's an interesting chip to play with. The code you have so far seems reasonable for controlling the chip and converting the outputs to digital. Now you need to come up with a plan to convert those readings to intensity values for the RGB leds. Do keep in mind that the output readings are going to be proportional to the volume (voltage) of each part of the spectrum so if you use those values to control the intensities of the RGB leds the intensity of the individual R, G, or B leds will vary with the voltage from that frequency band, and the resulting color will vary with the ratio between those voltages.
    In science there is no authority. There is only experiment.
    Life is unpredictable. Eat dessert first.
  • eagletalontimeagletalontim Posts: 1,384
    edited 2019-02-10 - 17:15:37
    I was reading that too which is going to be another interesting problem to over come as the input level will more than likely be adjusted. Not sure if I can do a software filter or if I need to use some sort of other hardware method to avoid this issue. My thoughts are to adjust the input filtering on Pin 5 (IN) to Peak the ADC value (255) with the volume at the highest listening level, then use software to get the average of all 7 of the spectrum values or use the Peak signal to display the RGB color.
    BYTE rgbColor
    BYTE R_intensity
    BYTE G_intensity
    BYTE B_intensity
    PUB getAverage(the_array, sample_count) | i, arraysum  ' May not use this function
        arraysum := 0
        repeat i from 0 to sample_count
          arraysum += long[the_array][i]
        return arraysum / sample_count
    PUB getPeak(the_array, sample_count) | i, peak    ' get most dominant spectrum.
        arraysum := 0
        peak := 0
        repeat i from 0 to sample_count
          if peak =< long[the_array][i]
            peak := long[the_array][i]
        return i  ' may also use peak instead of i
    PUB calculateRGB | peak, peak_val, addto, i
      peak := getPeak(@adcValue, 7)
      peak_val := long[@adcValue][peak]  ' not sure if this line would work
      addto := 255 - peak_val  ' figure amount to add to other values to increase Volume.  This might cause problems...
      repeat i from 0 to 2  ' R - G - B
        case i
            IF peak == 0 OR peak == 1
              R_intensity := long[@adcValue][peak] + addto
              R_intensity := long[@adcValue][0] + addto   ' default if not peaking in this range
            IF peak == 2 OR peak == 3 OR peak == 4
              G_intensity := long[@adcValue][peak] + addto
              G_intensity := long[@adcValue][3] + addto   ' default if not peaking in this range
            IF peak == 5 OR peak == 6
              B_intensity := long[@adcValue][peak] + addto
              B_intensity := long[@adcValue][6] + addto   ' default if not peaking in this range

    This code has not been tested either, but should represent the idea. If the "peak" is in the low range, Red will light up more intense than the rest of the ranges.

  • ...... If the "peak" is in the low range, Red will light up more intense than the rest of the ranges.

    My guess is that the average peak value for each of the 7 bands will vary quite a bit, so getting a nice display may not be so easy, but since it can be done with code instead of hardware you can do a lot of experimenting. Using an array of 2811/2812 led strips with the right software would make for an awesome display. Imagine using some of the bands for color and one or two for shapes or led addressing on an xy array of leds.
    In science there is no authority. There is only experiment.
    Life is unpredictable. Eat dessert first.
  • eagletalontimeagletalontim Posts: 1,384
    edited 2019-02-25 - 05:05:01
    So for an update... Got the MSGEQ7 chips in and got it all working :) Also have a 16ft ARGB strip dancing to the music, but I have a slight dilemma on processing speed for a really long strip... 500 + LEDs.

    Thanks to JonnyMac's code in the obex, I was able to get the strip working well with 150 LED's, but if I stretch out further, my array shift slows everything way down. The array shift is to give the appearance of the LED's traveling down to the end.

    Here is the bit of code that is slowing everything down...
    PUB Showem | i
          repeat i from STRIP_LEN to 1  ' <<<<<  This is SUPER slow if STRIP_LEN is set above 500.
            pixbuf1[i] := pixbuf1[i - 1]
          strip.use(@pixbuf1, STRIP_LEN, LEDS, PIX_BITS)

    Is it possible to speed this up? I could not figure how to simply shift each value in an array up one and the last one "fall off"
  • What, specifically, are you trying to accomplish? On a project I did last year I had a situation that required a crawling rainbow. Creating this effect on-the-fly was very slow, so I ended up pre-calculating the buffer with extra segments. When the program runs I simply manipulate the start-of-buffer pointer to create the effect.
    Jon McPhalen
    Hollywood, CA
    It's Jon or JonnyMac -- please do not call me Jonny.
  • The "Showem" is running in it's own COG which takes the pixbuf1 array and shifts each value up one to the next index, then when that is complete, display it to the LED strip.

    pixbuf1[0] is updated from the calculated value of the spectrum from the MSGEQ7 which is done in the "Main" loop.

    If the strip is 150 LED's long, it is quick and displays correctly even if I add a 25ms delay in the "time.pause". If I change STRIP_LEN to 600 LED's, the display of the LED's "running" down the strip slows WAY down. This leads me to believe the first part that shifts each index value up to the next index in pixbuf1 is what slows this down quite a bit.

    I was thinking I would probably need to make another COG run that will just update the array, but that seems like a waste of a cog if there is another way to do this.
  • JonnyMacJonnyMac Posts: 6,212
    edited 2019-02-25 - 22:38:34
    I did a quick test and I think longmove() will work for you. Try this:
      longmove(@pixbuf[1], @pixbuf[0], STRIP_LEN-1)
    This is the code I used to test.
      buffer        long    0
                    long    1
                    long    2
                    long    3
                    long    4 { end of buffer }
                    long    0
    pub main | idx                                                         
      longmove(@buffer[1], @buffer[0], 4)
      repeat idx from 0 to 5
    The output is:
    0 <-- this value is after buffer
    Jon McPhalen
    Hollywood, CA
    It's Jon or JonnyMac -- please do not call me Jonny.
  • Rather than shift the entries in the buffer, move a pointer into the buffer? You'd need to extend the buffer
    and handle wrapping, but the cost of "shifting" goes away.
  • eagletalontimeagletalontim Posts: 1,384
    edited 2019-02-28 - 19:39:17
    Got a pretty nice display working :). Thank you JonnyMac for the last bit of code. That worked perfectly!
  • Wow, very nice. How many leds did you eventually end up driving? And can you share the code? It would be interesting to see.
    San Mateo, CA
  • I am currently driving 600 total, but am driving them down the middle of the chain to give the "burst" effect. I will have to post the code later as I am on my phone right now. Got to clean some of it up too... Lol. It has been a long time since I have coded in SPIN.....
Sign In or Register to comment.