V3.16 to V3.18 Breaking Changes?

Hello!

I’ve been having a wonderful time working with the PixelBlaze V3, building some custom controllers for wearable LED suits for an outdoor event coming up.

This evening, I ran into a little snag. I’ve been writing all my code on one PixelBlaze (firmware 3.16), and tonight for the first time I went to install it on a second one (firmware 3.18)… to no avail. I still get “Looks OK, pattern is live!” though there’s nothing actually being displayed on the pixels.

So my question is - are there any known code-breaking changes between 3.16 and 3.18? Full code is below, but it’s ~600 lines… a bit of a doozy.

I’ve tried copying and pasting code between the two extant PixelBlazes, as well as exporting and opening the pattern, same result. The new 3.18 PixelBlaze will run a demo pattern just fine (e.g. by hitting New Pattern) so I know I have the pixel type right, decent power etc.

It’s possible there’s something other than the firmware difference causing this, that’s just what I noticed.

var USEFAKEDATA = true
var USESMALLDATA = true

var MODECOUNT = 18
pixelCount = 60

//This will hold all of our imported DMX data
export var frequencyData = array(32)

//These are the variables our DMX data will be translated into
//in the beforeRender function
export var patternSelect = -1
export var patternIndex = -1
export var color1 = array(3)
export var color2 = array(3)
export var color3 = array(3)
export var speed = -1
export var masterBrightness = -1
export var blockSize = -1
export var reversedData = 0
export var rev = 1

var previousPattern = -1

//The final output RGB values in some situations
var _R, _G, _B

//pattern specific variables:
export var pulsePosition = 0

var NUMTWINKLIES = 10
var MAXTWINKLIES = pixelCount
var twinkliePos = array(MAXTWINKLIES)
var twinklieVal = array(MAXTWINKLIES)

var values = array(100)
var fadeSpeed = array(100)

//beforeRender and Render functions:
beforeRenders = array(MODECOUNT)
renderers = array(MODECOUNT)


export function beforeRender(delta) {
  if (!USEFAKEDATA){
    patternSelect = frequencyData[0]<<8
    masterBrightness = frequencyData[1]<<8
    if (!USESMALLDATA){
    speed = frequencyData[2] << 8
    blockSize = round((frequencyData[3] << 8)*10)
    reversedData = frequencyData[4] << 8
    color1[0] = frequencyData[5]<<8
    color1[1] = frequencyData[6]<<8
    color1[2] = frequencyData[7]<<8
    color2[0] = frequencyData[8]<<8
    color2[1] = frequencyData[9]<<8
    color2[2] = frequencyData[10]<<8
    color3[0] = frequencyData[11] << 8
    color3[1] = frequencyData[12] << 8
    color3[2] = frequencyData[13] << 8
    }
    else {
      speed = .2
      blockSize = 1
      reversedData = 1
      color1[0] = 1
      color1[1] = 0
      color1[2] = 0
      color2[0] = 0
      color2[1] = 1
      color2[2] = 0
      color3[0] = 0
      color3[1] = 0
      color3[2] = 1
    }
    
    
    if (reversedData > .5) rev = -1
    else rev = 1
    
    patternIndex = floor(patternSelect*MODECOUNT)
  }
  else {
    fakeData();
  }
  
  if (patternIndex != previousPattern){
    transition(patternIndex)
    previousPattern = patternIndex
  }
  
  beforeRenders[patternIndex](delta)
  
}

export function render(index) {
  if (patternSelect == -1){_R = 0; _B = 0; _G=0}
  else renderers[patternIndex](index)
}

function transition(newPattern){
  if (newPattern == 11){
    for (i = 0; i < NUMTWINKLIES; i++){
      twinkliePos[i] = round(random(pixelCount))
      twinklieVal[i] = random(1)
    }
  }
  
  else if (newPattern == 12){
    twinkliePos[i] = round(random(pixelCount))
    twinklieVal[i] = random(1)
  }
  
  else if (newPattern == 13){
    twinkliePos[i] = round(random(pixelCount))
    twinklieVal[i] = random(2)
  }
  
  
  else if (newPattern == 16){
    for (i = 0; i < pixelCount; i++){
      values[i] = 5
      fadeSpeed[i] = random(2)*random(3)+.7
    }
  }
  
  else if (newPattern == 17){
    for (i = 0; i < pixelCount; i++){
      values[i] = 0
      fadeSpeed[i] = random(2)*random(5)+.5
    }
  }
}

beforeRenders[0] = (delta) => {}

renderers[0] = (index) =>{
  rgb(0,0,0)
}

//solid color
beforeRenders[1] = (delta) => {}

renderers[1] = (index) => {
    var intensityScale = .7
    _R = color1[0] * masterBrightness * intensityScale
    _G = color1[1] * masterBrightness * intensityScale
    _B = color1[2] * masterBrightness * intensityScale
    rgb(_R, _G, _B)
}

var _colorSelect
var _selector

//2 color pulse
beforeRenders[2] = (delta) => {
  _selector = wave(mod(time(speed*speed*2)+.125, 1))
  if (_selector < .5){
    _colorSelect = 0
  }
  else {
    _colorSelect = 1
  }
}

renderers[2] = (index) => {
  if (_colorSelect == 0){
    _R = color1[0] * masterBrightness * wave(time(speed*speed))
    _G = color1[1] * masterBrightness * wave(time(speed*speed))
    _B = color1[2] * masterBrightness * wave(time(speed*speed)) 
  }
  else {
    _R = color2[0] * masterBrightness * wave(time(speed*speed))
    _G = color2[1] * masterBrightness * wave(time(speed*speed))
    _B = color2[2] * masterBrightness * wave(time(speed*speed)) 
  }
   rgb(_R, _G, _B)
}

//3 color pulse
beforeRenders[3] = (delta) => {
  _selector = mod(time(speed*speed*3)+.0833, 1)
  if (_selector < .333){
    _colorSelect = 0
  }
  else if (_selector < .667) {
    _colorSelect = 1
  }
  else {
    _colorSelect = 2
  }
}

renderers[3] = (index) => {
    if (_colorSelect == 0){
    _R = color1[0] * masterBrightness * wave(time(speed*speed))
    _G = color1[1] * masterBrightness * wave(time(speed*speed))
    _B = color1[2] * masterBrightness * wave(time(speed*speed)) 
  }
  else if (_colorSelect == 1){
    _R = color2[0] * masterBrightness * wave(time(speed*speed))
    _G = color2[1] * masterBrightness * wave(time(speed*speed))
    _B = color2[2] * masterBrightness * wave(time(speed*speed)) 
  }
  else {
    _R = color3[0] * masterBrightness * wave(time(speed*speed))
    _G = color3[1] * masterBrightness * wave(time(speed*speed))
    _B = color3[2] * masterBrightness * wave(time(speed*speed)) 
  }
   rgb(_R, _G, _B)
}

//Rainbow Chase
beforeRenders[4] = (delta) => {}

renderers[4] = (index) => {
  var intensityScale = .7
  hsv(time(speed*speed*rev/blockSize)+(index*blockSize)/pixelCount, 1, intensityScale * masterBrightness)
}

//1 Color Meteor
beforeRenders[5] = (delta) => {
  if (rev > 0) pulsePosition = (pulsePosition + (delta/400)/(speed*speed)) % (pixelCount * 2)
  else{
    pulsePosition = (pulsePosition - (delta/400)/(speed*speed))
    if (pulsePosition <= -pixelCount) pulsePosition = pixelCount
  }
}

renderers[5] = (index) => {
  if (rev > 0){
    //if (index == floor(pulsePosition)) rgb(1,1,1)
    if (index < pulsePosition && index > pulsePosition - pixelCount/2){
      var relative = (pulsePosition - index)/(pixelCount/2)
      blend(color1[0],color1[1],color1[2],color2[0], color2[1], color2[2], relative, masterBrightness)
    }
    else if (index < pulsePosition - pixelCount/2 && index > pulsePosition - pixelCount){
      var relative = (pulsePosition - index - pixelCount/2) / (pixelCount/2)
      blend(color2[0], color2[1], color2[2],0,0,0,relative, masterBrightness)
    }
    else rgb(0,0,0)
  }
  else{
    // if (index == floor(pulsePosition)) rgb(1,1,1)
    if (index > pulsePosition && index < pulsePosition + pixelCount/2){
      var relative = (index-pulsePosition)/(pixelCount/2)
      //rgb(0,1,0)
      blend(color1[0],color1[1],color1[2],color2[0], color2[1], color2[2], relative, masterBrightness)
    }
    else if (index > pulsePosition + pixelCount/2 && index < pulsePosition + pixelCount){
      var relative = (index - (pulsePosition + pixelCount/2)) / (pixelCount/2)
      //rgb(0,0,1)
      blend(color2[0], color2[1], color2[2],0,0,0,relative, masterBrightness)
    }
    else rgb(0,0,0)
  }
}

var t_R, t_G, t_B
var prevPosition

beforeRenders[6] = (delta) => {
  var changeColor = false
  if (rev > 0) {
    pulsePosition = (pulsePosition + (delta/300)/(speed*speed)) % (pixelCount * 2)
    if (prevPosition > pulsePosition){ //wrapped back to beginning
      changeColor = true
    }
  }
  else{
    pulsePosition = (pulsePosition - (delta/300)/(speed*speed))
    if (pulsePosition <= -pixelCount) pulsePosition = pixelCount
    
    if (prevPosition < pulsePosition){ //wrapped back to end
      changeColor = true
    }
  }
  
  prevPosition = pulsePosition
  
  if (changeColor) _colorSelect = (_colorSelect + 1) % 3
  
  if (_colorSelect == 0){
    t_R = color1[0]
    t_G = color1[1]
    t_B = color1[2]
  }
  else if (_colorSelect == 1){
    t_R = color2[0]
    t_G = color2[1]
    t_B = color2[2]
  }
  else {
    t_R = color3[0]
    t_G = color3[1]
    t_B = color3[2]
  }
}

renderers[6] = (index) => {
  if (rev > 0){
    //if (index == floor(pulsePosition)) rgb(1,1,1)
    if (index < pulsePosition && index > pulsePosition - pixelCount){
      var relative = (pulsePosition - index)/(pixelCount)
      blend(t_R,t_G,t_B,0,0, 0, relative, masterBrightness)
    }
    else rgb(0,0,0)
  }
  else{
    // if (index == floor(pulsePosition)) rgb(1,1,1)
    if (index > pulsePosition && index < pulsePosition + pixelCount/2){
      var relative = (index-pulsePosition)/(pixelCount/2)
      blend(t_R,t_G,t_B,0,0,0, relative, masterBrightness)
    }
    else rgb(0,0,0)
  }
}

beforeRenders[7] = (delta) =>{
  
}

//single color spread from center
renderers[7] = (index) => {
  var center = (pixelCount/2)
  var t0 = wave(rev * time(speed*speed/(2*blockSize)) + abs(center-index)/(pixelCount/blockSize)) 
  //rgb(color1[0]*masterBrightness*t0,color1[1]*masterBrightness*t0,color1[2]*masterBrightness*t0)
  blend(color1[0], color1[1], color1[2], color2[0], color2[1], color2[2], t0, masterBrightness)
}

//3 color spread from center
beforeRenders[8] = (delta) => {

}

renderers[8] = (index) => {
  var center = (pixelCount/2)
  var t0 = wave(rev * time(speed*speed/5) + abs(center-index)/pixelCount) 
  
  var colorRamp = mod(rev * time(speed*speed*3/5) + (abs(center-index)/(2*pixelCount))+.75, 1) 
  
  if (colorRamp < .333) _colorSelect = 0
  else if (colorRamp < .667) _colorSelect = 1
  else _colorSelect = 2
  
  
  if (_colorSelect == 0){
    t_R = color1[0]
    t_G = color1[1]
    t_B = color1[2]
  }
  else if (_colorSelect == 1){
    t_R = color2[0]
    t_G = color2[1]
    t_B = color2[2]
  }
  else {
    t_R = color3[0]
    t_G = color3[1]
    t_B = color3[2]
  }
  rgb(t_R*masterBrightness*t0,t_G*masterBrightness*t0,t_B*masterBrightness*t0)
}

///3 color marquee
beforeRenders[9] = (delta) => {
  t = mod(time(rev * -.1 * speed / blockSize), 1)
}

renderers[9] = (index) => {
  intensityScale = .7
  var mbis = masterBrightness * intensityScale
  if (((index * blockSize)/pixelCount + t) % 1 < .33){
    rgb(color1[0]*mbis, color1[1]*mbis, color1[2]*mbis)
  }
  else if (((index * blockSize)/pixelCount + t) % 1 < .67){
    rgb(color2[0]*mbis, color2[1]*mbis, color2[2]*mbis)
  }
  else rgb(color3[0]*mbis, color3[1]*mbis, color3[2]*mbis)
}

//4 color beltlight chase
beforeRenders[10] = (delta) => {
  t = mod(time(rev * -0.02), 1)
}

renderers[10] = (index) => {
  var intensityScale = .8
  var mbis = masterBrightness*intensityScale
  
  if (((index)/pixelCount + t) % 1 < .25){
    mbis = mbis * .5
  }
  else if (((index)/pixelCount + t) % 1 < .5){
    mbis = mbis * 1
  }
  else if (((index)/pixelCount + t) % 1 < .75){
    mbis = mbis * .5
  }
  else mbis = 0
  
  if (((index*blockSize) / pixelCount) % 1 < .25) {
    _colorSelect = 0
  }
  else if (((index*blockSize) / pixelCount) % 1 < .5){
    _colorSelect = 1
  }
  else if (((index*blockSize) / pixelCount) % 1 < .75){
    _colorSelect = 2
  }
  else _colorSelect = 3
  
  if (_colorSelect == 0){
    t_R = color1[0]
    t_G = color1[1]
    t_B = color1[2]
  }
  else if (_colorSelect == 1){
    t_R = color2[0]
    t_G = color2[1]
    t_B = color2[2]
  }
  else if (_colorSelect == 2) {
    t_R = color3[0]
    t_G = color3[1]
    t_B = color3[2]
  }
  else { //white
    t_R = 1
    t_G = .6
    t_B = .2
  }
  rgb(t_R*mbis,t_G*mbis, t_B*mbis)
}

//twinklies - pop in, fade out
beforeRenders[11] = (delta) => {
  for (i = 0; i < NUMTWINKLIES; i++){
    if (twinklieVal[i] <= 0){
      twinkliePos[i] = round(random(pixelCount))
      twinklieVal[i] = random(1)
    }
    else{
      twinklieVal[i] -= delta*speed*speed * random(3)/ 25
    }
  }
}

renderers[11] = (index) => {
  var anyTwinklie = false
  for (i = 0; i < NUMTWINKLIES; i++){
    if (index == twinkliePos[i]){
      v = twinklieVal[i]
      blend(color2[0], color2[1], color2[2], color1[0], color1[1], color1[2],  v, masterBrightness)
      //rgb(color1[0] * v * masterBrightness, color1[1] * v * masterBrightness, color1[2] * v * masterBrightness)
      anyTwinklie = true
    }
  }
  if (! anyTwinklie) rgb(color2[0]*masterBrightness, color2[1]*masterBrightness, color2[2]*masterBrightness)
}

//twinklies - pop in, pop out
beforeRenders[12] = (delta) => {
  for (i = 0; i < NUMTWINKLIES; i++){
    if (twinklieVal[i] <= 0){
      twinkliePos[i] = round(random(pixelCount))
      twinklieVal[i] = random(1)
    }
    else{
      twinklieVal[i] -= delta*speed*speed * random(3)/ 25
    }
  }
}

renderers[12] = (index) => {
  var anyTwinklie = false
  for (i = 0; i < NUMTWINKLIES; i++){
    if (index == twinkliePos[i]){
      if (twinklieVal[i] > 0) v = 1
      else v = 0
      blend(color2[0], color2[1], color2[2], color1[0], color1[1], color1[2], v, masterBrightness)
      //rgb(color1[0] * v * masterBrightness, color1[1] * v * masterBrightness, color1[2] * v * masterBrightness)
      anyTwinklie = true
    }
  }
  if (! anyTwinklie) rgb(color2[0], color2[1], color2[2])
}

//twinklies - fade in, fade out
beforeRenders[13] = (delta) => {
  for (i = 0; i < NUMTWINKLIES; i++){
    if (twinklieVal[i] <= 0){
      twinkliePos[i] = round(random(pixelCount))
      twinklieVal[i] = 1.8 + random(.2)
    }
    else{
      twinklieVal[i] -= delta*speed*speed *(random(1) + 1) / 25
    }
  }
}

renderers[13] = (index) => {
  var anyTwinklie = false
  for (i = 0; i < NUMTWINKLIES; i++){
    if (index == twinkliePos[i]){
      v = 1-abs(1-twinklieVal[i])
      blend(color2[0], color2[1], color2[2], color1[0], color1[1], color1[2], v, masterBrightness)
      //rgb(color1[0] * v * masterBrightness, color1[1] * v * masterBrightness, color1[2] * v * masterBrightness)
      anyTwinklie = true
    }
  }
  if (! anyTwinklie) rgb(color2[0]*masterBrightness, color2[1]*masterBrightness, color2[2]*masterBrightness)
}

//foreground/background melt

beforeRenders[14] = (delta) => {
  meltMod = pixelCount / blockSize
  t1 = time(speed * speed)  // Time it takes for regions to move and melt 
}

renderers[14] = (index) => {
  c1 = 1 - abs(index - meltMod) / meltMod  // 0 at strip endpoints, 1 in the middle
  c2 = wave(c1)
  c3 = wave(c2 + t1)

  v = wave(c3 + t1)  // Separate the colors with dark regions
  v = v * v
  
  blend(color1[0], color1[1], color1[2], color2[0], color2[1], color2[2], v, masterBrightness)
  
  
}


//Rainbow Melt
var meltMod

beforeRenders[15] = (delta) => {
  meltMod = pixelCount / blockSize
  t1 = time(speed * speed * 2)  // Time it takes for regions to move and melt 
}

renderers[15] = (index) => {
  c1 = 1 - abs(index - meltMod) / meltMod  // 0 at strip endpoints, 1 in the middle
  c2 = wave(c1)
  c3 = wave(c2 + t1)

  v = wave(c3 + t1)  // Separate the colors with dark regions
  v = v * v * masterBrightness

  hsv(c1 + t1, 1, v)
}

//pop to color and dither out
beforeRenders[16] = (delta) => {
  for (i = 0; i < pixelCount; i++){
    values[i] -= fadeSpeed[i] * speed * delta/200
  }
}

renderers[16] = (index) => {
  rgb(color1[0] * min(values[index], 1) * masterBrightness, color1[1] * min(values[index], 1) * masterBrightness, color1[2] * min(values[index], 1) * masterBrightness)
}


//fade from zero up to full color
beforeRenders[17] = (delta) => {
  for (i = 0; i < pixelCount; i++){
    values[i] = min(values[i] + fadeSpeed[i] * speed * delta/2000, 1)
  }
}

renderers[17] = (index) => {
  rgb(color1[0] * min(values[index], 1) * masterBrightness, color1[1] * min(values[index], 1) * masterBrightness, color1[2] * min(values[index], 1) * masterBrightness)
}

//////////////Utility Functions///////////////

function blend(r1, g1, b1, r2, g2, b2, pctIntoXFade, mb) {
  r = r1 * (1 - pctIntoXFade) + r2 * pctIntoXFade
  g = g1 * (1 - pctIntoXFade) + g2 * pctIntoXFade
  b  = b1 * (1 - pctIntoXFade) + b2 * pctIntoXFade
  rgb(r*mb, g*mb, b*mb)
  //return retVal
}

function rgb2hue(r, g, b){
  var cMax = max(r, g)
  cMax = max(cMax, b)
  
  var cMin = min(r, g)
  cMin = min(cMin, b)
  
  var delta = cMax - cMin
  
  if (delta == 0) return 0
  
  if (cMax == r){
    return (.1667 * ((g - b)/delta)) % 1
  }
  else if (cMax == g){
    return (.1667 * ((g - b)/delta + .333)) % 1
  }
  else if (cMax == b){
    return (.1667 * ((g - b)/delta + .667)) % 1
  }
}

function fakeData() {
    patternIndex = 15
    color1[0] = 0
    color1[1] = 1
    color1[2] = 0
    color2[0] = .1
    color2[1] = .1  
    color2[2] = .1
    color3[0] = 0
    color3[1] = 0
    color3[2] = 0
    speed = .2
    masterBrightness = .1
    blockSize = 2//frequencyData[9] << 4
    reversedData = frequencyData[10]
    
    NUMTWINKLIES = min(blockSize * 3, MAXTWINKLIES)
    
    rev = -1
}
1 Like

Hello and welcome!

I just set this up on my PB3 w/3.18. When I started it up, it was all black, as you described. I checked at the variable watch window and saw that patternSelect was -1;

Around line 97…

export function render(index) {
  if (patternSelect == -1){_R = 0; _B = 0; _G=0}
  else renderers[patternIndex](index)
}

So I changed patternSelect to 15 (because that was the value used in the fakeData() function) and it seems to be working – I see a lot of nice colors! Possibly, you just need to be sure patternSelect gets set in beforeRender() when USEFAKEDATA is true.

Hope this helps! Great looking pattern, btw!

3 Likes

Well dang, that was it! Thanks Jon, well spotted and an easy fix. Much appreciated!

I’ll leave this topic up as a cautionary tale about the kind of errors that are easy to make after a long day of coding. The project involves DMX control of the PixelBlazes via some LoRa WiFi radios, and after holding all that code in my head for the weekend, something had to give I guess. Now onward and upward, with more coffee.

Cheers, Jeff

1 Like

Hi @JeffJeffGlassGlass,
Catching your live stream was really fun!

Glad you got it figured out! Was it for sure a code change, and not the version change?

The last breaking change to the compiler was in 3.12, where the “compiler now prioritizes a declared name over a built-in.” Before you could do weird stuff like this (max is a built-in):

var max = 1
var value = max(max, 2)

Would actually work, but thats nonsense! Now the compiler prefers user defined identifiers in every context, and you’d get a runtime error (invalid function call) with the above code. This actually improves compatibility when new functions are added and old code has a function with the same name - it will continue to use the one defined in code.

3 Likes