Pixelblaze V3.19 beta

,

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.

I think i get the power of the map, but afraid there are always going to be a lot of other patterns. hence once you get that perfect map done, and go find some other new pattern, you have to tweak the map. and it would be better to embrace the excellence of the power of the map rather than strive for every patterns perfection… make ALL patterns ‘more perfect’ by abstracting that hardware. regardless if the are written with the latest direction of the wind in mind.

My pov is have/support multiple maps and make it easy to pick one.

Every one of those EXAMPLE maps (along with the ability to customize and OVERRIDE some of the global values Pixel Count and Ex-pander board settings) ,be callable, bindable as a “config set” I’d like to be able include/exclude certain pixels to have them always on or always off, or virtually re-wire the ordering of multiple displays daisy chained. I know you can do all of that in pattern code. but dont want to do that in every pattern for every project. i want to do that more globally.

I think the map is underappreciated being cast as a simple set and forget translation layer.

1 Like

I tend to agree, and anyone can tweak a pattern to be more optimal for a given piece. I think the universality - that it can work out of the box with no modifications is powerful.

When it comes to displaying 3d patterns on 2D, the new version can do that now. It defaults to a 2D plan with a fixed Z component, which is probably not ideal but is easy to explain/understand. Perhaps there could be a way to specify how the 3d pattern renders in 2D, which of the 3 coordinates to use for the 2D, and perhaps a way to rotate/translate/transform in the style of a UI control without going in and coding it.

Going 2D to 3D is less straightforward unless you assume a cylindrical map and “wrap” the 2d around it, like a tin can label. And perhaps if we have a way to adjust how 3D patterns look in 2D, adding features like that would make sense and would have many applications. For something like that we’re previously talked about a renderPolar or something as well, to make it easier and allow a pattern to apply to different coordinate systems even if the map is cartesian.

Outside of PB, when I think of going from 3D to 2D its a projection. Like the map preview rendering 3D points in 2D browser window. I don’t know how well that would translate to low/medium density 2D LED setups though.

Yes, and that’s exactly the issue I’m having now:

I did a 3D map, but while my Y is correct (I made that height), my X and Z (essentially the circular components) are narrow and limited to a small range. I’m debating the right way to handle this, and so for fun, I put a controller with WLED onto it (actually onto the second unit, which I just built, and photos coming soonish) and immediately found a bug (it didn’t handle inexact matrixes correctly, so 11x27 is 297, and it would only light 3 rows till I changed it from 300 to 297 pixels) but I’m noticing that doing X as the 11 (so a wrap around the tin can) means that it’s a decent XY for things that only understand 2D.

But others don’t look right at all. There is no One True Way.

I’m going to go thru patterns with a few setups simultaneously. Like maybe a 1D strip, a 2D matrix panel, a lamp setup in 2D, a lamp setup in 3D, and see if there is a way to make “do the right thing” in all cases, or a way to at least make it a config. And yes, render_polar would be interesting, essentially when you do the X wrap on a cylinder, it’s really just polar (X becomes the angle, Y becomes the height, R is meaningless since all pixels are R from the central axis)

Now I just need to add “lava lamp simulator” to my to-do list, as my GF basically wants these giant 4-5 tall lamps to become that.

@zranger1, you’ll be happy to know that of ALL of the fire patterns, including on WLED (1D and 2D), Doom 2D 2.0 looks best on these lamps, according to her (and me too). I have to turn the dragon off though, as it’s way too much white at the base. Might want to add another dragon option with less white.

@Scruffynerf
Cube fire might be a good start for a lava lamp. I’ve played with it to make something similar on PB cubes where it moves mostly in one direction continuously. Maybe a merge of cubefire and rainbow melt.

Yeah, and I’ve seen metaballs used too. But I want a real lavalamp, so I’m looking at this code:

https://catkane-doodles.github.io/lava-lamp/

It warms the wax and those bits float upward… And as they cool, they drop back down. [warning - takes quite a while to warm up, just like a real lava lamp. I’ll have to figure out a jumpstart for it, like randomly warmer bits maybe.]

This one is built on top of a retired physics engine, but… it works.
http://wellcaffeinated.net/PhysicsJS/examples/lavalamp/

Ok, my own repo of the lava is a bit faster (massively increased heat source),

https://scruffynerf.github.io/lava-lamp/

and the code looks simple enough that a PB port will be possible, especially compared to the dozens of other projects I looked at, either more complex,.or worse: “lava lamp” (including led ones) that didn’t actually do “lava lamp” just plasma etc.

So hopefully this will be an actual lava lamp pattern, maybe first of it’s kind.

In Doom Fire, down near the end where it finally gets around to setting the pixel color, there’s a line:

hsv(baseHue+((0.05*bri)), 1.3-bri/4,baseBri * bri);

To decrease the white - the amount of desaturation of really hot pixels - change the constant 1.3 to something higher. All white will disappear at around 2.0, so maybe 1.75 or 1.8 would be about right.

I almost put a slider in for this, but figured I was already overcomplicating things. It’ll go in the next rev.

And I’m looking forward to what you come up with in the way of lavalamp. That ought to be super cool!

4 Likes

For example consider a square matrix wrapped around a cylinder with a circle matrix on the end.

If you just want this to be treated as one shape and render to that like a canvas its easy enough.

If you want to target these two panels with different patterns or use cases you have to write a multi-segment pattern. Or use multiple PBs and communicate between them via some external mechanism.

if you want to run the same pattern on both displays like scrolling text , the pattern needs tweaking. Even though text scrolling pattern would/could work fine on either display mapped by itself - just not with a map using “compound shapes”

I think a MUCH better LONG TERM approach is to simulate how multiple boards work today, kind of like setting up micro-services or virtualization or containers to handle different aspects of the overall layout. Maybe its not in the map or multiple maps as much as the ability virtualize a portion of the LEDs and map those to a pattern and run more than 1 pattern simultaneously.

For example.

In a pattern - have the ability to specify index start/stop as a parameter, not defined in the code, and to be clear the code should not even have To know about this constraint on which LEDs the pattern renders to by index) Then you can “target” the pixels that pattern renders to. Sort of like you can do with the serial expander config.

Would also want to be able to run more than 1 pattern at the same time targeting different sections of the overall mapped LEDs.

Then you can easily have an UX input/output text scrolling running on the Circle, with a few Digital pins, and then run any random flashing pattern on the Square matrix - as CONTROLLED by the pattern running the UX/ I/O Pins.

Several different concepts here, but ALL working together to make ‘ALL’ patterns ‘more’ “portable”.

Multiple pattern rendering could make it easier to re-use patterns together rather than always re-writing something new for multi-segment scenarios.

Ultimately the goal is to increase the ability to re-use an existing pattern in new ways rather than always needing to re-write a pattern to use it in a new way.

Conceptually - I dont really see this as multiple sandboxes running different patterns as much as i see as multiple threads. Ideally the separate patterns are running in the same global state, or have some IPC mechanism between them.

I know i can do that all with 1 pattern and after it’s done, i have exactly 1 ‘concrete’ pattern that works ‘correctly’ on this project and if i want another one it needs to all be coded up again…

RATHER THAN : just picking some random pattern from the library and defining the space it can render to, the square…in this example.

or i can run some random pattern from the library and loose the ‘customization’ for the project.

To me — either way is sub-optimal and there should be some middle ground so you dont have to re-write every pattern to work with something custom.

lots of different concepts there, but all similar in that — ‘everything’ does not belong in the pattern rendering code routines…

1 Like

I understand your view… BUT… the goal of mapping is to abstract, not to compartmentalize. To remove the differences between different LED setups and allow a pattern to run anywhere.

As for running multiple patterns, right now, we have both a multi-segment pattern and a multi-“map” pattern, which are super flexible and would allow people to use that technique to run whatever they want. What we don’t have is the ability to run more than one pattern at once, but it’s on the todo list, (or some way to import code)

Yes, I agree, it’s sub-optimal right now, but it’s also running on an esp32, and so we’re limited in terms of cpu/space. Given that limit, what we do have is pretty awesome.

2 Likes