Pixelblaze V3.19 beta

,

Hello!

I’m working on finishing up V3.19. There’s some new features, bug fixes, and features around stability.

Here’s the full change log:

  • Array literals! This will be very handy for initial setup and data sets for sure. Also comes with a new function/method for setting/overwriting many elements an array at once. No GC, so it’s still important to re-use arrays.
  • rgb() calls with RGBW strips now use the white element, and have HDR support for APA102/SK9822 LEDs (RGBClock looks amazing!)
  • Fixed redirect in captive portal that was causing some operating systems and/or network setups to have problems.
  • Global variable limits are checked, and if you happen to run out (on V3 thats 255 of your own, plus pixelCount) you will get a nice error on that line instead of crashing.
  • Fixed a bug that would prevent entering setup mode (and clearing some other nonvolatile memory) if it was already in setup mode. It now works reliably, and the flashing pattern is consistent (always BLINK + 3 flashes).
  • Fix discovery flag setting in the recovery.html app
  • Added Multi-matrix mapper example
  • Command/Control - s will now act like clicking the Save pattern button.
  • Fixed some issues and improved responsiveness of the websocket connected indicator.
  • Fixed issues with open/unsecured WiFi network configuration. The lock icon shouldn’t show up for open networks, and the validation state handling of the passphrase field has been fixed.
  • Resets due to brownout (when detectable) no longer trigger loading fail-safe settings.
  • When fail-safe settings are loaded, a popup message will show up warning that this is the case.
  • Likewise, when a brownout was detected a popup message will warn about power issues.

If you’ve been having any problems related to these issues and would like to try this beta, please let me know! I’d love to get more eyes on it in the field.

Array literals have to be the most requested feature, and I’m happy to add this! It’s going to be much easier to pull in data sets/tables and that sort of thing.

I also want to add the function that is doing all the work behind the scenes, so that we can update arrays easily as well. Since Pixelblaze’s language doesn’t yet have GC, we can’t create new arrays indefinitely and arrays should be reused instead.

Like other array functions, this will be available as a function that takes an array, and as a method that works on the array directly. The first argument is the index to start replacing values. You can use it to update a subset within an array, or set it to zero to update from the beginning. Any following arguments will then replace/overwrite any previous values. Giving too many values generates runtime errors, just like if you tried to write outside of an array element by element.

What should it be called? If it were named foo you could use it like this:

var data = [1, 2, 3, 4]
data.foo(0, 5, 6) // data is now [5, 6, 3, 4]
data.foo(2, 7, 8) // data is now [5, 6, 7, 8]
data.foo(0, 1, 2, 3, 4) // data is now [1, 2, 3, 4]

Please give us some good name ideas :slight_smile:

As soon as I can get a decent name, I’ll publish the beta for folks to try out, and assuming all goes well, push this as an update soon.

7 Likes

So freakin excited for all this! Literals! Happy to help test.

This method feels like Array.protoype.splice(start, deleteCount, item, item2, itemN) but without deleteCount.

ES6 already has an Array.prototype.fill(). So then, .replace() sounds like it might makes sense, but ES6 has String.prototype.replace() which works a bit differently. There’s no replace on Array – does that make it a better or worse candidate!?

replaceAt? overwrite? patch? sub?

var data = [1, 2, 3, 4]
data.replaceAt(0, 5, 6) // data is now [5, 6, 3, 4]
data.overwrite(0, 5, 6) // data is now [5, 6, 3, 4]
data.patch(0, 5, 6)     // data is now [5, 6, 3, 4]
data.sub(0, 5, 6)       // data is now [5, 6, 3, 4]
2 Likes

I agree, .replace makes the most sense…
Yeah, it’s not ES6 but we don’t even have strings, so…

It’s a nice function, mostly for the multiple set option. For a singlet, you can just assign a new value, but for more than one value replacement, this is helpful. I’m sure we’ll figure out nifty uses.
The pseudo vector stuff for sure.

Yes, I hinted at literals coming a few comments ago… I’m most excited by that, lots of good bug fixes too. Happy to beta test, of course.

Ooh might be nice to have a “automatic zero”
form, like .write

Example:

var exampleArray = [1,2,3]
vs
var exampleArray = array(3)
exampleArray.write(1,2,3)

What’s the story on reusing arrays with literals?
Is that ok?

I’d assume the length is auto set with the literal,
So my example above would allow making a longer array and just populating the first 3 elements. (Now I’m mulling over a pseudo pop/push idea… That would allow all manner of stuff we can’t do now easily)

Actually I’ve been curious about how to best copy arrays too.

Added: and to complement write, might be nice to have a .read (that did have a start position, and length) to grab, say the 2nd,3rd,4th, in an array, and do something with them as a new array. But that’s a wishlist item, and optional, as I can see how to fake it…

Yeah, once created arrays are all the same regardless if they came from array call or a literal.

Behind the scenes it’s making an array and then calling this function to fill it up. I do it in chunks to avoid blowing up the stack, so you can have arbitrarily large literal arrays up to memory limits. The overhead is much much smaller than doing a bunch of arr[n] = ....

Push/pop/shift/unshift are on my list too.

2 Likes

rgbw using rgb() calls!! I’d help Beta test this as well.

Thank you all for the ideas! Leaning towards replace and replaceAt.

Also considered setElements and setElementsAt. Felt too verbose, too much camelCase (oh how far I’ve come from my days coding enterprise Java). C++ also has assign - but this also implies updating size, and emplace which inserts/adds. Thesaurususing a little I found pave which I find hilarious but would be perfect otherwise.

BTW - length comes from the array’s size, and literals size are pre-calculated during compile. The whole array is allocated at once, and then paved bit by bit as the expressions within the literal are resolved.

@Scruffynerf, you asked about copying arrays, this isn’t too bad with existing tools:

Array copy:

arrayMapTo(src, dest, v -> v)

Partial copy e.g. starting at index 12 in src for the size of dest:

dest.mutate((v, i) -> src[i+12])

(src must be at least 12 elements larger than dest in this example)

About .read that would be slice() (docs). Also in ES6 you can consume arrays using ES6’s spread syntax as arguments or in array literals.

Continued ramblings about JS follow…

Ideally PB code could be used in JS environments with a little bit of polyfill. Strings aren’t arrays even if you can access characters using square brackets, and arrays don’t have a replace function in JS/ES6. Strings are array-like enough that you can run many of Array’s prototype functions on them though. E.g. to convert a string to an array, you can call Array’s slice() on it, setting this to the string, as if it were a member method of the string:

Array.prototype.slice.apply("123") // gives [ '1', '2', '3' ]

(String has it’s own slice that returns strings)

Or if you are brave, dumb, or smart enough, you can install it into String’s prototype, which is one of the coolest and most dangerous JavaScripty things you can do.

"abc".slice(1) // gives "bc"
String.prototype.sliceAsArray = Array.prototype.slice
"abc".sliceAsArray(1) // gives [ 'b', 'c' ]

Thats all to say that it would be possible to polyfill replace for JS code that works on arrays and doesn’t break String’s replace.

3 Likes

Beta update files!

I haven’t had a chance to add the convenient replace just replaceAt, nor get the built in docs updated.

I recommend that you upload these using the /recovery.html tool, since you can get progress information. If you need to roll back, the regular online update will revert it back to 3.18.

3 Likes

So the process is download the file and put the PB into setup mode? Then http://192.168.4.1/recovery.html ?

You don’t need to put it into setup mode. The advantage of /recovery.html over /update (note: no .html) is that recovery.html has progress info (shows you bytes loading), vs the ‘blind’ update on /update

1 Like

I like array initializers! :+1:

The size difference for a pattern that defines and renders a static 16x16 bitmap defined in an array of packRGBA(r,g,b,a) tuples:

version blob epe js
v3.18 21147 21566 16609
v3.19b 13349 15316 11138

It could be even smaller if I could do away with packRGBA(r,g,b,a) and initialize the array with 32-bit hex literals, but a literal like 0xFDB97531 gets truncated by PBscript to a 16-bit value 0x7531 with no fractional component. I could do ary = [ 0xFDB9 + (0x7531 >> 16), ...] but that’s just as bad in terms of space and even worse in terms of readability. Extending the grammar of hex literals to allow 0xFDB9.7531 might be a workable kludge; otherwise parsing all hex literals as 32-bit integers might break existing code.

I’ve been working on a Pacman simulation but I kept running out of array space and at times crashing the editor; I’ll be interested to see if the code size reductions above and the checks for exceeding global limits will make enough of a difference to help me finish it.

1 Like

we’re on the same wavelength. Yes, a 0xFFFF.FFFF would be ideal, but in the meantime, you could do a function:

function p(a,b){
    return a + (b >>16)
}

so...
 ary = [ p(0xFDB9,0x7531), ...]

that was my thought in this comment

For slightly more readability with 4 256 values, you could do:

function p(a,b,c,d){
    return (a << 8) + b + (c >> 8) + (d >> 16)
}

so...
 ary = [ p(0,255,128,0), p(0x0,0xFF,0x80,0x0)...]

I think I got the bit shift math right there, but double check me. And the forum parser doesn’t seem to like it.

Yes, that’s how I encode images:

function packRGBA(r, g, b, a) { return (r << 8) + g + (b >> 8) + (a >> 16); }

image = array(256); // allocate row storage
image = [ packRGBA(0xdf,0xbc,0x5b,0x00),packRGBA(0xe1,0xc2,0x6c,0x00),... ]

You’re right that the palette indices could be stored the same way…

1 Like

Literals are limited in range to 16.15 (31 bits). The least significant bit is used as a flag for instructions. Variables and math all work with the full 32 bits, so an expression can create the full 32 bit word, such as your packRGBA function.

As a compromise, you could represent data in your code as a real number instead of hex to get the full 31-bit range for a single literal. For example your 0xFDB97531 divided by 65536 becomes ~64953.457778 (6 decimal places is probably plenty), but once encoded would end up being 0xFDB9.7530 (dropping the least significant bit). Humans are least perceptive of red contrast, so you could re-order it so that red was in the least significant byte without losing noticeable perceptual range.

A 31-bit literal is 1 word, the full 32-bit p(0xFDB9,0x7531) expression takes 3 words, and packRGBA(0xdf,0xbc,0x5b,0x00) uese 5 words.

By the way, when using array literals you don’t want to pre-allocate an array, the array literal does that for you. Doing it twice orphans the first array making it unusable but still retaining memory.

Change this code:

image = array(256); // allocate row storage
image = [ packRGBA(0xdf,0xbc,0x5b,0x00),packRGBA(0xe1,0xc2,0x6c,0x00),... ]

to just this:

image = [ packRGBA(0xdf,0xbc,0x5b,0x00),packRGBA(0xe1,0xc2,0x6c,0x00),... ]
1 Like

Just a heads up, I’ve tried to put 3.19b through the paces and added stuff to the unit test suite - looks solid!

I see you figured out sensible behaviors for the cases we talked about, and you can watch it run out of memory as your hint that you’re using defined literals in a loop.

2 Likes

Thanks, I figured that out eventually.

So, v3.19 beta participants, how did it go for you?

  • Great! No problems!
  • Had a problem (please post details!)
  • Haven’t tried it enough to say
  • Stuck trying to figure out how to update

0 voters

PB v3 standard - the 3.19 beta fixed the app connect/startup redirect hang to pbWiFIsetup.html

1 Like

The multi-matrix example is very cool.

Feature idea: have a parm to reverse the XY loop then it could be used with panels that use a different wiring on the X and Y. cant just rotate then its mirrored backwards. What i ended up with for now is to create a ‘panel2’ function with the inner and outer loops reversed.

At first i thought that multi-map reference meant ability to bind a map to a pattern. As someone said I’d have to find the post to recall who – said it best: “sometimes i want a 2d map sometimes i want a 3d map” this is what i miss the most. So much – I’m defining multiple maps and select between them via variables/defines at the top of the mapping tab as a workaround.

Feature idea: Ability to save a custom map as a default. If i build a nice custom map. I know i can save it but if the “end user” finds the map tab, loads a map , then POOF that custom map is gone and i get a call. The low tech solution i use is to provide a document file of the configurations for any given project and put the map code in the doc file to recover from.
This would be better if there was at least one ‘custom’ Map slot to store a map and reload it from flash.

If you combine both of those it be useful to be able to have maps stored and a specific map can can be bound to a pattern via the playlist. Maybe a ring maybe a matrix maybe a 3d matrix so while 1 ‘custom’ map slot would be ok, all of the defaults should be “callable” with at least 2 custom map slots being idea.

2 Likes

I sympathize… Having built a tall lamp with a 3d map, I just relearned how many existing patterns don’t support 3D “out of the box” yet. It’s easy to fix, and once we get all of the patterns into GitHub, submitting patches to make things more uniform and universal will likely happen.

The whole point of mapping is to separate the hardware (and layout) from the patterns. Ideally you shouldn’t ever have to change your map once it’s set correctly. But we’re not quite there yet.