From 3928ef3e6a31fd17a2ffda087e560e7304228733 Mon Sep 17 00:00:00 2001 From: whowechina Date: Wed, 27 Aug 2025 12:45:22 +0800 Subject: [PATCH] Supports banner light --- Production/firmware/mai_pico.uf2 | Bin 157696 -> 158208 bytes README.md | 20 +++++++++------ README_CN.md | 19 +++++++++----- firmware/src/commands.c | 24 ++++++++++++------ firmware/src/config.c | 2 -- firmware/src/config.h | 6 ++--- firmware/src/hid.c | 42 +++++++++++++++++++++++++------ firmware/src/hid.h | 2 +- firmware/src/rgb.c | 18 ++++++++++--- firmware/src/rgb.h | 1 + 10 files changed, 96 insertions(+), 38 deletions(-) diff --git a/Production/firmware/mai_pico.uf2 b/Production/firmware/mai_pico.uf2 index 05c979e68ac755f7e38eb7f74dae3daa20fdd8f9..a7fc89ec87b6a6b3d1735e46437cea4496f44fc0 100644 GIT binary patch delta 24091 zcmb_^33yXQ*YM2E(lu>p3v}-!X(3&J&=x4GX<9DbfU;VlXeo<95hEa0K&grfh`z{0 zr%+T>^i=@`q>4+mD*B3RBk)=lwI~u7)T^RRn(aT6o8|I-@Ao|4|NMWS=k(6ZJ#)4> zGjnFSY3+__JsGnt6^}ZU=Er!Hkj8zZHV(qtxwu27H=gtWpJV4N>;~06hp_#oy z9rql(k;nSeSG%PB#=+?Ac94WPk3CN8}q13m7UqG5PO7=)kBBty;ywIiABB3 zgc8N+E)0l0UQCqY0N*J`oQS;0c-CIQTlkDkC^sNB^+a(=aUd%ORS`lUee@K(URW5N zT=SK;s7kSqdjUVM5*NCWQDHY$8>^I}-KW^a?Zj;=@uU7&@pXT!xVt}E>~&%But!%% zkW##_AB)-jIkh^}V8-IGe&Ma?)S8*j)T%h~cTb%7muE1fvb}@g|4lA~wP68@LWyzW zby7N}BD<};>u{X7#Fbc`ScS!(eVjPLkHmMpn%dM`R0qQbuY|T>lB<~H@SN*^;DV`B zDN?&cM#MG{k%N3TM>4)hq|MdB$Z68j+kV?VR~1rKY-^I(x<-9*2PXStu;%IwbWoA~9G z#uhv(PQ2*cZ*kd=IF#bs-dTK5;JD1>kd&!S*ECh|SvIA3R49zkFWd~$Z6k=>1<%%v z*+|>LRYi-rz)#y*22yG}QW3W=twka37dFI?%-b;*k$Nbf4NwOlcHDnZi4w#IeVj4R zPEbm9g6MtuRD1;<7-H!se>)heZXnh+2*VPlkDL##HNyRe#KIH!Luyj*vBcy+O`9Q*r&|O|m)3Sc_a$1tqol-u=wwJ$8DG8Jfl3t}suhL(h z)p8Xg3WNw(h~@U~t0h`4#nKa7hNq0TN^mpcbQ@1SK8Rc1nCs3L`UzVv}%9=KO(7 z7H!8i;L260#KV4u(;^(t9BQ(=HM*#wg;qVfX6`%^Z%~L@ccE1=ByHVz6dO})jx*0A zn`0E>30I+YJ~^{4?1O?PJ8nbB#yi(3#8=&Vq|q^~Yv#rmGfIE=vs{D{qw_k@X#_f~ z#^b>njZh=CI2LMr1vMIjHR8<0W>(`#m$Q?#5^JqJhqcDOIszr%Rzz!vN6kR_LPON!>_~110+dDOUDRHzQeH;Ms`8O4F$<`+rmGR_K_MZW9oe;C^X+_A?aPC5xWF(JP!C!w}RDe^D2PPxn^4!JLqotnPEk@1$cgH)GfSzWR`hu#hePI zcw3m-N*T4~0cx3xBGg!2TSr0leFqDwV!&&Vj`eq?_?=U&W6ym~Y(P4V9tnDQ|CBJj zK-W;%uM%H!XH=`ihuxQSn^h`ti(8$k57r>@9uQW|#i6NGPyMr<(Eep|WQ@`HUIkJ|C> z5fy5?b=DYeGmmuF^0!%eevGBxK8?SQ$0H{#e86(~oW_{gjf=5KSsGRF z{rJ(#L}euQI{FF18!aDw5p^)q55-QcA+F%0J+}m~Vm@=!6Ui$BtwB8_ zxTFlJHBx1*!CaTQ&Ozb$aNUO49%MvzXJ@NEAI(Ip@qg}5)MZZ9TMMn3Rk$S0oZl2B z>Rjp5(yL8Yjo9x)#=+gOFp{5g|MsW${*|v7t;m?vol@JX-_18w>JZ{)B8`~fzyD9| zCRW4c``n?aiV|;ie(Y$~#aC5hjhN#%TEB*u)zahCljPg0Zu|bek3B#L2H|9>(B+r z$3g^~0YV_Vwo@Ty3;xn9C95Nl(G9v`5J|=d25)C4(krKJ?T#NAjb>J-7staa^@$V4t zI&1B^n3A)HlL})`v-dUCL-g!D_yoLv!mkq+l?~?2!P4gfr4K-@2i8RuRs?E{4ZhnI zcy|Z9D=~cBh{q=a#S)-6vYOME0L3lNu)heJadFY11{b=@it!-f&$2?rd#7I&O2$<* zs;YDKO+1@SivvUl`XTZHRh6Kowu1>4ADp^XwJY_hRwWFzSxyY)C{P~3T}c!*%4&b# zQJ(@lggJ0fi1C>VLIPZqiL{I|s|D^}tEf_ctQIHv81{$IkKj1EZ5~Xf8P+yyJWLXcVN}@V{o-|ZL^@$sZF-d05{;&4H`18 z@q%}PA<+b*%GmyqF#Z}LWfewDx+(Pn3>8;d5)re7tpk=m;D#FU50`Lc5@G!-xS%t~ zjS5-xVT-CerKMy+mgNsSCT5GR69yh|R~Ao;9bfZT^V9grd~e5t2i4*%0biAqi}mR? zg}B36!*2}qShPI`x!uNqLeHV2}(O0SU>yWuVXk*H}=EDY;c}n8!vv?OEwih$+HQOFntP6(md zOh_%}T*1@k*b08iR^AW7vmRQoegw=l;43>qJ``-#R_0q81!BI&eD1&cTcFj0ezpm4 zwgR*P><54WM0lcdRM@iw4KM4lCoAg&!cQ2*e5ebsna46e%7fL|F_vfk1eALrt%0%& z@VpH0KBPUu!+b@U?uJ~cv2taNrK)X3?=`edi8&~CLSGbL?RPmgsjxO$jkT$6)+Yu) z>Hhwu25p_*dZO`cl;>tbWyEk$b6um9#@iY;_!z7~I;<*y?CVWryGWeo9>}f;Tn* ztw6jy8xl)TZ|T?AQaS zOfEKVEbd3z=OBL!(qZKUO-*FIvj;G&09}Bk?T7^$qx(w@B_tXK>5m#?8k1v^qLLET zP=2J}085FqwQBLDewc&-pK%c3S?6JOnWm}zG2(6yMjA-(?q6bv4_G2mFI2?aW2+hU zhoMw&jhR$sZ;XkGNmPrQftb4bcSj=-Sq#94GR<2cy%m6I&gPAQRj5NP{@_XhBHs%K zY6h1s>W`m5kgxB6)7LlIiAyo`Ahp=gzu)qwJ(k3m;xS+=R~}M~_|C3`VNMJUv(!_| zC2f#ec!;cp_X4~90OtTM0oXcFZgSP#VDbt5NmCiCBVJs}AsviDYO%OKy6Cb!n)v#j z_bO2gJfHEZP%J#3^r}%DJRkFxAhfLj@RcBRdOrto@MWM2yExkTSoo*yV3WfU;pVy= z@~BT51ERz<4=y!9ymQ*EDEiH=0(a};H>rdd>k7zeR|Ve)k(#gX6E_D6GdVtYy=nQ& zUd5L}I@rcjGfko1ecTltFj8k2zsq$5@M8&>(a9p--pUF-dbB{MrEf6%VqnjOpm_0S z2=x@*Y>-cJ{pbKy+!q~XAaNCd8K54Z0f4m(A7~lmnq0?M8LVmRq6}Nq+Y{qp$*UGO zxHLM0wY=(@xoYutml^0?a$Ysf3?bA|t6H?Vw1zf*;?$#+sbea5bQLVo7~&Ea4#;(M z^5uY>_d88!R5h#6;7WxGM=J?K4`NRXW6M0eKH}jL4V$wt$h6!=3`XPvJ`-HJU}}tu z+2dN^V+imW;nD~A6v+7G1^G+~N;Snf)M`Sh)sw*PgIxuqi^njDZkdZmtHngutJ5rf z0pUxX2CKG;Z4;$ zUL$-{KbSK|B9`iW$&2%4UM#VIT^XytZ;?9+q2Yg=MMm-L&yEV1BOYJ%%_?8td;R{t zS^$IJ5r|+?e8TXVsc{xJ(y0-vI8m4})35q@`eIQ*8-WYGr*yyIA;La*ZuYI|5bl| zZGCkrYJ(lA@y=AkPJSYqI_Hh?_<9)J&Hu~vz&HXgu~g0&FWX)^(s--6F3T;a5{ zg*)bEC2Bp`SYa<}nO3EPf#nI|`MJ}B9rfzWiJ@U?j?3TomDAtX;Z5m<1q+OQoH5=$ zvSnpU`Ph-;l=7vjL(2*fXcL}-kjHRQT1A%ywZ>gamFpFoNOsYk2Ih5GI{3KW1bBM9LhHQD#?Yk8CUE)O{UJ!kW-%D;joJ2SE7JMCTEgbMIp^J)W| zi)>;JP0B`LVq;F?txjZAcVlC5_h`Ut6K-rY%-Zhp_3iZ+LH;gpjHq&AQ~+s}k4?@e z8rg(B+CH>;D3rOqtZYbdzE+8UdOvq8wi>G0Ea8TpnhU!|q&i;wOE}*+Sal=Ny;<-! z=7dc@c!Xs75i-+b(%}%uwV4IXWM&*}K$sa>03e_IS#-i;n0mM1yH;PW2VUO3#eQ{m zUm&&0jZqc!nftx0{9X6I4#(@tkV2g7EJL3{S|OU9cvJy;=P)j1eQTAluqk8AS`-?l|E+_0z{8$>jutS|Mt~y#d$nLCd7QFG7}=c9 zS^Pp{b9&UBz8t7CU$~>WF#dDTpg?e_?j9KOpoN(!A@5O%lRR8+3Sf`(2wlyl4Iu|+ z5u`kFWU--%!`e`+=EN1F~}qrN-FxU`!yh8-`tSaQ5#Su+Vmi!(Re?_;~AOyGa} zjvYqB@Y$@QNvF#}h`sLUgVKx_ZJh)@T?#$t5Br4Lcv#H)b)!rQ@k=kt_4OU}di##~ z#z2jMo-q&@dix&qhXO;D80V1!LvP>Reij&d`|k9!z|h;b7JMr%8xm{$p~%n}u+4i8 z#l8g#j6i6W@crT(V?Kmm&!1JkI0ofq<(5h=y2_Vje4Uf^^Sm)3Cda_WeTL$!7$ebg1!Ui zM&YIwG}cJGw;$df0a!8;X-i@3_yN-I!}zaM!n6hO+7LK4gl8$hT_y-;0jCnuVx2oudg4&aw3LZ~LZTSi!Jys>PWg=8M4yCCr zu+Z`-uGl`#oCK>k*!5t09#_MzhXBWqCOv9VKd%;b!jm^_h%eLO;^x_SWOE~qEV+|M zOP)4b=4nFFax;ERxM}&={30Jl2t52-=wwanynVTl$B;h*`8|-&gggiNFNDvRkIkL# zy=D5*CRY=&mMwgGZ5?c3+Itgv%HjEQ@8m+%{Z{XdwX#-T5f?rSoeyhXWS`|&2K{HO zFm^@Z)KA@pwM=W$G9I-4z`aP8d)vLzKw8G4@iweii$~qxtz~#v&oB?U^Whz<$#O5a z3E1jJ;n0e-LHBtrwNa=YI8C&VMMzITe&iE+SC}#fy9q=LOMSf;*XOV47V}Z}Z@q@i zg;n!kSG0x&zPd~@Rhi6G2?l>jMeBDZ_q4|93#-*Iq@Vz)>AvF^*;w$BlUVO`z#4Pi z5Xcp~-f-l>bFVYgky#tix%;PHp?*b5*2F8+9estmBZMby860*AZ`lS7VeQP;|GBVF zaS1>V`48b)1+WRA3!q6Y^xKBSUk$VRmd0JT`1{Uz!&(TMm9jmSqLt%DP4GsK=!4Op zwR5W%=~(-&hbL>-7Vr7BdoNA`nG>BC9LUhTUbns%?Dn|OzETI_OXtco+$wyzQdjz? zXVE%5<_XJRXL~_6W@ThwcrhDl&UKz&+j!B!8#Y(*^IuPHjRu>|YW)NL2ZdUd84h%X z*1^?Ey+TyM8426@{OtP_xZrTaf1M*4o(KKJn&=n+seS%3hXOUP)2(BX+i+*6yV&Zr$BK~lIMKX!K}or6<>zPda%k#0A{fX0J8y10Br#K)wl@a%+k7h4>D)5xyUW- zc^^bW4fgMD>^ZC!m%7Y`-q})mt?>Ekfq0?t{pvB=_gp!Kbi|o&uvuc#%`e!(R!$x- z`O0{iuLwnJhDUwj0$D^sSh&WJ%7n3XXzCDo+WM_ExdPd3omf=6h23k)uw6K{rVuX? z@NGHqx0(yh$rW5w4EM$0FbhG`B5%l+N0V3fm)ELMBDzYS2FtNFK297coN5`CKNk*$ zl-*asv5=Wph}2nZNX%n#;XdGagJZG7+jpYB7@|Toqa9eBB~O&ZpLm7%Yg6$UVe;Cc zC5HZumY>h2*J<@S2!!Iqci0loUgppm#v$fEY7t@@C>i0of_9&lYVtj9jzAduF-!Usk<gXKJZGrlqr?K2K<>UQ+x^}c z7Y_GMq$^)~j92d4$t!{6Mj8JKiDZS0q#hzxv<(}|U>`~#_l1z}kEo+Z=wWPNaV>Z! z*%ZQHE0iq}rtG#sXlq>{TOwFFyu_53FJzy~Z59-s|2#0 zsIIAEH_Ik(|6Im;N5mL)aPc8xj2*H@t(EkvmFb7hioK&VUMep|6Olk3m07M%Lavh` z@3wwrMH$b-QQWU*1kx5l-kCWlr1r7#h)l4l%*d=Vt7-)Ou6GA9aah)jP$_gT#8BW> z|E~0~_&L)gUejc}2%PzPy5Iiux_@wR#JD@e3lGSdE+J2sA#ddc^Sf3|nk$o=1oC)y z%&BVFkxoIRPH;UQIUy1>dm?6f&<~>;!PRDq6IO|PWDI6V3}(m}{G8s9eNTDM;Z8^U z4FY*81)mZ|JynvF2dHoZtA?vW;=N97XML=&_Nn$@FM2xCgiQZd!&z0T6(tH}Z;@&> zxPV8PvG={O4Ft`z5!nX7A-!Pv;|mPOM*?|vHMcQVc=Xu~k?uKL5^=UnM06hY!l;el z$oxKmJSWpGy0q3R7DoATtIdh4Rk|u}!$?vwwzb=YSLlBV?6HH&tV_Ycx@IWX$OnNO!~>Im;yE zWin)CH(bp#WT;vvA3il0_mybqLvFwdqR^I8!yg8Ow(>nono^< z6@);HGj$qf5?nj~hE$kgZNlwmuGX;EA!B_ie08QUo|WJ#r4Fv`O^MiSg(S)fnJ5J2 z{3Xy4F?fSO&V`ixDq;qn|2N;&vGHIVpRi9LUxzTj3^s|JO-`=6hDQYj_W0s)(0_X) z$b#nEh)QjIUVGfb@!QRCH(DUyMzC^LN+egxNX9;%Y_8#z!jSg3odUTav+ODfd6f(q zZO6iVK7JPxiy*qX7>Y_95q09glKHUuXmBFEKz@+%zEvW3tBf2T*Ib#D@#{tG@%shx zlMH!{guF(E9KCNhKN#)~nGEHi)ujlsp!qeTdv1FJC}U_xk76p2UuEjFNF-ZiB#}P0 zEpBINtq(4r2t@oa$)tgp3H4r876$S)?n*Xeh_8RY$QtdBQm!&i znES_DVOJ0|mm?zK;;5q9gk=DsXddd@Ph!<*A=;W#M0{L4e|9S~| zy$l)Y6Nm2QHHhma1p;wy9WeQJ*xP{>TRq6W1E5#vbY3l!sy`xC#bkWCf{zn8Sc3_q zKh%`&h?3hX1{7;6?&jG!i$FXPQ3Q=ILI)Mb*jo)+ecaBoK*tx+?hy@+V3i`Ck7*x= z@W(pz2!X59`H#481{zQvw;#Dp!#B*kg8zStgSXvy5n*%fI5xO%LuL~2&xla=AKACWeGrIKgR%2@0lq#N* zc$~?#YUreR+>DRWC*$!*Y@#RPae2U|)T5DEX(Z~JlDiBQiFVvhNdlf{*y{hnf%Q?f zGc8YoAZEBZv@IKv&G*7td@+Pr06Xbp3AiOZm*#Gg1llAM26bG_ZLW{0*O9zgyadx zLLW@R*W)MXUr9J{l__vlFlUP-&K9{iMe6QFR#X_*-8O}i6nrg?r5jRkTFfvorbOz= z6g&&((7#gPU&!^SW{}KJ;Q|nxpRmhNSfpeSF2ap;>LBod@90f~a8~k0*mA{s1&HYg z8m^H(HV7Bu4tjhLmiFhUXRyrpgqf?_HSS0RnJJGOy@o**MM;a(8yIB)aA}+Qv8Y<&`s2NWT&S~f$Osv9n--e&8B z`xx$T9jx>wx{Tm`0n1X4PS%X6O{5y-BEx3BpoJ1WZp>~4TlBzH+bWpi&%yIf$b%Hf zs)H-333QhpuMM|Q&UQ)6?J_ar%nj(Hm|>|0QVYN_a<8!I5LtT~TToP2qQCDo-!um$ zLu7b4J0z+*WK^4PCoNCIK%?+{c99YzhJKv@$7MnJm~2e$=b&r>m@YSgerCj>3zuxv zERb=|GN)Tm_qmIUZjT*LNkNb^^%TnNqS#fqtZj;FTQr<)QBs5>vjo))m(fB)Pyx{T zp3^V7os!`)Ehe@}TC~ZvsMOx>H~!v@zzJsA8ou}U6?^Wv*u{tRVDd9v>JsQA6aF$h zOi@j-oTvTk3srn_OZ4*yI&(3u9KhO0Y9H9W1e$jt}2JN_=4RGg#nB~|{O4v`zu-Ro%c2%_Yb~QM%MTSi^l`^e%^3OUr zxM1=$jGUBI0$ykg&Usp*`m~Jd;%GMY>&>Vay-f)p#HOApGOf1q)y4ke?P<-Gsz)iQ zl5x&?MxywPjA9$Vz|p`ndQU?~s=1;~>tu4mxvFDtAzU`r!?aH79vywG5Z zPUkq-7O|z$-xmcn)8Hrz4f!k}+YfZ)Ok9oE&~-EM2E38FXX3Q*XhSu#aoqS28H0`M zMR`9sP#*gRq|Emnn4fLIV4V_GTi6FWOMQlx`B7gdqC(|amLEc=%*LOFTgSXl(t4jv zYxQ%gmR7QfAB?i$Lx;O9LwkB*D@@Uib=4N7_=A@$i{#GJ^0_!ivl9w&E;8b7r6|yw z=HkuxKH4`IXUXJlkQvN$A}+oC@S?lgEQ%gV8t^!rN#{4DUOXlI}xQpI159i}m^uRoPeQ*qzFY~UD zHmf{CH&fDxw?jzqawA?C=A84A#Q7x|=W8k+IGm2kVd3wFj&?0J&#=4Y#`wsM487Jx zcNv1RYze$&Ho4c03A@kEI)9f{gCYhzcm;*$>7i@!z__L1iVLazS{%8rOEpb0D?ZO3 zg_}|p=J^~aP|_rK=0lS7hh)+})?1N{1#DOZ_>?ruu&HLTjHqitwCtftH@cVJ4EUO~$$qcyKt+v)7MnrEl4lvrhWIUSOO9Wn_=qPzL* zs@B|%{9cgo1{pThESFKeO7UuwbEtCk2JT!bCCh_&)N_+eLOg1p<>xcmQw!k+J0&ad z;vo~@#H$tlHo#vC%(C<(*)23UEtA z?;V%A4jY=;u9f_cl6>w{X;rvhX4Neg+`~bP(Q{baNy&Pg!bO+S(d+TG`CA}P*aT1s z_x8D3MNLLaJ4}eSDH8^PmEfu$?E5s?rgZTqY3F)8J^Cbke!*Ua8zbj!$trJy6r-^8 zDR+G!gop1tT4upCd8f>VRMU#%!kWLngUkK#-8`&K+6K$?2)k~PX4 zJ2DJ{LqsDo*eJOh2!`fUs@aI+(n5@lsJ}1DcaMWfxE~C8iRNv@U#2qOeiN?M9sxK9 zVCx{T?U3FQQQ|#GiT7kmB%oQSJvF-~1D{Bx74VvMErdjajc6a{=SDiZEz$-$re1J;1U>?$$1&%3yGhZjJ4A&2+gw0s=T`up{uW( zUYnat@7sbO3L5DFoEg3wVT4u;wU&|Uw&Fh{`u$51;a@Tl1PhG%h6YPT`6-AI9>PX$ zb_%U|2!9l=MK#-G3NET-hxOOnRr+}ldu@|-;GE+U_2V+?XlNFE(CO?O>&co%$#&er zsPEb?TPRS?4jJ_~;3p17b>pJ3U)=x(*@3sh68OLloDoYvt7w3#6#B&u2p*&W_7RzL zC>};at}d?R^;8ph+oLkeQ%#$U#o6ey=tTNs{Evr~&r#BbE4lg@zgJL&hE!GRkx5IRZUc^uD@vDzbJrLgG??$k8f z=2Shf+9Oju$1W*imnpK*l4&mlrfK#YTPS%7*TE9>rl)XZBb;jX%80+=QfC)+<6+~1 z`fm-Weu^I2ix-9E$vGkMIU(cImRZ5)qcevSN?kC3K8tt9Fy)xt{!QP17E8B)spp?E zd&GGEaxesTv5%7HxP}hhk5A)wC_Vr~4)Y{7QkH!w@%U24<9@h=DAmRj(XhYlyB%EJB8Vn8f+rRM z+zZe^$G(XFiFeZTFG8ReqDD0bWdqVj|E*5mLHq>P()SOBEpbR@iNWIywq3lcJVxb7|)h zoHH;NlBod8Wy5+A{rw2e4a|dqO@_0QEzZhpfgn;7J8-TZr9)SK1zS~&b`gE>6}%yu z4Ov{8&o9&es7y94$p^PX?}>7Dum#gmya`l){wTgNjGptA#PBN_!*uINgtxbEQtMxC zrR4Qs15?i%GDBgcK&kKnUk?nMl)Nb)jlPj6ej}rZ$XV4{O>Cj2TYZp{xA1b+YOp~O zefTYWPgoSH>5@rqL|pJvXigO+UD%ey+8w^kL`-G2Z`YDSpX$OZ!}W8{ON`IU7~hcn zNO=vP)Y3Gnc_k(9;x}Vi?QUScH-%n&7Y`5DrkZ0i?z{L}-c*yJeBdDSgJbxphAA3@ zv}#sgq~9M4&y(-VtfI{OZq;|UXIav^pN2mE0j>mdFZlq!B;)d-j0@L(HtMHSgKIfm zAsG5YJR29$TR((dg3#=m^Sz|f_cE2tB|9x#x0y%qRTd>5UD5qM!I94{zk`oo;nxtV z;f!6pt>5XWf*~yzw)u`Tn#48*&grX>cmsR~*2T}D<)7fsmAZ?;+nv<&scd4?{DHL3 z0E`8=o$5ZrZ)Y(=7{T=g_JwSK#Y3>&+{Lr<8Pxk3j@*Bunr@lOER+cWCEYkBEen`N z$W#W%Fj>>+)NcH*u;p@oluYrX%oM!!(TYR-(rJgScUvg=;tErolrdG?W38LhH(A?L zKeAGCQihlFvqbf08P(6=^O>VVTlu%q6qsoL9d`dOWewd7zm*_V%*#kDKwnYPBd7R_ zMDZ6HMb7jz>~4*B6r<8*l$?=aQ_VSV&oTStm6g4d_+lakSg_@m+6yW$D?4wcC51Z-9}2j zm33#Txgev33fO)f_%^a4sQ;NBGW6|chZ(P6PeVMYaGyHR06ccjt&l*Im1Cbj~W zKv#F~vW)CjZnV2Xf)H& z*#R3!)=RJ-u;UVLqNTq=hrWCX2JFyulL5CC5QN?A{#qys#z(lSjXZ`$*{=|`tY&p8 zf5oObn-gFv3BGHZg5Dmcf=@WEMcP{d-hrD9uA7i{$x@_U2G1X%>|uaI0Ph1#u14BV z;W=O`yn|ETXJ#QSd~dEjPmli^oXMnBhlt~}AwgE&ynOkRm1GhnA_R21=>btT@@K#& zxJ+`FO!BjAZd5Uya2cPYDS^Gtzi?()pxD{gWvj@tRh0aVe@dvETuG+gdQ&rqxb)`8 zI8HTA95*|}j#zG8z53=A<4E4(SVFF>LMj$EFI%x>HK|*6y><0}MzJhhv0}-}z&k`| zJMj@ZFc^0fdL;vRWdJ(FfVurLZp)Uh2HmUS-@?_)ZeFp97-#X-`D8vNzFm#SfUP>PU+ zx3*l@3}%J1POgdW=D3YYRxHy`$;nPMbCulH8&+Q53^&=9tPE7VdBr3tvuI&+Fx4Ee z^CX}jK^jNqn}!b`IchTdJdLcjE+LYK3`(PZz5>=)}>%ms0+a7&<4#yJp-<=nU=jw3ch4E zONNVIj7jVjOp-WaWS2b~0Ga@{!DTxwz;Z~h2WSJ>56}fb4<>Q2?~uZkrm!CvRHK>o zv-M9s)&qoDI%t-GNi<;)7dt8B_4j2exKDy8Z}=y?(j83id%0=Wv0(cA-J3QX38qho zw_5hc(#KP{^bHAXk@j7<;lMyGONYwjPhSx4@8FZY`xRjOI;0%}b)O182SJ+U|0gJm zyc>B15_@qs+{w5Ti2hFycKQwRlC|Sy*8ZO={r@UA--)y@f|l&L;s2HB8PN8BLxbTB z2dl8BOn`xb%Kw!}*gR%-?FLMv`48q{mA(ZvLfrQUZ7wHv2F}a5RjC_+%$yb0g)0^> zU$U4~&zwO_EqN`m5J04JRly)5gNuAG@xsG!F+f1tX#f}Cg!XTMlK?dU{qXKrfE|wl zYk+s5?3LiN8J^RE=`xmnM0#Ja6KOL6v;YjBK|h>sva(Pfo=NgdAibadp3WI&PkaJt zzXx~_j^w-mSx>|@j?z6Zy|09H>vJ$d0n`JqJcF=}$HEpV zvvH!##vvs3jDE`Y`b2QWNPzncQ|tvcZN@fR&R3+i{E*tTw{a8MS6} zM=2qI6`ISbM$bjQMLY5u(*6PP(d*KarT=eW6V!VbU;@;87NG5aSARp8$u_8bH&mJ# z6ks$wUkIlEw}NCsedp2t8^L2E$lBeyL=ZBhqpsd!bRwK6q z3npA8&B#Tvxk=FB+#&U_db|LGx(MI_h=%KFDF6hZ2p|*uvrR(}7jP@!9sxHgb`9)j z{cP7;e~awcCJ&ax9V}x^twmfjSMQ|LhI2#MQhhjAK}QegiUZbWKmHcE_&>1e9`<8w z2tHsFfA-Tg!@2FLcbj4BU|1jwWVhsNaY@@0wqBvc6xJA~%QT5I>^aFi-o#xCTOI#0 zao5200?vp>(&}O^C)ms-Av2|4QKy)Djp|Cd$hA?b87;FFwU%-u?HXTFZBk8o;GzI@ zv-)TNEkFu=vXm=<3q7Ywxmvhyz?q|e@kSYLw4wH(x?`?r@X=f@Y-m0>nw|ZX$V^~ZYsd<4{Pc8aa@)b1NsN2v3_Cgyrt<&-*E(uG9JQ#30!PeC>%h! z*%dQtGn@Q&^yL)BVd=|z&I`+AEsZ99@h?6<09 zk*C!REIhQc%5$P@C(oAZlSmD`g5$4Sz+OS26@1gafXU zaJiCjxmVEPx!IcETwT21?S~c1fcm5KIL}=THy>oANuE?QPgYZO+|`1skoXAH?V-0; zaZ~YP`d$?`6wVWVuHr_)ZHOQR_H#gTjcd3}e3sr@13!2B3q4c=?QxKvg`DnP=MBsD zEn{E4Al-mKw_@L{H9CsX!WPnk#JlOxIxa`Q()ku__HKpiI_&BWyCxO7esm`^5xa^L zwA|E?<%Y`Srq()Um}y+9X(8NJk}uO^;Ig$mKe_(Oe2^?2u*4|Y!ziPxnt3Ji!*GdQ zPV9~uSLPQSxH3QBztsO_0eyWMHz^8yjr~#oG;p?pdJgu#^0^*qK%B`%PUIMpwjXJY z07U>8M#%BhG?!D-&t`Jbw5uLgfMh1O7{=4CK(t;UDN!I(0_tSajWf9xxIy8a$r-XL zO5roK2avWL;1FDpJisnVvOlO~qZ4OyBiPK`!+szo5(BaKS+Bs?-7qEX0AN#-4WJF6 z3IOWL|9*!Iu0WfPJoypQF8l;(pZ`>1@($z=eU7vX>~INcLHd>Mmp_zD0sLRY4808X-~l>@hQlk`WE(40OouL-|pzJiJ9b0ut9_1xf%wJ z1}--8k+%+SeD%^?4r~{kbCQQiHX0_g(W|-!?krrzz)8u&rMJWX<6Ek^R@NV-TGV|V zmlftmt=BSFYzp_Zg&~I>DWgcOO$@6!jK!|hEtZj{)@FvVScZ^XB1u+q1)aqVZK)g$ X)>y11OJ!)UT9*bVU8U*fL$Ut{-s*Z_ delta 24237 zcmb`v33OA{_Aq|V&Cs+-O4=0YM7c>?NSgwsEl{9BnwF3bK$(P3r9nxy&m$xYIG-}N%g2;*WVjrv~h=#4eodLQnl(!)8@vFY0&rxhW(DGSj{ zi?9YEDNSH$5wk+7R~su3J^3-D874!TO6EoBKNJT#kw#XnX=YE%pe{23oW!W*NlsSh4LiLH`?tQb^92!Zs9@z}x4 zRcNce3Fg}*`>EIQJ{dRLkBkzxvC?Rha%Z|FJE-lrNydHBmB1YdC2-GnDY$QanA;rC z)zC=FHFja{cVGW%9hzarT+qjSs7S3E;7zqDxnBcH?vFrUh@}Sm!2f%E2K$UVQ5@=} z&_9IUxgo#@w5b zhL12$BoyKInGX`uYJLn#xCNj`d=POLyol?B^dDWs_8$gFJzfHfeBz?@i>xH|lV6(E z;=0$mw8H4b!<1aR_khLcKH`yb?**q?Bc@hnYNJx7*3GOdxAt>LxkHRem1}wkq}xIx z`auXA*JmM3Gi6h7S-?-zQUFqFno|+AKg}rt{07y~oDCxpZG-$8fC>Pv`H6#4l*m2N zO&N3CG)k#V<&p-TgN>Mnk5C18Z z|CD}s>e8`@mLNn^CftUrTyq@bTdgIKr)l1L(56Q^yH(Eh_Aauj^n^R=Qr5`#(GHcK zaQFFC`|2Q0xc}wb?U2>jz~D5o=M~c8vJ-g+NEA4LoTI!nN@)o9vPSX=_HKg!n?U z(mada7%$;I_nGXo>2KFWe6Syt%p_(dG(?TOeT{^B+pkAz9YLFUyQ+|o;<`wxhZG@n z1PILmLZn1fq{JL3ky@AlCBA_Yb0Q^_=0YmA=N~xYujj|$Sj)TCWdCyR0CHX znga=2DAc(vtd*uYr832qz`g5}=;Y7?JmZ(>;vjt5kMua4^J+Lo@?P{4LV1J`H!w^m z8|XMfvg&Monq6iq8!F@a0@cQJIU-FcDulN=tNJ_ZPHVsF!+rWw;eM39Eq=3KrZYk( zU}5g2d+R3qRrwd)rN_2dw_9Jb?zeiu;xnKY4$?fG`G=pO9}O4PKY@=iFY1Tj`HWky z#TO3$tS27Dq-8Vx2*;yJgh(k;dMTuFb?OG5X;qgxyhZvs=;}e`Fz=yKsr0mw91o&L8cf_zGT}W=`h^Wl#fv2N6uk!JO zdA4}&fe_M>Ml0pweR3TM-Ck@!I<+1#diZ{lc`Z*jcXF4EyWr2LlyUF+Z|FA4WZXf& zJX6N~6``*CIgqnVCk^kzH{(8nX1uUEl#^j!SR-Q=R`0ORZa3JQtn=*Kt>xDKDhZXEF6CUz z&isCTPXwy0lAWKWX%b9kqq#Lbq;r%@xp$Z^^L6+Y=4O8Xfh_@vw8Stl9y-INe#QKqnHtcha?ZK{-I)*<+^*8745{kv~|LxV=T66&h!bX8f^Pg@)HQ>ZQ0EPFAG?KpdzHETkgDYdOg ze`pTM)3|Kb?Z`X>Q=6TNlSw`_d<-R~FdS+<*`=_I1TnZ%F0qCsj~C3D6~n z{D1tVd1A#oMmsVlyHctf^}DQf6*`2dDM-y(LQnjq*+9q*3VrEO+v2zvyk|U(I+d*w ztD(a(+JA;-)Br+mhS6->C`1R65xonV)%g*K-u*a6Tz`a)K)RU3G|dv2T*Pt1y5i5y zFl$<_4pRl~zQN_@OeC4r$p;U4*uR)qX1E#U?uC!@gjrz$G zhJsa$V3i47I+SOh2%>IaItz0RR-2sjgc|i@CyuKu02~>Y2Oaa6nxZ;`6PbF!%H}(U!|J7SF-wnJdK=V?zBkL~&w( z8=xpLMmEHa0B0es8j9$>G@{MbHRv*=6JWZT0i2Or(<0$)%-zHLNlBSlO=&ZDJu^gl z4xgdB!&iV*;a=F=g7ngfTdY#sbufRF+ZM(*ykFAq-PmAqBv+@~0YI0Zn1LnzN-uQj z?epx7>D$XtF6CBKH(4%S40YZJwneIsNQSh$7Ex)r=6_pJ^X!_KWD_%a$ub3!>Fmli z>G9ffl|J2);8G1$pgStl3so@O{OyH~weziz&R)GHzUb1SWK+UL@_ka}06qDBjrpLY zzja(B^;H;q6JT#z6K5(9V~&Y@`y~ABp-_=QXFoFskM3m}eX$5A4X>p1ML_9@m!U@$ z;{MFkQ6}nWTUcH;Fp`YxqY^TrRaRu+%YNKe46#Pj>5CsS>qeC~$Sbq;byhM!P7U)> zh5iJikOn9PgJ_zG7F1+FmQ}CNr`n~^`QGNmkpFi#aoHZ+6||&ES{dqmvs(fVM85f; zHVGYq1Fl{zvB}TKxx4ss(7>fkS!wS+6T3s5RY3|VQD1P^@!fr^``Xg%5yOwpOp59t z=PccQ0Hc|Cuyjy^vAf8Qhe?a~Te!=ak>QzZSqt22p~hhEZ|Ze9e&Taz z?_|Y9T(r+PSy5@ITU@K=)^^Fk-?muL#nd`S3t>c0&cwt-nQ=HApc=s?C=dZA-L#nB848}M5-m)x~=EX~m7)ZSzc%r$>! zy~8@uI?meO{Lh1O?tW&1xqplkqc$t^qn5+^bO+4Xyj9lq;imm)b-Lp}o=0j(C$PeW zXgQY{V78bCjJ5>~c3fnrI}L6B{_6W{((DOz{j~HtIX5~$BS)p03;86@ID|}PE@Wu3 z-aCOhkc$wl=Kf-CnhTN{ZF>nQ@sMvMs8ay315A-+WK5`Z9j`w0urc*N4y^DkUu9tSL?$0 zcLZoipJu8nbYZdCKslA%z>BRHh@Ct$V`c%8b7Mu)I6RHu>SP7J4#HaSgW4g8-T;oY zIpTy--)IEqG^-_uI4g15|9050??fE6Q3Xir2?2mT%+D308QL1{nZ^nF<)zlrVZ;HC z0$!BS#ePV04AN18Z4f^Jd6yx)0k8@dYaPrLYk7^Vx)FUmqiKBnL9PW_D$L=1p0iS{ zsgYrg#!uQy66BukT4>PJ=eil>swA6Zr5OqM2>1jh3&wZNOc`T*aHD6h~)-!#dEI*Fj7PO^{xd5 zRoJq<^h~+Roluzoxi3Jj-X3pr&xwzV?**0xYVyjPo(iB*13+jt5Np*z;sF3+Ei!ub zwxMP@_qQ(vi2UVCsaEBnflXHJ#8J@ynXUO~R-v4`uS-=*qwdZV-tNwsUJSAmpI+E? z!19+nfmRjc5pr&$F9%76{OFcKC(D5%n}NqRfKLF<12~#dO19Sa2vkm0SMmfx?qm=b zQ^WFnLdQUgs-NbPN1Gu;V=2#xTB>xY$3>>-DdREGO zU6VK7<14q$f&RF=v)xaDG(?OmzGIeKZkx3j;@|ibVN17kZQDst1*#zVPfxwfBMuqgT2f)^L9D+gwG7+yS!s3 znqg`;&@Sg5_GzF?zHP$MiqsM1Fe^90RsakOJ{*?)=(vusF3-T`+OSH3ztEQo_(v;f z0rs5#!hYCmicp{Aqk9{S$Oj~=e7Z<%oG&3dcbk+MTU^MwrNG1xW^$WPA7+v#U{Vxe zGCm^GbnifW0ZOeL1J#h@%Nt%ef(U=#?RdDH>*ssd(is-q;x*VcHnQOWLxC^qjnlwP z;l>_B12hjnVjIZ-IM54gJ|S@k3zD(sEW}*^=b4{tbpz-7<%ohc3e>};xWV{x+>l;Z z62YDmy*y#q*0F~=Co_Yl^p$?o73z%jXBD}8mBXkPYw9Bb!4UFzh#Uc6L8|T1EGbeW z)QnAi+dvy2R^UfEIU;0D%&IAdUcF+h_Cj7Vvwuoj0}GUVzEJ0n-Pgj@O1jD7{|oDQ zYh!bFXG&KbxbMg=DVhVjKR<^Mv=h_?p6q(FedYuVDW8v|UTL2`k%H8p;nc47Db)t7vokRHlk31yEcj)0B2tIB_0k=#0K$1=zqHRC0e_fqa1iRIc2O^Q2b> zLtPwZrq6gQ^8koLT)F~6GNy7c5mlqd4pK(5o1tfA#dp9|ISwth7ZAeG)z8d?5rf~w z4t4(SWol*_iw)l1K#=ZUXvhms88Xi&S0+Qwi;_`{t{KFeE=C<{PS$gV1gdB%Kl2a*@(2M9Eh*nL4bxX(Mn)>ah$F55BQKeB2HlRucETAKo@vRry5KnwxGo~Bus--sl=yEW^N+iF z$Bey(vIr`3Z&gPrLic~)mQmg~pJ2K~v1Ajx<7KRcw-l^}|{6)HiWL!9Ec5FVC? zP8`C;q~9?xpWl_yuqklMvjLWq`+St~Z?J^X-rczgb~mNa_jh+b9+IQI_F!is(5CXQ zwJ8Vx)P{^dT&hDT4dtNhM%0)V?40Z;Mp_u?1OMj*`b_FGF%Nq15}30=zaLP+h`PIi z$W-JHcAn|t=WQ~sEwFpsUpde><6$x`;Fk<~+f`xx!J_~oWr6*kwP2SG07n7B24Dko zsbmB14MPRq@ZU4MJ4p_Z;6xK;cXzH220K6O9swod10!Jm8ti-`1oKyzdXg=*ke{Eb z20Pb=$W#>^Aw;IC!OjPuDwSCfafG6CRU;7b_&@eY_TPD@5eVJOT%6xOGZ;k1eV3$f zjzBs6vWt0-7~kE`_(Gd>n7|VkD*H#ZEOFow2P_@#kO5^kS$j>Q(J*JQvw|@#%n5gl ziueTOG%y#LD3Y#6I5R#E`I=^9m9a9{*4?=yXspT&$DRr1LM#vRwm>W|9NQGkgO~~O z9*3AI99tJOK`bBg)4hl!_4Gx0XV92yXY;ZU>FE|)tLm=-i zhz$wHW(J2qtN`++L98Ggi>cjE%==%l!2c#emwFkrco2M(gU}053B6W6^fLJpta)w_ z^fnOI0z5qcdL0Oz0KJF8)ERI#Ks*WNX}clZ3osYP>jKDch4_m`q-lfjCxE8`XLA;txRl^L$uiLwp;=*Y$%{CWOZTbTCrX zFc<6we3S9nGuV$JhwJ@^@rn);C|c@}b5{e^)}2l~oS_%z4LKPox8l-N=M@ix`jIHr zc^ja#KyIp2&OH>6hZQt9V=2+$!un}=X#E^KjDbTZ{4_In z>B!sx-7rdn-(H5MQ@1AAnFVPK>GP1@4e36RrXYQm`FQC_T+RfSrV=$SF@bwCbx{{Z z?qA!-nUL$l_9fM`VGWMlQ$35o{Rc6FmYEt(_zkOx02&mq>B*>9^!UOZFmHHr&W`&+ zotJ|Qy<*g`;X%caP8gqvC#()49r1vb5R#f-0Y=<&Z4AgI@&4pNhWfR-we4WsCgxu& zbXdu}y&?^-X3jwTH|Dn$X^BWuI1^eH|*UxQ&`kC(uc7EPf2s2MPV(8VW zcqlV+^}vRHUF$8sUP`ag=yhOWCD#mgLGBWd#sHNIz9Z+P;WN4+9t~;sNWxWkb3M6e zyLqY;7jG(`>bMMkzunpzIbI;ug&IR4REFqf5N`$8KqI;i3}lA9S^x*cB~G|%WJ1GS znwB!BBv<2#Ypc$}CPf=+x)vfSTcKSjz_b^iie*R)PjoxXC`%9tZE z>b1%~(VlC^%5PWBns(JgSli^xsI}Krm@D-)rmZT|BWg>e&_jZK)%=G$RKFdHpN6>nEkFH+QHg1KBmq@16~3h%szN{ghDU zr#7gK@mOP5z-Vi8jL&3FJaxBfCz3S&>9Jd-+={MP!C^L@jZMG??Z7+bm2ox+m)?B_ zJZL_UdNmJDhXGRKppDC!iCgs420620%bThYy~o;$GHTz> zJ~(as{o5ExZ4HlIBf@U#P#qnO=u(HwjLb4~44YB!P$onx)mO<3-tkc>k(3~rp}IpM z4I%}7l`vDOgTi3by(vT!jHHYBQ*9bfwF9o>f8G5rD!NIxQ)q@=9jeV%#{7{T)AbeF zx)^WmBwo@7H>~K!>D%) z&AGcn^=tZ!tVhe*4z+l8E{-uaPUaa;7BJpr`R3v?mJ9B4Q0s&@F~BLN?4_dQG{A;y zW>u68agTX5Elu&v<1g)0!uLkQC0VK+^@d_vFitfdh3gckcOEmE^*OjOEX- zFm1L(3cMn;(yI?pXXBZdUTsibMeWdaBQWmcG0NI$yvWl8A~X4WJ5*2BWwe{Cca5{S zEz#?9Lmes%JX>ct#-E2%t{zcm@z}FO*he~41gqd6G8b$?LN9+uD8f4(s;T8@ zhqeyyv{*Bv4*9N7nWG)5LzZvdFWHl2$?8XXl+oVF^Sx8V_x+zTwd#rbC8qTPX zDbtEEze_bJO>wqq`~kSR;%M0e_bKhnZO05U65GWrIQD@m;Fjof;39#(idoX5;@v!} zyG5)%Y{fH~-##3gSP$F0(iW~cW!dy)cP#HIRmYdA6P0Q$#kVnwTHjU4;1u>$uTyc@ z-C7RL(L5Y9?2piaxp@KgGQE(Q0K*N+NbAQ;K0JJzlP@=ULQ?SbPHgv->4W z!dF}iEy=krA8i-?|V(%x|ote1}Tg|qX3 z6*Gz!^!;4so0I);8guKUNu_8}eBWY+Ln#g-gEk%m*#)u$tntuOyQom7-T_Er}qei{Gs-x`7JPY z{SrEksTBypT`5GQse!dRVVlED_`-zim{niArkbEOy4C79H!}8j38ZxirFC_vq%OF7 zXvmN?mS#(0Xxc?Q??nRMO#W9LD%egfkh<~ENow3fRJ)kRUM#{s4_BAd+PW)3-m7t352+p zFLbY{(6=>eVi#OJdvWS`V(rJiy(5v7jaux)z0AZf@={3_wn=qxe{nnww<%{r)5G8f zF_{?kWuf6Ncc`q^udS`k>kl?r6Zgl^w99!ZmW!nLu0ut;s;nq4&#fvP6>Z?(3r*49 zp=z?`>{LFj+G>Wo;4y-X`UfH3AD}~fUTdzhN|^yWmD^)z+7&$C6(YVr-BNDF%xtS_ z2jcR3CT`mMdF1;=$k#elO0a1zELvuGkv>A&BNXyFbdaO!E0QyQzlH;$P!IP9d4dm$ z2>#NcQtaPl?F$#K3JhgEDrr~p*egZYH#$_=J7<7ihMnkH%*JRl>feP1`2ChrpU|Y- zUR>SXd9-IMTkGU$Iz=?OlO;|S%*{~y*hV;-SP!rT;8Gkq-*%1h04G@CcU?=|-(=n4 zkr=XfXw4W_|2H8g!vCgF{XdJT%?V8XpC77lVOsfDo~c$6#w69Q=4DtdlA)tR^->_g zpkk_J==_DNJ=kk_>@_0nzd^lT1NT_fh-#Q<4bJECFaEJyMYMMZw%K%2^ zl=lv0`8a&B+^S?KL%6f!XLJwoj2;p(>grH!DNicYls{r6her`kexbU7TM4FkccVe0 zS8l&V5W?gbb+=HI?ha)!vRdPJjzaiM_;v};w2^1pC}8?bK5jGiTL#-!gn zQb7FUV*HM6)_E0|4&C6VZ)`w@do*S9<$$!$+GOqhy!N^6*Itb_P@Kf76(VX1JRkRF zpH|@h*v-DJz$O`~gA?pk1s;-FL2gFD+Z6IB1>Q0=D^L>LDU!LRaFZ$~Uk;vp833!c4@~tj?wB4B6G~U+JkXW@!B%KOp zQYTexl?vD6qwE_hJQNqQ->YyL+*oWKpS=j>b8akH`aHpl@`OMXHa`*1GCUjl%7gWB z)l)1lf;eWlv$X|wZ5{&ARd9FfD8N72eTmqq)CD>}4SeMxxnT|2KrfsbyY)?`wMW^Z z8hi%C32LxGm0m?&(;!p+rL@YJNzym+a%>dHp+Fm~a!$b(CF2QrJG(9!_XQf;lkq+H zMHXu@t<0CZ=E21%rK@Rtj1sF(5g8-}--T1yO({4nUiWxJc=nAHJQeF$+#B~-$+?qo z4B}{k&Yz1>(Qe}9*(8vM&F_QraVsX< z*$aJewbFEjkbTnkS_VaEhua0KP8CU*il^XowjmWHR0p5;5L4H(FQ(%0vMuoPsh0gM z75AqLU<3nq(&NhR&VRz9fTRnWQO-tqT@nw^Cc=`lfi#g%r(->SiJg>=7vTl$d+FGK zPqJ6iaZ%=Va8qe@ngiuZU5&ae2C8Z!<*s#%zaM7pF&nVzOp%%&rTU;u7%TVS%u; zNLU(YnYO`8AYMO3SU(yT^NS%*=7LMX|8TU(xi$VDJS2BH`yh??t5WOeN|cQZ8?AZH zh(UECgX-~|tOvkGZE&6022+H~5UzkUD30tpSf#4jSM_+cQfZ!nPR9>QeIqs8e@Ci4 zTO?sNnA7M)ajm^Vo$qx|>{(Q58Qz=>5RFch+57>3UcZWVz6>3OB=E&Gf#yaLT_fI- zWmCgQ7m?>yiw*zo2sH9nG7ZDwJ*!Oln!^#)Jb&F{o%bReq|M5&cycnI5r$VIngO z2gTxj$nbLAkKwDr0>w&1#7Y2>Y&3*AKZGqus%QApKF7=coJjUjfd03;rs5|=pdz{= zCgW47h;At$jy2-80_f9>Ks_da_645q3nIE>0R4IWR@5{@Giv(`#2+TGfw3Z`%(z2I zF6ELdxz(%Wgd{)2zsQq(QAE-LB)3~%^-yqo=4I#>V-m3H3X!N4fH+@427A33wWH%P zMD5Ew-Iqmlt$@DST3Hw>+?rNjA$vB4&Z=!9x;8*;vfk;LVI@>wjt=;hBAu$BGU@A{ zJtnx(uNOQPWz{tzVl_aF+%&^Q@My-cqemOO?goQ3(Z<~TE z@mltYDYzl$@ot0jQrwkZ$duqVIA_Nx&P(;Msh{7MUX>SPCsv*W%4iqcCYUBFJAhCf%z_sX1&^ag8Rlocaayq~wk zet{(r^@wF)TRUvkNnBW0X_0cj1nEURsUO)1x8wfmmmw2pBO`W6x#!qbx8sd@8-fEi z)^FUs;q61rp=T{c=T4_@J%sX%t}PbDSL-YV=alI?56xTGWRdW10?0j8Ry{*x+ZlKi z9>Cr+1DD}p?7kWJAIgp1xf@^Guqkclq17?52fV?H{f0p7?UvpbcLD=?(Y$rMRPVE6 zXW>csBlcgja4vp`eQy@NM-|oS!N9%i5YYcJ2j3^vRI)EJ`nZC<&zMatC!y1eNH&u%Nq&RB*9#n4&xd=cIGfY|tK z6I!c5$n1OwM-03`WZ;E>G^<|I;svq4gVzEAYIYGdJ0K24k65#8joIt1djwK8^HMg8 zq+AT>V{%V#j5?z*3`WYcvl4{;}HWa7a70-{N?&} z@Zw>GbCzSph93U!@%-Nt@m~SmoqmgTV%2Ep(^i4Btoi|wS`XkGDVp1O*=!oD3Bclp z$Q>h}@&Ql&0|EJeu!C0OzFDboK-L?|o`0;Mf6`K(tkBx->@xWxXln=2ZpTxy8%9dv=>4r zfC7{_2w?#74glN$$cMZ(2%A7S6?`KKB3Mz(fp{qZ*)JFgdHE36H2XSF_~bR%P;Ylxla3Z?y(Su$Hy>uH1)V z`mq+E0-jG$)sm_V=S~=U9pg*kJIwXe!#7npKs7&!xr$)PBq}{8+^BaqPtT>?@DM0yrsnKFpIah?Qd18$@bt00FTs zfev*(6=;GZxEP}LQ=a^%BJxk-(!6#sGLf_tBqTZ!`tu<^5soXTTBcS!(umLor%dTj`Z>lUH#IOA>DK4RN;k_#U?DlhB zhtEYi?7-V(C*fT13-;3;c($smZpV|$tfL;;`B0obrai0PC1SM;T*2aoM^(-XrZEu4 z5Vc?OEWQ-6cph&^+XK58yPz2&6pHUW9}1bl9cE-8bdvr2c|27map(;G)HKJ&R6Wm@ z4ZI*ye>bpPZ^?9?mpFAtyg$IoiSIi!f zMtkt)uoX_REqm}hRa0iUH5Yw*D6!br)0t||@tn?yIK7H@#S`U-4NtS~SMelOeDIcs zB*~S;m}yw`UJ=K=piroD6x=Y>xU%h?JR3H!Po(whfMl@4Mk;K?SSQUAQagZ^vD2Q( z))G6#IoYBE_#%A@E}&kwx{?)Ll5E?u#|Nl1grEN zZE)S{)IWh@Q%xI)eN6PKBrn;D}%hpxNLNoUIJc&2Y9Jr3)JRC0?LQB7xq) zb{U~fv)kXn4O-HtQEA;FD!Ci3={_3gZI%l>Gq)rZ{4{; zu0PxuGn@7b&+v+f;rmeW7)ekaJli}Ay=V-bRUZ?PKSmZ?mt>dJ2`05melTL47Lj#6 z#7kwzKz*8h?nC@2j51W@mTP~TfNraZZYy>qlgfw3xG+e<2|jF|wQAWnTk#6j;;d)N zs;tS*x?%MzVg$5Tc|ER*^!NxLOCZHlL5~|+_Ro*-VAT$5wY8usL%Qjpz!6yWCnElz z;G=4ycs$a`34s#!<|iXRZqC-F2)v+GX6PKCK>yM=O@t%$oWIL`T*SLJK-$;Qq%hyoQM61 zG}{3N0mN*Y;^CQl1kCSc2YrE$Q%ifZzkh-I7zycdw~*%1CGhmxgx#Ue%<$uDGSv}6 z#4*0anf;>Dh2K|)#jl0^@m4F5G>I*9!EUgco$11#sjT*A%ip#xoOlT4T`{4w|KWxD zk3c9k|0}SF+?`Pw8CmkKwXg!X`_YB3_UwOK}is*g~ z==Jb37)<%Bj9z)@N=yt^-6o>j28dL_%dqn{+Ea*%7ey4lDDsYP0m*RHVg*WVx~p-* zW_4La?J^)@{g0_L?IkFod{$kIt@aOI%0EO>eh1q)%L|tc{R)lryl$9Hup^y8yNGN% zkWF;sVX$F5QriB}oF1|_d9pV}WPgChZSuQJbv{ovw1J39KZ;cP3AfYaaks>9`ViMF zF{8YZE@Vn{Nu0=Zhiivrs7sRF!{#rZ&0ium*FdT?yQ1pJ)F;dJmYc2^I;*}e68}23 zQ;SmAXRqTYmHY}>hTtHE;PVq_a72U=x+Nn23(myZY~?SYbT)hMFZi%Be0Zam*XZlQ zvy+IaZeV51H@%(DhsraN%h5sxu80ZPvKx4yide$i+zN;DuugE?fMc0KGWOvcco`eG zfwL9(2xVJu#nGkRZ#XkC>hN{r(eVe9*za!OEVkx%T)>w84vlAHBK$RpUx>+>P(2?YJI%%d_v9&8;VB2}BB- zu3oT;4wow!zSNmdFJHJ2K9YC>yVQ%1uyhv|tWAQvBtfAhC0+PBO9upKtU4exWJ!P^ zbPEu)A)Zc1M5h~He}<-bZfqcgGnG;Cmn|5@9`DAtJ>#VIvNRRJfeB?YtWSt(FhCTU zEupBojOq8!y=TEFdc{5Ubm`E+gNF_uLzfmnSXeTKWhv^hn04Wplv2hXw$bp%(o*UU zc7&XIC5EIWuLyvLM9Al|oT^}H1$8WT`$!891PGc`gr*>93DgEPdEh`EDv(DErR>`Y zls1JtTDJqV0<;5o0V4GmPxU;xC3U3$fEhVT21o~JBMBdbpw>Ay{Cx8mvB}$g$ z?*iBZZ~)*az=>?6nJ^HZU_$tx9He;&KnHNa052YvKRC}$PnkLnF1$gUovNbvjXfry zjM`c*QcgvbO9UH`nO=B`5BD{R4dO&-i3Cj}Kx5S!N^q!fSwj$#1qfQW!bj-Di|8a1 zv=oZYC)YxE!3E+y09#TJtpPX)@m7G#033jnhys7CJ%w^atg591$K7oyRB;3OF-19= zQaerm(sMN2A0Y9^GbL0?B>q9D^qtg5eE;FPsj5i)>LVK(0w5&8KgT_2>45mcWs4Ai ziEv#ucNvKfB5`Up(!8^ZhptE%Nl%D>m;iB-7DA*N zdMC4qeW;$_eG{VO|5uUdH3&2T$F#x~A zw;upDJPZ5*-hsT=BjN23S|ag$5{Gk6@;z%i+_eRe0+2L;-<$ZnXj%{^#E^Eg`!Xqm zYABqkT?AOSTQDDPfSpBR=tO~`ZvsNnOGtAYIoXQ*1z&-@SCOVSKrTQ$94PhP$A2$@ z_yc>Pe*>rhAnE^xZpix@;3z=NxC#^x?U6J&sxFbWW>H%s4ZokL;b&1cmdm2z*<6}> zE8-jdq0!J(LhK?x+eU+}_r8ZT*8o0zpASj=e*_Q{OFM8R)KvDf0 zDcier9<>cKf!yf8)?2`j(cz^7YGI9)550W}Km|Z8z#ITOzykpD!Nc2C?BaZCnLw(1 zkyQEA7}>M1Z}clWw1Dc%77nIrv6+2zFg1{>@v?&pD1N#Y9y$B+_0(4&2irCn)Ovm} zrJ|H!^sN}4y%0*V@ivt&l`1M#K#WmH-Gvvk4TaRqFgE$2IpN?}NEzW^78g_3N7)jh&w@WNz>$_}T@il2jV zh9wTPlch_jY}oA^S0Xx6D-jv3gfJULrQ+%A$WhcVJd0gEipsz>?50uFgorL9sm$8w zl)JkNtQB&2&eXZ7zA6|69og>7qv%wTO>Vk0bK%|e2vZ*=GIw!UvZUGdd*+Es* z1gO3>Rn$OS$G%)e^*oH$=J2}ah}goj50GVwX~wi@_p-V5xa8mNrDEJISSv&U3nI;G( z-f5PvT{odD*`0|lR>{gV2gd1nMI8W-XP??y99yG_9?zNTE=m2I6xFhmUwWn82iH#To_XVcTk{8nvPeX=s3K z01RE;Xm;roYH6%qwYj`qb43NGQ1jW+X_ONjuX!4{;m47QKisCEl%}W~mKVd#PPjfe z4B(X~k>*7JkPDV-aQD0%=IclW412Sh=~PO3(mU{bQ!q+h2G|X76rdeoOA3b8Pc!7>BVF>((F5jG`>$^We;KVdH(w=-$MEl(){x>==B{eZ6N(v zJNy{h_egUJwmwfm`qU4wD*#aWBR^*egu7r9udzv_@$J;vclXSoG?@IjN>3Ad7yN%T zh98T15q=v5<_@z2a|hu!IQAIKlN~G|`(GGCc#I(e46siPn_Wj8iB$-zt`jx1I?_t& zV~#slAROCPPd?6%weNXRHz*XaVB6*stc8G8Z0FZ+ta_n{$3hB5N;~vA(t_o0C;T7C C)2RRe diff --git a/README.md b/README.md index 163f5ec..2f6b9ad 100644 --- a/README.md +++ b/README.md @@ -29,15 +29,15 @@ This project is the most complex one among all my projects. ## About the License It's CC-NC. So DIY for yourself and for your friend for free, don't make money from it. And plagiarism that doesn’t even dare to mention the original author is not acceptable. Plase note that pooling orders and group buying for raw materials is acceptable. Selling off any leftover components without profit is also acceptable. -If you're interested in buying from me or some kind of commercial use, please contact me (Discord, QQ group, Wechat group or leave your contact in issue section). - -## My Discord Invitation -https://discord.gg/M8f2PPQFEA +If you're interested in buying from me or some kind of commercial use, please contact me (Discord, QQ group, Goofish). +* My Discord Invitation: https://discord.gg/M8f2PPQFEA +* QQ Group: 854535476 +* Goofish: whowe12345 ## My Other Projects You can check out my other cool projects. - + * Popn Pico: https://github.com/whowechina/popn_pico * IIDX Pico: https://github.com/whowechina/iidx_pico @@ -51,7 +51,8 @@ You can check out my other cool projects. * Musec Pico: https://github.com/whowechina/musec_pico * Ju Pico: https://github.com/whowechina/ju_pico * Bishi Pico: https://github.com/whowechina/bishi_pico -* Bishi Pico: https://github.com/whowechina/nos_pico +* Nos Pico: https://github.com/whowechina/nos_pico +* Voltex Pico: https://github.com/whowechina/voltex_pico ## **Disclaimer** ## I made this project in my personal time without any sponsorship. I will continue to improve the project. I have done my best to ensure that everything is accurate and functional, there's always a chance that mistakes may occur. I cannot be held responsible for any loss of your time or money that may result from using this open source project. Thank you for your understanding. @@ -139,7 +140,6 @@ Before we start, I want to encourage you with some hobbyist builds. * Install the cover, each needs 2x M2*5mm screws. - ### Portable Display * Resolution: 1920*1080, size: 15.6inch, refresh rate: 120Hz (recommended) or 60Hz, built-in speakers. * They're easy to find. Choose cheap or even tiny-flawed ones, they're much cheaper and work just fine. @@ -154,6 +154,12 @@ Before we start, I want to encourage you with some hobbyist builds. ### Housing * It's your job to design it and make it look nice. +### RGB Light + * All RGB lights are daisy-chained in just one WS2812B chain following this order: + * [8x Button Nodes] -> [3x Cabinet Nodes] -> [1x Banner Node] -> [1x AIME Node] + * You can set chained RGB LED numbers for each node (Button, Cabinet or Banner) using `rgb` command. `0` means ignore the nodes. Note that AIME node supports up to 16 LEDs, and you can't change it. + * If two or more RGB LEDs are connected parallelly (sharing the same input), they're considered as one node. + ### Firmware * UF2 file is in `Production\Firmware` folder. * For the new build, hold the BOOTSEL button while connect the USB to a PC, there will be a disk named "RPI-RP2" showed up. Drag the UF2 firmware binary file into it. That's it. diff --git a/README_CN.md b/README_CN.md index f699e08..a76e837 100644 --- a/README_CN.md +++ b/README_CN.md @@ -31,15 +31,15 @@ ## 关于许可证 它是 CC-NC 授权。所以你只能给自己和或者免费给你的朋友 DIY,不能利用这个项目赚钱,比如收费的代做,出售整机等,连源作者都不敢提的抄袭或者稍微改改换个名字来打擦边球就更不可以了。注意团购和拼单订购原始元器件是合理的,非盈利的方式卖掉剩余的元器件也是可以接受的。 -如果希望找我购买成品或者寻求商用授权,请联系我(Discord,QQ 群,闲鱼,微信群或者在 issue 区留下你的联系方式)。 - -## 我的 Discord 服务器邀请 -https://discord.gg/M8f2PPQFEA +如果希望找我购买成品或者获取商用授权,请联系我(Discord,QQ 群,闲鱼)。 +* Discord 服务器邀请:https://discord.gg/M8f2PPQFEA +* QQ 群:854535476 +* 闲鱼:whowe12345 ## 其他项目 你也可以查看我其他的酷炫项目。 - + * Popn Pico: https://github.com/whowechina/popn_pico * IIDX Pico: https://github.com/whowechina/iidx_pico @@ -53,7 +53,8 @@ https://discord.gg/M8f2PPQFEA * Musec Pico: https://github.com/whowechina/musec_pico * Ju Pico: https://github.com/whowechina/ju_pico * Bishi Pico: https://github.com/whowechina/bishi_pico -* Bishi Pico: https://github.com/whowechina/nos_pico +* Nos Pico: https://github.com/whowechina/nos_pico +* Voltex Pico: https://github.com/whowechina/voltex_pico ## **声明** ## 我在个人业余时间内制作了这个项目(没有任何赞助),并且会持续改进这个项目。我已尽我所能确保所有内容的准确性和功能性,但总有可能出现错误。如果你因使用这个开源项目而造成时间或金钱的损失,我不能负责。感谢你的理解。 @@ -156,6 +157,12 @@ https://discord.gg/M8f2PPQFEA ### 外壳 * 设计并使其看起来漂亮是你的工作。 +### RGB 灯光 + * 所有 WS2812 LED 都是串接在一条 RGB 线路上的,顺序如下: + * [8x 按钮节点] --> [3x 机框节点] --> [1x 顶灯节点] --> [1x AIME 节点] + * 你可以使用 `rgb` 命令为每个节点(按钮、机框或顶灯)设置串联的 RGB LED 数量。设置 `0` 表示忽略该类节点。请注意,AIME 节点支持最多 16 个 LED,且无法更改。 + * 如果多个 RGB LED 并联(共享相同输入),则视为一个节点。 + ### 固件 * UF2 文件在 `Production\Firmware` 文件夹中。 * 对于新的构建,连接 USB 到 PC 时按住 BOOTSEL 按钮,会出现一个名为 "RPI-RP2" 的磁盘。将 UF2 固件二进制文件拖入其中。就这样。 diff --git a/firmware/src/commands.c b/firmware/src/commands.c index 1d90038..dec9f95 100644 --- a/firmware/src/commands.c +++ b/firmware/src/commands.c @@ -25,10 +25,11 @@ static void disp_rgb() { printf("[RGB]\n"); - printf(" Number per button: %d, number per cab: %d\n", - mai_cfg->rgb.per_button, mai_cfg->rgb.per_cab); - printf(" Key on: %06lx, off: %06lx\n Level: %d\n", - mai_cfg->color.key_on, mai_cfg->color.key_off, mai_cfg->color.level); + printf(" LED Number for Button: %d\n" + " LED Number for Cabinet Light: %d\n" + " LED Number for Banner: %d\n", + mai_cfg->rgb.per_button, mai_cfg->rgb.per_cab, mai_cfg->rgb.per_banner); + printf(" Level: %d\n", mai_cfg->color.level); } static void print_sense_zone(const char *title, const int8_t *zones, int num) @@ -157,21 +158,28 @@ void handle_display(int argc, char *argv[]) static void handle_rgb(int argc, char *argv[]) { - const char *usage = "Usage: rgb <1..16> <0..128>\n"; - if (argc != 2) { + const char *usage = "Usage: rgb \n" + " per_button: [0..15] LED number per button\n" + " per_cab: [0..15] LED number for each cabinet light\n" + " per_banner: [0..15] LED number for the banner\n"; + if (argc != 3) { printf(usage); return; } int per_button = cli_extract_non_neg_int(argv[0], 0); - int per_cab = cli_extract_non_neg_int(argv[1], 0); - if ((per_button < 1) || (per_button > 16) || (per_cab > 128)) { + int per_cab = cli_extract_non_neg_int(argv[1], 0); + int per_banner = cli_extract_non_neg_int(argv[2], 0); + + if ((per_button < 0) || (per_cab < 0) || (per_banner < 0) || + (per_button > 15) || (per_cab > 15) || (per_banner > 15)) { printf(usage); return; } mai_cfg->rgb.per_button = per_button; mai_cfg->rgb.per_cab = per_cab; + mai_cfg->rgb.per_banner = per_banner; config_changed(); disp_rgb(); diff --git a/firmware/src/config.c b/firmware/src/config.c index e76c564..df62803 100644 --- a/firmware/src/config.c +++ b/firmware/src/config.c @@ -16,8 +16,6 @@ mai_cfg_t *mai_cfg; static mai_cfg_t default_cfg = { .color = { - .key_on = 0xc0c0c0, - .key_off = 0x080808, .level = 127, }, .sense = { diff --git a/firmware/src/config.h b/firmware/src/config.h index ee3d2c7..1606a0c 100644 --- a/firmware/src/config.h +++ b/firmware/src/config.h @@ -13,8 +13,7 @@ typedef struct __attribute__((packed)) { struct { - uint32_t key_on; - uint32_t key_off; + uint32_t not_used[2]; uint8_t level; } color; struct { @@ -30,7 +29,8 @@ typedef struct __attribute__((packed)) { } hid; struct { uint8_t per_button; - uint8_t per_cab; + uint8_t per_cab : 4; + uint8_t per_banner : 4; } rgb; struct { uint8_t buttons[12]; diff --git a/firmware/src/hid.c b/firmware/src/hid.c index a6e0f79..79b4eb9 100644 --- a/firmware/src/hid.c +++ b/firmware/src/hid.c @@ -6,6 +6,7 @@ #include "tusb.h" #include "usb_descriptors.h" #include "button.h" +#include "rgb.h" #include "config.h" #include "hid.h" @@ -106,14 +107,27 @@ void hid_update() typedef struct __attribute__((packed)) { uint8_t report_id; uint8_t cmd; - uint8_t payload[62]; + union { + struct { + uint8_t led; + uint8_t gap; + uint8_t r1; + uint8_t r2; + uint8_t g1; + uint8_t g2; + uint8_t b1; + uint8_t b2; + }; + uint32_t outbits; + uint8_t payload[62]; + }; } hid_output_t; -void hid_proc(const uint8_t *data, uint8_t len) +void hid_proc(const uint8_t *buffer, uint8_t len) { - hid_output_t *output = (hid_output_t *)data; - if (output->report_id == REPORT_ID_OUTPUT) { - switch (output->cmd) { + hid_output_t *data = (hid_output_t *)buffer; + if (data->report_id == REPORT_ID_OUTPUT) { + switch (data->cmd) { case 0x01: // Set Timeout case 0x02: // Set Sampling Count hid_io4.system_status = 0x30; @@ -123,11 +137,23 @@ void hid_proc(const uint8_t *data, uint8_t len) hid_io4.chutes[1] = 0; hid_io4.system_status = 0x00; break; - case 0x04: // Set General Output - case 0x41: // I don't know what this is + case 0x04: // General Output + // Bit 2: QR Reader P2, + // Bit 5: QR Reader P1, + // Bit 6: Coin Blocker, + // Bit 14: Camera RECORD, + // Bit 15: Camera Ring + break; + case 0x41: // RGB Light + if (data->led != 0xfc) { + break; + } + uint32_t p1_banner = rgb32(data->r1, data->g1, data->b1, false); + rgb_set_banner(p1_banner); + // Note: p2_banner uses data->r2, data->g2, data->b2 break; default: - printf("USB unknown cmd: %d\n", output->cmd); + printf("USB unknown cmd: %d\n", data->cmd); break; } } diff --git a/firmware/src/hid.h b/firmware/src/hid.h index ea175f0..2eada91 100644 --- a/firmware/src/hid.h +++ b/firmware/src/hid.h @@ -7,6 +7,6 @@ #define HID_H_ void hid_update(); -void hid_proc(const uint8_t *data, uint8_t len); +void hid_proc(const uint8_t *buffer, uint8_t len); #endif diff --git a/firmware/src/rgb.c b/firmware/src/rgb.c index 24aa88d..8f8a4d9 100644 --- a/firmware/src/rgb.c +++ b/firmware/src/rgb.c @@ -20,7 +20,7 @@ #include "board_defs.h" #include "config.h" -#define LED_NUM 12 // 8 buttons, 3 cab, 1 aime +#define LED_NUM 13 // 8 buttons, 3 cab, 1 banner, 1 aime uint32_t rgb_buf[LED_NUM]; static struct { uint32_t color; // current color @@ -118,7 +118,14 @@ static void drive_led() last = now; for (int i = 0; i < LED_NUM; i++) { - int num = i < 8 ? mai_cfg->rgb.per_button : (i < 11 ? mai_cfg->rgb.per_cab : 16); + int num = 16; // aime light + if (i < 8) { + num = mai_cfg->rgb.per_button; + } else if (i < 11) { + num = mai_cfg->rgb.per_cab; + } else if (i < 12) { + num = mai_cfg->rgb.per_banner; + } for (int j = 0; j < num; j++) { pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u); } @@ -201,11 +208,16 @@ void rgb_set_cab(unsigned index, uint32_t color) set_color(8 + index, color, 0); } -void rgb_set_aime(uint32_t color) +void rgb_set_banner(uint32_t color) { set_color(11, color, 0); } +void rgb_set_aime(uint32_t color) +{ + set_color(12, color, 0); +} + void rgb_init() { uint pio0_offset = pio_add_program(pio0, &ws2812_program); diff --git a/firmware/src/rgb.h b/firmware/src/rgb.h index 9dc5c40..ea96f20 100644 --- a/firmware/src/rgb.h +++ b/firmware/src/rgb.h @@ -21,6 +21,7 @@ uint32_t rgb32_from_hsv(uint8_t h, uint8_t s, uint8_t v); void rgb_set_button(unsigned index, uint32_t color, uint8_t speed); void rgb_set_cab(unsigned index, uint32_t color); +void rgb_set_banner(uint32_t color); void rgb_set_aime(uint32_t color); #endif