1- <!DOCTYPE html>
1+ <!DOCTYPE html>
22< html lang ="en ">
33< head >
44 < meta charset ="utf-8 ">
1313 function gT ( t ) { for ( let type of d . ledTypes ) if ( t == type . i ) return type ; } // getType from available ledTypes
1414 function isPWM ( t ) { return gT ( t ) . t . charAt ( 0 ) === "A" ; } // is PWM type
1515 function isAna ( t ) { return gT ( t ) . t === "" || isPWM ( t ) ; } // is analog type
16- function isDig ( t ) { return gT ( t ) . t === "D" || isD2P ( t ) ; } // is digital type
16+ function isD1P ( t ) { return gT ( t ) . t === "D" ; } // is digital 1 pin type
1717 function isD2P ( t ) { return gT ( t ) . t === "2P" ; } // is digital 2 pin type
18+ function isDig ( t ) { return isD1P ( t ) || isD2P ( t ) ; } // is digital type
1819 function isNet ( t ) { return gT ( t ) . t === "N" ; } // is network type
1920 function isVir ( t ) { return gT ( t ) . t === "V" || isNet ( t ) ; } // is virtual type
2021 function isHub75 ( t ) { return gT ( t ) . t === "H" ; } // is HUB75 type
7172 return ;
7273 }
7374 // ignore IP address
74- if ( isNet ( t ) ) return ;
75+ if ( isNet ( t ) ) return ;
7576 //check for pin conflicts
7677 if ( LC . value != "" && LC . value != "-1" ) {
7778 let p = d . rsvd . concat ( d . um_p ) ; // used pin array
101102 nList [ j ] . focus ( ) ;
102103 ok = false ;
103104 return ;
104- }
105105 }
106106 }
107107 }
108+ }
108109 } ) ;
109110 return ok ;
110111 }
215216 mul = 2 ; // ESP32 RMT uses double buffer
216217 } else if ( ( is32 ( ) || isS2 ( ) || isS3 ( ) ) && toNum ( n ) > ( parallelI2S ? 7 : 0 ) ) {
217218 mul = 2 ; // ESP32 RMT uses double buffer
218- } else if ( ( parallelI2S && toNum ( n ) < 8 ) || ( n == 0 && is32 ( ) ) ) { // I2S uses extra DMA buffer
219- dbl = len * ch * 3 ; // DMA buffer for parallel I2S (TODO: ony the bus with largst LED count should be used)
219+ } else if ( ( parallelI2S && toNum ( n ) < 8 ) || ( toNum ( n ) == 0 && is32 ( ) ) ) { // I2S uses extra DMA buffer
220+ dbl = len * ch * 3 ; // DMA buffer for parallel I2S (TODO: only the bus with largst LED count should be used)
220221 }
221222 }
222223 return len * ch * mul + dbl + pbfr ;
229230 let sLC = 0 , sPC = 0 , sDI = 0 , maxLC = 0 ;
230231 const abl = d . Sf . ABL . checked ;
231232 let setPinConfig = ( n , t ) => {
232- let p0d = "GPIO:" ;
233+ let p0d = "GPIO: " ;
233234 let p1d = "" ;
234235 let off = "Off Refresh" ;
235236 switch ( gT ( t ) . t . charAt ( 0 ) ) {
240241 p0d = "Data " + p0d ;
241242 break ;
242243 case 'A' : // PWM analog
243- if ( numPins ( t ) > 1 ) p0d = "GPIOs:" ;
244+ if ( numPins ( t ) > 1 ) p0d = "GPIOs: " ;
244245 off = "Dithering" ;
245246 break ;
246247 case 'N' : // network
247- p0d = "IP address:" ;
248+ p0d = "IP address: " ;
248249 break ;
249250 case 'V' : // virtual/non-GPIO based
250- p0d = "Config:"
251+ p0d = "Config: "
251252 break ;
252253 case 'H' : // HUB75
253254 p0d = "Panel size (width x height), Panel count:"
268269 }
269270
270271 // enable/disable LED fields
272+ updateTypeDropdowns ( change ) ;
271273 let dC = 0 ; // count of digital buses (for parallel I2S)
272274 let LTs = d . Sf . querySelectorAll ( "#mLC select[name^=LT]" ) ;
273275 LTs . forEach ( ( s , i ) => {
274- s . disabled = ( i < LTs . length - 1 ) ; // prevent changing type (as we can't update options)
275- // is the field a LED type?
276276 var n = s . name . substring ( 2 , 3 ) ; // bus number (0-Z)
277277 var t = parseInt ( s . value ) ;
278278 memu += getMem ( t , n ) ; // calc memory
321321 }
322322 // do we have a led count field
323323 if ( nm == "LC" ) {
324+ LC . max = isAna ( t ) ? 1 : ( isDig ( t ) ? maxPB : 16384 ) ; // set max value
324325 let c = parseInt ( LC . value , 10 ) ; //get LED count
325326 if ( ! customStarts || ! startsDirty [ toNum ( n ) ] ) gId ( "ls" + n ) . value = sLC ; //update start value
326327 gId ( "ls" + n ) . disabled = ! customStarts ; //enable/disable field editing
327328 if ( c ) {
328329 let s = parseInt ( gId ( "ls" + n ) . value ) ; //start value
329330 if ( s + c > sLC ) sLC = s + c ; //update total count
330- if ( c > maxLC ) maxLC = c ; //max per output
331331 if ( ! isVir ( t ) ) sPC += c ; //virtual out busses do not count towards physical LEDs
332332 if ( isDig ( t ) ) {
333+ if ( c > maxLC ) maxLC = c ; //max per output
333334 sDI += c ; // summarize digital LED count
334335 let maPL = parseInt ( d . Sf [ "LA" + n ] . value ) ;
335336 if ( maPL == 255 ) maPL = 12 ;
409410 gId ( "pc" ) . textContent = ( sLC == sPC ) ? "" :"(" + sPC + " physical)" ;
410411
411412 // memory usage and warnings
413+ memu += sLC * ( 4 + ( gN ( "MS" ) . checked ? 0 : 4 ) ) ; // pixel buffer (4 bytes per pixel) + at least 1 segment buffer
412414 gId ( 'm0' ) . innerHTML = memu ;
413415 bquot = memu / maxM * 100 ;
414416 gId ( 'dbar' ) . style . background = `linear-gradient(90deg, ${ bquot > 60 ? ( bquot > 90 ? "red" :"orange" ) :"#ccc" } 0 ${ bquot } %, #444 ${ bquot } % 100%)` ;
439441 function lastEnd ( i ) {
440442 if ( i -- < 1 ) return 0 ;
441443 var s = chrID ( i ) ;
442- v = parseInt ( d . getElementsByName ( "LS" + s ) [ 0 ] . value ) + parseInt ( d . getElementsByName ( "LC" + s ) [ 0 ] . value ) ;
443- var t = parseInt ( d . getElementsByName ( "LT" + s ) [ 0 ] . value ) ;
444+ v = parseInt ( gN ( "LS" + s ) . value ) + parseInt ( gN ( "LC" + s ) . value ) ;
445+ var t = parseInt ( gN ( "LT" + s ) . value ) ;
444446 if ( isPWM ( t ) ) v = 1 ; //PWM busses
445447 return isNaN ( v ) ? 0 : v ;
446448 }
447449 function addLEDs ( n , init = true )
448450 {
449451 var o = gEBCN ( "iST" ) ;
450452 var i = o . length ;
451- let disable = ( sel , opt ) => { sel . querySelectorAll ( opt ) . forEach ( ( o ) => { o . disabled = true ; } ) ; }
452-
453453 var f = gId ( "mLC" ) ;
454- let digitalB = 0 , analogB = 0 , twopinB = 0 , virtB = 0 ;
455- f . querySelectorAll ( "select[name^=LT]" ) . forEach ( ( s ) => {
456- let t = s . value ;
457- if ( isDig ( t ) && ! isD2P ( t ) ) digitalB ++ ;
458- if ( isD2P ( t ) ) twopinB ++ ;
459- if ( isPWM ( t ) ) analogB += numPins ( t ) ; // each GPIO is assigned to a channel
460- if ( isVir ( t ) ) virtB ++ ;
461- } ) ;
462454
463455 if ( ( n == 1 && i >= 36 ) || ( n == - 1 && i == 0 ) ) return ; // used to be i>=maxB+maxV when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
464456 var s = chrID ( i ) ;
467459// npm run build has trouble minimizing spaces inside string
468460 var cn = `<div class="iST" draggable="true" ondragstart="hDS(event)" id="l${ s } " style="cursor:grab;">
469461<hr class="sml">
470- <span id="n${ s } ">${ i + 1 } </span>:
462+ <span id="n${ s } ">${ ++ i } </span>:
471463<select name="LT${ s } " onchange="UI(true)"></select><br>
472464<div id="abl${ s } ">
473465mA/LED: <select name="LL${ s } " onchange="enLA(this);UI();">
479471<option value="0">Custom</option>
480472</select><br>
481473<div id="LAdis${ s } " style="display: none;">max. mA/LED: <input name="LA${ s } " type="number" min="1" max="255" oninput="UI()"> mA<br></div>
482- <div id="PSU${ s } ">PSU: <input name="MA${ s } " type="number" class="xl" min="250" max="65000 " oninput="UI()" value="250"> mA<br></div>
474+ <div id="PSU${ s } ">PSU: <input name="MA${ s } " type="number" class="xl" min="250" max="65500 " oninput="UI()" value="250"> mA<br></div>
483475</div>
484476<div id="co${ s } " style="display:inline">Color Order:
485477<select name="CO${ s } ">
493485<div id="dig${ s } w" style="display:none">Swap: <select name="WO${ s } "><option value="0">None</option><option value="1">W & B</option><option value="2">W & G</option><option value="3">W & R</option><option data-opt="CCT" value="4">WW & CW</option></select></div>
494486<div id="dig${ s } l" style="display:none">Clock: <select name="SP${ s } "><option value="0">Slowest</option><option value="1">Slow</option><option value="2">Normal</option><option value="3">Fast</option><option value="4">Fastest</option></select></div>
495487<div>
496- <span id="psd${ s } ">Start:</span> <input type="number" name="LS${ s } " id="ls${ s } " class="l starts" min="0" max="8191" value="${ lastEnd ( i ) } " oninput="startsDirty[${ i } ]=true;UI();" required />
488+ <span id="psd${ s } ">Start:</span> <input type="number" name="LS${ s } " id="ls${ s } " class="l starts" min="0" max="8191" value="${ lastEnd ( i - 1 ) } " oninput="startsDirty[${ i } ]=true;UI();" required />
497489<div id="dig${ s } c" style="display:inline">Length: <input type="number" name="LC${ s } " class="l" min="1" max="${ maxPB } " value="1" required oninput="UI()" /></div><br>
498490</div>
499491<span id="p0d${ s } ">GPIO:</span><input type="number" name="L0${ s } " required class="s" onchange="UI();pinUpd(this);"/>
523515 }
524516 } ) ;
525517 enLA ( gN ( "LL" + s ) ) ; // update LED mA
526- // disable inappropriate LED types
527- let sel = d . getElementsByName ( "LT" + s ) [ 0 ] ;
528- // 32 & S2 supports mono I2S as well as parallel so we need to take that into account; S3 only supports parallel
529- let maxDB = maxD - ( is32 ( ) || isS2 ( ) || isS3 ( ) ? ( ! d . Sf [ "PR" ] . checked ) * 8 - ( ! isS3 ( ) ) : 0 ) ; // adjust max digital buses if parallel I2S is not used
530- if ( digitalB >= maxDB ) disable ( sel , 'option[data-type="D"]' ) ; // NOTE: see isDig()
531- if ( twopinB >= 2 ) disable ( sel , 'option[data-type="2P"]' ) ; // NOTE: see isD2P() (we will only allow 2 2pin buses)
532- disable ( sel , `option[data-type^="${ 'A' . repeat ( maxA - analogB + 1 ) } "]` ) ; // NOTE: see isPWM()
533- sel . selectedIndex = sel . querySelector ( 'option:not(:disabled)' ) . index ;
518+ let sel = gN ( "LT" + s ) ;
519+ sel . selectedIndex = sel . querySelector ( 'option:not([data-type])' ) . index ; // select On/Off by default
534520 }
535521 if ( n == - 1 ) {
536- o [ -- i ] . remove ( ) ; -- i ;
537- o [ i ] . querySelector ( "[name^=LT]" ) . disabled = false ;
522+ o [ -- i ] . remove ( ) ;
538523 }
539524
540- gId ( "+" ) . style . display = ( i < 35 ) ? "inline" :"none" ; // was maxB+maxV-1 when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
541- gId ( "-" ) . style . display = ( i > 0 ) ? "inline" :"none" ;
525+ gId ( "+" ) . style . display = ( i < 36 ) ? "inline" :"none" ; // was maxB+maxV-1 when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
526+ gId ( "-" ) . style . display = ( i > 1 ) ? "inline" :"none" ;
542527
543528 if ( ! init ) {
544529 UI ( ) ;
703688 let l = c . hw . led ;
704689 l . ins . forEach ( ( v , i , a ) => {
705690 addLEDs ( 1 ) ;
706- for ( var j = 0 ; j < v . pin . length ; j ++ ) d . getElementsByName ( `L${ j } ${ i } ` ) [ 0 ] . value = v . pin [ j ] ;
707- d . getElementsByName ( "LT" + i ) [ 0 ] . value = v . type ;
708- d . getElementsByName ( "LS" + i ) [ 0 ] . value = v . start ;
709- d . getElementsByName ( "LC" + i ) [ 0 ] . value = v . len ;
710- d . getElementsByName ( "CO" + i ) [ 0 ] . value = v . order & 0x0F ;
711- d . getElementsByName ( "SL" + i ) [ 0 ] . value = v . skip ;
712- d . getElementsByName ( "RF" + i ) [ 0 ] . checked = v . ref ;
713- d . getElementsByName ( "CV" + i ) [ 0 ] . checked = v . rev ;
714- d . getElementsByName ( "AW" + i ) [ 0 ] . value = v . rgbwm ;
715- d . getElementsByName ( "WO" + i ) [ 0 ] . value = ( v . order >> 4 ) & 0x0F ;
716- d . getElementsByName ( "SP" + i ) [ 0 ] . value = v . freq ;
717- d . getElementsByName ( "LA" + i ) [ 0 ] . value = v . ledma ;
718- d . getElementsByName ( "MA" + i ) [ 0 ] . value = v . maxpwr ;
691+ for ( var j = 0 ; j < v . pin . length ; j ++ ) gN ( `L${ j } ${ i } ` ) . value = v . pin [ j ] ;
692+ gN ( "LT" + i ) . value = v . type ;
693+ gN ( "LS" + i ) . value = v . start ;
694+ gN ( "LC" + i ) . value = v . len ;
695+ gN ( "CO" + i ) . value = v . order & 0x0F ;
696+ gN ( "SL" + i ) . value = v . skip ;
697+ gN ( "RF" + i ) . checked = v . ref ;
698+ gN ( "CV" + i ) . checked = v . rev ;
699+ gN ( "AW" + i ) . value = v . rgbwm ;
700+ gN ( "WO" + i ) . value = ( v . order >> 4 ) & 0x0F ;
701+ gN ( "SP" + i ) . value = v . freq ;
702+ gN ( "LA" + i ) . value = v . ledma ;
703+ gN ( "MA" + i ) . value = v . maxpwr ;
719704 } ) ;
720- d . getElementsByName ( "PR" ) [ 0 ] . checked = l . prl | 0 ;
721- d . getElementsByName ( "MA" ) [ 0 ] . value = l . maxpwr ;
722- d . getElementsByName ( "ABL" ) [ 0 ] . checked = l . maxpwr > 0 ;
705+ gN ( "PR" ) . checked = l . prl | 0 ;
706+ gN ( "MA" ) . value = l . maxpwr ;
707+ gN ( "ABL" ) . checked = l . maxpwr > 0 ;
723708 }
724709 if ( c . hw . com ) {
725710 resetCOM ( ) ;
733718 b . ins . forEach ( ( v , i , a ) => {
734719 addBtn ( i , v . pin [ 0 ] , v . type ) ;
735720 } ) ;
736- d . getElementsByName ( "TT" ) [ 0 ] . value = b . tt ;
721+ gN ( "TT" ) . value = b . tt ;
737722 }
738723 let ir = c . hw . ir ;
739724 if ( ir ) {
740- d . getElementsByName ( "IR" ) [ 0 ] . value = ir . pin ;
741- d . getElementsByName ( "IT" ) [ 0 ] . value = ir . type ;
725+ gN ( "IR" ) . value = ir . pin ;
726+ gN ( "IT" ) . value = ir . type ;
742727 }
743728 let rl = c . hw . relay ;
744729 if ( rl ) {
745- d . getElementsByName ( "RL" ) [ 0 ] . value = rl . pin ;
746- d . getElementsByName ( "RM" ) [ 0 ] . checked = rl . rev ;
747- d . getElementsByName ( "RO" ) [ 0 ] . checked = rl . odrain ;
730+ gN ( "RL" ) . value = rl . pin ;
731+ gN ( "RM" ) . checked = rl . rev ;
732+ gN ( "RO" ) . checked = rl . odrain ;
748733 }
749734 let li = c . light ;
750735 if ( li ) {
751- d . getElementsByName ( "MS" ) [ 0 ] . checked = li . aseg ;
736+ gN ( "MS" ) . checked = li . aseg ;
752737 }
753738 UI ( ) ;
754739 }
817802 function addDropdown ( field ) {
818803 let sel = cE ( 'select' ) ;
819804 sel . classList . add ( "pin" ) ;
820- let inp = d . getElementsByName ( field ) [ 0 ] ;
805+ let inp = gN ( field ) ;
821806 if ( inp && inp . tagName === "INPUT" && ( inp . type === "text" || inp . type === "number" ) ) { // may also use nodeName
822807 let v = inp . value ;
823808 let n = inp . name ;
850835 }
851836 return opt ;
852837 }
838+ // dynamically enforce bus type availability based on current usage
839+ function updateTypeDropdowns ( change = false ) {
840+ let LTs = d . Sf . querySelectorAll ( "#mLC select[name^=LT]" ) ;
841+ let digitalB = 0 , analogB = 0 , twopinB = 0 , virtB = 0 ;
842+ // count currently used buses (including last added bus)
843+ LTs . forEach ( ( sel , i ) => {
844+ let t = parseInt ( sel . value ) ;
845+ if ( isD1P ( t ) ) digitalB ++ ;
846+ if ( isPWM ( t ) ) analogB += numPins ( t ) ;
847+ if ( isD2P ( t ) ) twopinB ++ ;
848+ if ( isVir ( t ) ) virtB ++ ;
849+ } ) ;
850+ // now enable/disable type options according to limits in dropdowns
851+ LTs . forEach ( ( sel , i ) => {
852+ const last = i == LTs . length - 1 ;
853+ const curType = parseInt ( sel . value ) ;
854+ const disable = ( q ) => sel . querySelectorAll ( q ) . forEach ( o => o . disabled = true ) ;
855+ const enable = ( q ) => sel . querySelectorAll ( q ) . forEach ( o => o . disabled = false ) ;
856+ enable ( 'option' ) ; // reset all first
857+ // max digital count
858+ let maxDB = maxD - ( ( is32 ( ) || isS2 ( ) || isS3 ( ) ) ? ( ! d . Sf [ "PR" ] . checked ) * 8 - ( ! isS3 ( ) ) : 0 ) ;
859+ // disallow adding more of a type that has reached its limit
860+ if ( digitalB >= maxDB && ! isD1P ( curType ) ) disable ( 'option[data-type="D"]' ) ;
861+ if ( twopinB >= 2 && ! isD2P ( curType ) ) disable ( 'option[data-type="2P"]' ) ;
862+ // determine available analog pins
863+ disable ( `option[data-type^="${ 'A' . repeat ( maxA - analogB + ( isPWM ( curType ) ? numPins ( curType ) : 0 ) + 1 ) } "]` ) ;
864+ // if last added bus has an invalid type, change it to first available
865+ if ( last && sel . selectedOptions [ 0 ] . disabled ) sel . selectedIndex = sel . querySelector ( 'option:not(:disabled)' ) . index ;
866+ } ) ;
867+ }
853868 </ script >
854869 < style > @import url ("style.css" );</ style >
855870</ head >
@@ -871,7 +886,7 @@ <h2>LED & Hardware setup</h2>
871886 Keep at <1A if powering LEDs directly from the ESP 5V pin!< br >
872887 If using multiple outputs it is recommended to use per-output limiter.< br >
873888 Analog (PWM) and virtual LEDs cannot use automatic brightness limiter.< br > </ i >
874- < div id ="psuMA "> Maximum PSU Current: < input name ="MA " type ="number " class ="xl " min ="250 " max ="65000 " oninput ="UI() " required > mA< br > </ div >
889+ < div id ="psuMA "> Maximum PSU Current: < input name ="MA " type ="number " class ="xl " min ="250 " max ="65500 " oninput ="UI() " required > mA< br > </ div >
875890 Use per-output limiter: < input type ="checkbox " name ="PPL " onchange ="UI() "> < br >
876891 < div id ="ppldis " style ="display:none; ">
877892 < i > Make sure you enter correct value for each LED output.< br >
@@ -897,15 +912,15 @@ <h3>Hardware setup</h3>
897912 </ div >
898913 < hr class ="sml ">
899914 < div id ="prl " class ="hide "> Use parallel I2S: < input type ="checkbox " name ="PR "> < br > </ div >
900- Make a segment for each output: < input type ="checkbox " name ="MS "> < br >
915+ Make a segment for each output: < input type ="checkbox " name ="MS " onchange =" UI() " > < br >
901916 Custom bus start indices: < input type ="checkbox " onchange ="tglSi(this.checked) " id ="si "> < br >
902917 < hr class ="sml ">
903918 < div id ="color_order_mapping ">
904919 Color Order Override:
905920 < div id ="com_entries "> </ div >
906921 < hr class ="sml ">
907922 < button type ="button " id ="com_add " onclick ="addCOM() "> +</ button >
908- < button type ="button " id ="com_rem " onclick ="remCOM() "> -</ button > < br >
923+ < button type ="button " id ="com_rem " onclick ="remCOM() "> -</ button >
909924 </ div >
910925 < hr class ="sml ">
911926 < div id ="btns "> </ div >
@@ -934,9 +949,9 @@ <h3>Defaults</h3>
934949 Turn LEDs on after power up/reset: < input type ="checkbox " name ="BO "> < br >
935950 Default brightness: < input name ="CA " type ="number " class ="m " min ="1 " max ="255 " required > (1-255)< br > < br >
936951 Apply preset < input name ="BP " type ="number " class ="m " min ="0 " max ="250 " required > at boot (0 uses values from above)< br > < br >
937- Use Gamma correction for color: < input type ="checkbox " name ="GC "> (strongly recommended)< br >
952+ Use Gamma correction for color: < input type ="checkbox " name ="GC "> (recommended)< br >
938953 Use Gamma correction for brightness: < input type ="checkbox " name ="GB "> (not recommended)< br >
939- Use Gamma value: < input name ="GV " type ="number " class ="m " placeholder =" 2.8 " min ="1 " max ="3 " step ="0.1 " required > < br > < br >
954+ Use Gamma value: < input name ="GV " type ="number " class ="m " min ="1 " max ="3 " step ="0.1 " required > < br > < br >
940955 Brightness factor: < input name ="BF " type ="number " class ="m " min ="1 " max ="255 " required > %
941956 < h3 > Transitions</ h3 >
942957 Default transition time: < input name ="TD " type ="number " class ="xl " min ="0 " max ="65500 "> ms< br >
0 commit comments