From 14a7509d2fde9a083085bfde29ab86201725dd79 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Sat, 21 Jan 2023 11:31:03 +0100 Subject: [PATCH 1/5] Improved power consumption --- ap_fw/main.c | 15 +++-- ap_fw/soc/zbs243/uart.c | 2 - tag_fw/fw154.bin | Bin 0 -> 32040 bytes tag_fw/fw29.bin | Bin 0 -> 31818 bytes tag_fw/fw42.bin | Bin 0 -> 31871 bytes tag_fw/syncedproto.c | 128 +++++++++++++++++++++++----------------- 6 files changed, 85 insertions(+), 60 deletions(-) create mode 100644 tag_fw/fw154.bin create mode 100644 tag_fw/fw29.bin create mode 100644 tag_fw/fw42.bin diff --git a/ap_fw/main.c b/ap_fw/main.c index 18762aa6..ee24df17 100644 --- a/ap_fw/main.c +++ b/ap_fw/main.c @@ -168,7 +168,7 @@ extern bool __idata serialBypassActive; // if the serial bypass is uint32_t __xdata nextBlockAttempt = 0; // reference time for when the AP can request a new block from the ESP32 uint8_t seq = 0; // holds current sequence number for transmission uint8_t __xdata blockbuffer[BLOCK_XFER_BUFFER_SIZE]; // block transfer buffer - +uint8_t lastAckMac[8] = {0}; void sendXferCompleteAck(uint8_t *dst); // tools @@ -437,7 +437,7 @@ void processAvailDataReq(uint8_t *buffer) { return; // prepare tx buffer to send a response - memset(radiotxbuffer, 0, sizeof(struct MacFrameNormal)+sizeof(struct AvailDataInfo)+2);//120); + memset(radiotxbuffer, 0, sizeof(struct MacFrameNormal) + sizeof(struct AvailDataInfo) + 2); // 120); struct MacFrameNormal *txHeader = (struct MacFrameNormal *)(radiotxbuffer + 1); struct AvailDataInfo *availDataInfo = (struct AvailDataInfo *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2); radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct AvailDataInfo) + RAW_PKT_PADDING; @@ -468,14 +468,19 @@ void processAvailDataReq(uint8_t *buffer) { txHeader->seq = seq++; addCRC(availDataInfo, sizeof(struct AvailDataInfo)); radioTx(radiotxbuffer); + memset(lastAckMac, 0, 8); // reset lastAckMac, so we can record if we've received exactly one ack packet espNotifyAvailDataReq(availDataReq, rxHeader->src); } + void processXferComplete(uint8_t *buffer) { struct MacFrameNormal *rxHeader = (struct MacFrameNormal *)buffer; sendXferCompleteAck(rxHeader->src); - espNotifyXferComplete(rxHeader->src); - int8_t slot = findSlotForMac(rxHeader->src); - if (slot != -1) pendingDataArr[slot].attemptsLeft = 0; + if (memcmp(lastAckMac, rxHeader->src, 8) != 0) { + xMemCopyShort((void *__xdata)lastAckMac, (void *__xdata)rxHeader->src, 8); + espNotifyXferComplete(rxHeader->src); + int8_t slot = findSlotForMac(rxHeader->src); + if (slot != -1) pendingDataArr[slot].attemptsLeft = 0; + } } // send block data to the tag diff --git a/ap_fw/soc/zbs243/uart.c b/ap_fw/soc/zbs243/uart.c index 45d33b5a..9491e66b 100644 --- a/ap_fw/soc/zbs243/uart.c +++ b/ap_fw/soc/zbs243/uart.c @@ -21,7 +21,6 @@ void uartInit(void) { } extern uint8_t __xdata blockbuffer[]; -extern bool __xdata blockRequestInProgress; volatile uint8_t txtail = 0; volatile uint8_t txhead = 0; @@ -79,7 +78,6 @@ void UART_IRQ1(void) __interrupt(0) { *blockp++ = UARTBUF; if (blockp == (blockbuffer+4100)) { serialBypassActive = false; - blockRequestInProgress = false; } } else { rxbuf[rxhead] = UARTBUF; diff --git a/tag_fw/fw154.bin b/tag_fw/fw154.bin new file mode 100644 index 0000000000000000000000000000000000000000..5926bf48977c5baf4091a596498edae7d15835e0 GIT binary patch literal 32040 zcmeHv33wD$_HT7{r_%|6gbtBrZ$eOZS{6l#5DmD3MiOTn9mhsU5JiYktsoGZ&Va~T zgD|3ON}L%NkpDb&TtH}P_^Y5}L^CR=sU+16S-V_1Wb5?%om*9%PQW+s{lD+M_r3QK z=vwY}&OP_sv)y}(=YBViQ~&r$cJ`pZ>v#UTVJw}$KH<{&w=e0G&fn;5x{Ej5z#u@y+OULr)N>JFzUzl(n`HU73v)$wYx&cg*V79&wISV=kmOct4{ZW#njE7 zO0Xo_QjlVi=vb&MULhU5v5+Z-`r1>(r}-aDoR)2p3_Z~`r;lj^2YCl9bQRz z&?{MQ+e_ni3_x@y6QdZac*b52dX65Oe3}yje-`YDnt`M^-W_+;I@p~IX2wKNOeKn3 zqPVTvt6)*zc?N~?%04b#lx#r8z`HXc&NDl$!i9^o;-s+C_(8-fBq7fk9TzkPTGuP0znj$NIA;}xUz9Bks8i~kDLSp~ zDy?r5{fQDL%z$W~pyps)MvMLgJ&%SALy6&{f9TBAO8}7CBqvB_keW)Q<`Ooc&JSy< z9^YIuzNL12D{{^&)g?+ziBgN1=}TpB-Tx^{{e}J*r~Xn~%W^L9ea_IWCc_l+)K2h+ zFu|PJha$XtN*yK1EUE2q67NntoXorXA5IzS9$5djk#`Re?(KvUcp>myt>TUo13PM2 z>lwV#b~uB}+WuvF4_|OkmXSAQ2tt=i`drmzq}Ssu_9|XU6OzHEwQ-iUCa_z5Q^0a_ zp^=Zji^MY$nw!@0mgO9t!cDU8g+{@!)6&S^aH(zfo%?gbIm@4EcINazN_~g8g@mdn zNd>72K@(6cff5BJTAS8-ENhD`YmsmfS{YV8!S5-}=qgDIK_zz<$!FfO)+o0}SVJ{e zHr86MX>7nRh##^8qr;X!W?K{y-*b(S{LeM==EabtNXB&P+IIRySZKsS@i6s%t+A#s zt~M{(X~vxAEk7asM~zIDpeBEbT2bO=kTkxi54wzD)mgK?nr$hy+qkSjF_7*k?1`b3 zS|w)!6Tiz-+&4vQ#G{5Kh#EtpHR5XuuL$O?qFT>M5!Bj3GIqJeorI~Qshj20^Kg>| zs*UBUZ?P|HO&h5iP-Ft?B8s3#@~@7|tzM&;!dYjW(ll=x>D_Qs-Ia~?F{87JcnE40 zjApL)?F={UZD$F8jkEmK%*Q{kj!)Yq#>eIPJ5xFz#wZ<~*1B%27#FJ>E5>zL6%nyw zobc8tMZN*DVG4dzA=Ek*5U%k`GA0aeLWNf|LxNVrFyIG##ENSms&67`ne@C^aV=hE z>MuD=haEA*9fd5Ud|?=CORSh?y}OkJ;#LC$E7hCDif%S-iSr4TUGi=%`Raze&P&pwmOeUFR{XbWP8l$@1W5) zuh_AYbDM}C#EJ~9@<5f8!j3)i-W+q$j#Zo+9aB@qigb;#SurFcPVUf4b;H`~uM0O^ z-yWQd-VbN3!eV5j7+b4uSX2FV%?;PL1ShwGo5+$#KFS$-1jj}i#<#22@!O?v+j-yK zrs_S-HM?7CceSqbJX5@0aiMBmGSfClGZ?l_7E!yr9x0p z5aJZ~Bi?vc$jiGt*TQ6a)$;i1WjngV(oE-H12d<*|0jXj;Xo zC4@My^o(acam_29SBqa$h?j&Kr8GX6F|b*lAfXLGYN8^_7CxB>-CXlFGrN+0>54s@ z%SSD|d(tK6wNKvLj#eXRHS9GD13@2%iY;W=-IYYs@enJLKMAH^ycIk3~r(JR7gIt@(RU$b1=h2=2S(fI9Y0I@a;yjRocKx zG5OdcSbRG}c(2o{QN7z0r0yXIqp9b00O*kLdWG}{yu&&dE72Odvz+tQ?Wy0H6Nglk zQnOO^tlB*}kBJqdr50x&s@p79+SV)8fAlLTc?CTwn9RsPI#O&>#y~<M1FEL z*`t)Kdf`qZpKNgOY1A~ip!cStvS3iPA|5r$rwTJbT6r&3Rntg5F-EDI>9n{!(@@i0 zt1?msaRa%abl1RK*NLh49#2qSh5?a)2^`3(GM>&wb#I%^^>4?M;zU6$vmxW*_TYp_ zq~R`S-xV`fVqy|Z=7ZPGEc}tR@2>n+X`@S-%04ktM);|-z^d%iAwkukgEb*dGhRsN zKy6y>YDUnSbo5xCt~1H{+gG|`u!=^*J{Z}c`oaOVD41);(A|M ze|x5D)lxlB5?lPB@&>u%l>SKLCA57oM@gzycK}(~&7UdBuN9bBf zqr7$a0pL4MI&REsY9}=eaeDv}4|uaXfD+6KrmbhZN{M3SXx0a@(m`!s4JTEsywXG- z9BU&_fh&y^rcxS4w$^7tP7GseVkK9^Er*+3OCDDpd0fIYrQ0FSkmRe~Hlq$kwI4-U zMPL2ShF!4Fdi@T4klHfZ=y`qJs8M3-b4&Qt5e!Z4LFg+D$1>4HyjTLAfUC7Zf+!<%cNbSj6jnG0~#Hc!11mTKLjzG{u3 zVRYGDK!uE~(7InObR%dECs&wVq6ltOx>~1?uXcCco}BbeCQ3k@sLuC-SUDE0jZitt z+B>L*;yRN}YGDGT6X`0te*p7glX0^N#M}X5f?}z;-gxV850%CfkK^6y<$wDXba3DW z1cMOYqsN`@pCgGvXq4+SPxPK@f@?hwus5TK}>z+eC0R0!{@5Z-4H2p8nU&QJYDIa-Tbr?_+$Ig{#;h?EvA)RU54CNKOqwK)oq(wKi9p@oyD)38qAO*UbD!} zg|+dzXFsM#ZN}i%x(Tz}$VRb5Cr!igA)g%|k~iqF&+-O+_Sk3b^&?vwm_yv!^jD+h zucjc)GjgAso1Wmd7;}vj?QCuejBKS^EqG~ds<{%H$#PAoSbnwz1%!B2XVz#s(;+3G zSZ&iw`8Z3U-Ej_YfAQ6C z+tI)}N_j95AW2ai&NoF0(Baq6VT6LyR@WULa+2_T8EwJiL;gw0 z3;&rn=${M!i8xCOW;lfb+M52#qt_R;xZjhuI@c60hXs>wms*eklOkkO2+5j1^-KG# zcuJ_%-rH`X9=EB81!3vot)#8f3EET|h6D?eoNRIrR`lc$D-_%SW=)Ku^q)^J5?w&P0@Q{oanMPndo8nET zGHa4N*0fVV9N`mIpD|GpB<+X65lX}45(PnT-n}DTR~EjgqVY+g;fVf@z;EPh!(Sh< zf8=^@gJEMlf!(55A6%Lj-}J8GLBTL0EKhFV$Xqj)ZZzf@!yoX|FU}bg`T6yKOnWiS_*m6yA;ifNBDEj#VxBajkjH#qrn_YblXU1Bf-$} zzG0wP>C+XgkaiJV5-d7RKGtYLHi9rOYA3AXj;3%q{kOywaU@@aN29r*H^L9e?sDPr zXd2nrY7MKfyi;njyIQRLSjR=I{7=R?%ebdFU-izKT{#btYJ-Vr8O6wNWI=#b9h){EgPOtA$IN?3ckjNT*?td0fL5#LR{fScO((CEL8wlXHC*=<7?9S{x-2OD2ahJ zOl$HE>jVF!u!WTq5`Lh!UZVCxE%NTkxD~miSog{aYx`t!9)8_ zR+8N(aF?7WG2Q|r;S34Y&YA*7@fj7an!DK(D(gU+zq5K*&FMU^5LuI&{?cL4_YU5NQM#i^EEXoc$p>qkqsR zv%rsTkx80G#dn*|vD_S;2H@P+Jg*n){4Z8k2yql7`r1kRg~3Mw zQDx9gup9s-&ibHvKfEcl9$%NSzpY_HV|#8~{3azPlKe|}Bf2cjilbY-5?zF@tm-6T z&dJJYRdj*RG#6T|tcrp@q@7-#!W=Tu!HUmT}03MQEk;Xglnd> z*G`Q@*|*hqepmJGnmx69bLPfyF8KC!k3MGL+6@}le$<(3!LuF34xN8htUO1~StpLQ z4sz%X@q^8pM7Cz`Kv}pc>EeZWV2#OHf}eOHfpIJzi0M0I#t2mq#K?>hnEXD;X%R21 z70ywF8h7$EE-gWdShKw;`_#qrAaSS(D>1TBmoDD5OISK#7e*|hNl?ICsL~vebqS)qU%BPs~&%G z6N1|<(1=e9@xv8)lE#^|FvK|1`hdof5nUjj9~tUdof)#$+z?6)P6_oYjb|(f=6eIK zEZAANqUYt{TasJE>Y2Lg!h74R4#` zgKx*Frm~5d%3@`W@PHyrRO3g&(N0JaGEjjjpi0h6l+^vzC?3sEF+)=LAzDqJ#qE$8b2l1rI6wiwdIQ3k?9ZrRZo#bwU#`gDc zS7IzF{3L`rRZn(%tyeySP!xqp>y?Ncrg({j3&JJ1KSt4nyg@U=bd%Z<*UcK!E*P-n zl(_|}aFdd5<-XH2-A1lH!`s|2p}hqm>sDGMl)aMbmDE~uGSxMqwAIgW>kWu;WHoFv z(Bi%6{B4CjDIy^}9Q6X-?a!E3aR_=}A@u2#AvT^A+Ld(N&O-@n(cOlqiSwh7t_Zha zWM<-b8_}^rH+oiZs3f)e>UM(Fhn1Mokk<1AT$b!v!zEOJ1z9Vk_B8;5njR%te}oUoz*v`GWnQndc$Gn8ajUU+R*FFTnPNs)tTO0aPLKLd zQ(;5rVnX-MrZC%Z*W)Ps!-S(oBR^_AKk9(kcuaCmjcJgX`^3h=oA@<)nLppb%JDf? zCaOEI;fLnNR2K}LTn%kRhNbTJsDptqu2T+Uefkb9YiO>uUwt$WF$V-$;B|iz?TRKY z8;`+Y6?$NLVI0NA{}zow^TTB#H>4+AYmFu1kOCW%Mgag7?E2~8YM#wLojuj*N1TW8 zB9w$*xMFu{?5X_0AFa-6^ zdjFp>5?DJx16aZhY>3G=S^sb!+o@b?nyxs_RKi3c0PP|O$RvXK8usSQ+KiZe)WssY zsw3+)!P<=VC~GqI!+C3LMckPMne|OOZN&Rh_$KC^)+s&QuQH}Iq>EK%M%3ugjEIhd z$`mkUMvx&h=Dnb^S1>Oga&TO<2vM7&ic!?az}A@VK{G`QRIaPd*iQ>$h`?w*;7UUg z=Qh`m4r4D!nR026m?&R2pU$ zrU;px^5EAZV0}ZZXO5t1ityXcrC<>lx^D($SvpwA!s4!GhQPRtRl8QEq^+cA-xOXWRTKM<`tw02_JI5b zbEvgM)yy?;*4*JJ8I9i48o;bSW%U%!cdF_}(DPp;MUXFrvE~A$MdmtfHWMCUIvAK? z9ZqcgMlJSrr((1jqjRw{YO!zsO|kFPVrM%Q>(`|0Aq%Z<7Yj{%WQq&> z>8($@+pSkUZyjOKLxYVcV#WmO3z2zYq1O&%oZa2jrjv zMKs`IJHnZuMUX}`TKm7EE<(f#eB*f*zH5MBcOQrx4VSls(!mFB^sHGm_ z=v^N?eR-y;TWs*ODmhwMtilNgn8+};OQg+5Hhr-1uvmr7TM<1VnT~}Z7el~xB;xuY z;yN60w?~^ziD`D;l z5`Lq&=N^u9`ygUD5{Wws6Sx;)^vfu0Uyj|I-3j=mSFNyGr887jCqV%yS3rMtLSKmN z5+b{WWZF|}70b_(#aMm@zi)+P#hr>27=qXpq{YD3!Z6%N=+V)@T>o{7U<%9h-C z7R!IZmLc4$(l9cv^_**CM2Nh-k#{#|f4Dk(oqeBW>%sKwcPwJn723$wuU6Rr-fa~& zGFI6%N>}xkLJh9E7yWg1KTOr$erSs-8_MDtY7V<&vfr`0c5<#4*1L9C$(ya}qxg3? zDq3@Qgz+qeo==A-KnE2_K9ssjIa)HT>Z@AJRec4+7`D^Bw3Gx~X>qBiR2rwVV@Ek{ zi+9xk|3)Ct-@hJLZM!krA|60Is1)&_PsOSM68d@^r8;;+bt0lXx@NrxcpGxpB?AJv zuA%jzjjm^|&R&;t`qk(PLX|y+TVqxYs&MQr4ci2+z^^xcwvN#MdEc(4>fOyXds=Gu zw)*^OJN#EX@9+JB-?lU74ADV}Qi4U};BgQ|u$U@EWF_bbB%e+Q9?vthzh&UIc6)Pc zV$kk4^4EPW%CGxdl=dS=E^GJ^HaKhA&_6C z-Naaq{Z9qkbmdnh$hem?GTMZyi3*lWHem@eI3ZO z6EYEk#_%L#Rtk^7ex9jVGsPjQu`4**1mC}YOnV)o8uH6h#>ZhQ1=L@GPK_u>jd6H{ z*@N!(QqX*bG57N@?p5a8&)>=YC@9JY&<`N1trSNQWm{O}H`o>y`3=U3#}bYuQtPx| zMU?-d9Ed=WK}01TJIGMWXb*k1*|_18Ehh=zQ%d&N{Jx~E2RB8EBC8^dOy?5Vp3jE6 z3KfLM;vETLIul77D~=^H0qaQYj+RM#QDn$%6eAJhd3D2={0|kI|L9=a$YCz^11{wV z*ZpWp;?l^n$Wz!C=(cR+D2BDgh*f&RTr3wM75;<~E#F#=%d_V}kbkf~twhvsU8_8y zybU{}cI&jIfG5@pEnHSby1hqFVMZq<3AHZ&bzgURyuxtCgqGh`B0g+-%E#FS`CY6j zyRAN9-4<;f0nGL;ootlfl`{G%k>uoL)sc%M-AMh^LwE?bVTHzwwT76(r(*dh4GJTI zI^_x6drIVJH!kb?qe)!OM6uipi35rsQ-T?x2*O#|UH)3g(=cHQ?fMFfH0x?o*kL_^0i#(L^D?dYwvbxu#X3{632ck*x*MD<5W| zrcGG2>GNL1(6<_8YlvGJMMe1t%O)2vnNpYwH(&sE^ojD{IvDnEScptcImjPNCb%YL zUovE|#>amnUZ#DLs?Rs6R4iAROphkGB3$m4*6b}NTt#jT846VD7nCxgvR9pI*&p$) zfAX$VEv`?$&;H2d`j^4=MT_h7_ook_2nlZx^h1#KL)y4meoVN4tB-bnmLC@;;(9{J z!1X_c@wk2@6ykbPxEI$`to&!RWdPn+IYn;DKNlRhmeS)F!fm*oMvOx&|60h#wUSt) zyqGp3lz)s3i(>g|LBZ7xd@1J}s{Pyu75b-e51zkdEq^6^isv$+5!bas z7OvY_qkk5@#B(9}Hsz1A^uJPLw+bICz3@*gKPWhrY$-)KyX&F3}Cm`)OL5zpjvuTk=xCxjb+*F_;zlb5ifu-s?8F(AZ!*XCn{GtQsS~dn&dXRKC-#Wjhns^W7}JQb^|XM?+bmmIc~tF@0ldr@`EUU$RKb2 zcFURUuPr2+TXoU=3GtzPQVMO}*K_N^FJl!-)Ew2vA=<6aJ9X&`y&-TH6U-kgc;P1hcL$$Ib=^}tF!h>>fZ z1}2JqStsnjCHqb;i;?$&p;N?j%E^NaeH;b%x$}U2DxrT?NB@jI*Czq}E#J7#a<1<< z*Y}+32hR2Fw?*Hsk}oFu8HVy%LOGRCj_%B7CYmTqYMY|`oxcgleeZ9^RYIlQmx4@t zO5;Sif-I)&cif^}rBU$0YS+1RSKuC38|P}}TAS8>@drTAa6%dJ6Y z0UD!*D;;L) zx2CxN8f8FeAkEwTef)iWsh_5P8YbgFZo_$|emKw6ALp3{;5^g7u(yx5uex`q9?N~S zlt$HrIJP@cNL1GXanO)<8w&$RVG~J%?}hf@&WHw<6eYlFz6{lUje$Iwda{+a$;zD< znFyR7x6jE`i9q1kG8KWNOm?o3z=1lRi>5qT0)C~5wuREJep*C@6?#F5an+(n>L~Ie z%hE-Kn|`J6#WR%?;;hpvq%~5RONqlO*Ytl$`^r+cmcbefani$z&{n3@urkL`!W>h= zi_@%%(2g2NmrA*Zl@de|OG;b{+;2{3qNcC}gAI)dfIzLZva)fM8^$AYOf08CXk$6N zi;Fu}rovYkq%!VD2D?m=1tZG36l1BRWho(G%;2g;m7f_Pmty*ojxqt6CP88Cn$Rd< zTBxJ@$|h|s!wW$s^n?(KlquzZY~DwzVGHTFxdvTjK2X*3hIC7)pA!yfyR+>unDA zxtL+_0BC&rTXW)U0h_zBb(;OH$Fghl?8avJjUMZ*FRZZ8LtQVz#9ustgVEJ;d^w+& zJY2l(!DrE0&Do#fITn9srmK6Sa6LgdRr}V6g!j3vf?-CR`;Tuq;@{^?g4z93Yes2~ zHKQ@BM@C|+khorXd1Q-Va-Ti4pnF}O#c2{!+@A{;_o+imvhhxU3*)S}zUz)ZR(9+Q zAXArTDonKA`jV3XyBpufiRx$QwHsc~H1aoZ;%})D?DW!%ms)gYX}#KyO1k-#AohaeTV9 zI)~3NHs>iB|3OaGm?S{fl_1BpvLRzOMKc zPvDi}I8QnaXTat>GuQ3xKG*AvTk~SE`}5OZxKEufbDwaOS-c!dmU8J*dR?BOaPVo& ze6B9f=IoJ|;xwQB$%envKjNp@)_H7k3fiOG6w1Zy=Wl%hlaO6`Tu&6AI#GPW^T`R! z7*Ls}$rg?#NHMju)_|E}y)BPT2@pS>EYCHZXN{pa-EsA}@e?LqbM2(-ZoDab+Vq<{ z-e*5FXKsFhZnQ>@CgQsf-K~?lVHI^!w+`JNPrzVaw&0X7Za72Z#b+*9E1+g4ep z(+3W49*#k$^G=A=sPPGeLn9jvTTD-z6P}LmgIG-~&Tjn8nD94t3cXIjXM{RFBBa$* zfi=?6ohBS|V=zNHOln!YtuzWUqe(rYBCF@J)ekCVzXPS)#%v214yNpVCfD|C*Pr`l zZ->p%29qK-q|_;pB8SylqTKHPlM^hra3}z%2hdjZI6i76AiSg4<+gMH+i#z72mVC4Nwruh91+^a&*-VAFu#sDjLxL&?Le?qFe;bK4?8Ch zpn^DVrL`vTY(qJ#O|dHajuDHwPzsAPvz1j1UW?Q#q67=O0fejolm;Zjq@GUA-fm|p zt#Ex9A}z9tM=QLuP6=vP{^x-Ew?(n-=xMl1KL}kcdSEH+jysPU;&QjQx>K{ao7_E! z8m1`l-0d*^!WgGZ#EI149t*(uIp&l5In9i3Vji<7N655l0Xpb@(3zCGJ%}zNMJjAT z8f-^qU9a&JLsv7iC7VkndO3kgc=Rgt0%Q$(b)M#1Q=r+V0`f|)sntg`5uBE4f<6px zr*eIZz)j$qstJn`Eq-OvdR0C1tS#07wrP#JAv6tL-F&Vo;aqb%oGF6!$%*1mK0e|3 zINdsP8AxHB`Mgwu3(3i!`I%dzsSt}=@=t0qlS!Y^F;!SMo?tuN$g*K~ zu%RA*pnxjMy}DXxByLlnC0xA0W#n9*p8GCj_41t`50)7pQhe$+p5i?2^D?WAOL*fMuS#%x|&9X zg|=Z$)PyCF+E`ZHjG5OqA~vCvEA#BIms!Y+0NXQrFti#PRZlptn|{!l{9Ut7)8$wD zEErs#KIm-XkP{02ql-Ujx=ZUK>)8K|F6!MOdpESYqf=4!$%0721vYeoVMPLZk)ZY> z$^CPy-14eIf+pM!4%p!@em<)wJrEx}A1bby8Lqvv-B+F3d_J|wSDV^eliKp#zwBiu zT0GS0rz&}(;@v$##k+fg7T8}!C*H~Umq33HjBjTm*)sST0pwm*g8VFQ5Tg)f%)r4D z69uL(iBDTlu|}5%4f-rK`B`(kw+ipH#9#D#0w;#?ddDI7uqGPAZvz#0iuL zTSzK2(L@F~3bKz+cOBqdZ*Z=Ioa;T#<>p+6Ijm8-MBeoQ=Q_l@UNpFla5zM^JNv*b z!r^<~n`R2Vhx=jN59hwIEcg9oxd)qsmU|9O<3oo||LOE|$F^FpZRf9jM~(RUc~!gc znszy*X_wCsJ!C$Aistigg*!j%Ghw)IyC}Z z+j<#6FhpF|eG@tx`p)%z#Px$Q^sVcR)Hyt4d3WkzbsbcQgpPzh2px7KRuIb>)>+3? zf10d;&GBqwq^Zu?mrMcq+2#^j_ouP0R!V3!xxc##;WTJj_OUzJ7o=XCP7&MK=G3XH zvXX`TPLX^Bi;%Yj6E%es7*mKAB;SM&y=d{=%cS^rmIgQktV^RrYrmX|F zFnR`VA$p=#?g0coa!;IoPK&m8?wps?qh5jAVxnF#t7w7Sq|i{bbR~XQ;WzBpR?*IH zA6mWr%9`z0)ove_6K8+vYxlTLMcb-(himq<*Y1tbeof#TFO)_J0Y!@~2=4aI6NVOD zKvMcvWng9udSc*i|3Pe`o8>=*Yp(wYu6h2yD7gp2*@yV-BYgH>_}u;B>{scEsvWUA zHQHglCLI~MuOjbmDRdAGNufh%ND5&Wd!7{f3$v}oz&$cT2H3yrX z({7IbkSh(9+7-uYe0Ja-n$5@5jAZOV$U2jKz(~oO5kyDo$6_d~_l!E|IsM!g^>YPY z_yKn!6>Cpygj}AxqHlVdqqn)}%2oKVQ&Gk$>?A`zMv_*ehz)*+k491hcw$41QC8a! z1HVztV|=IpO>Z%4JRk<{#k%7~4Qj50BhASFcKzwJeYUMvs4E#=nFUe0V=qP*IZk&x zy6{yaLM@utbs$9{&zgHyESij~%QKSo(*)F9o;h0IlUUjomKJ-Rs^uG4{&Os!=%rIg z>}f=s0G;k-ZQE5umdtV~r(|m$XgrP8ROHz(31B%KY4{WW z+cWkwL;A5aF~!K|p2hfZiRyY2#)liHK1RoCbi~6YR!k#nsFUYCwBbZpCtygjE*KV zTHzbte?xHWSn+qCEa89m4S!!R{=OFe{^3Z~%QLU9q(r!bG>-Oy*KSG_H+Z7>po5C1 z@URjCi;Clm4hJzLbA#~a9>rbCkT8l)F3XG;VGssZ!z*6>G=!xC@78YA0KOlJT3)QP zg5GMRo#ZWT)g04agOA?fBLym;r)kB6@`8$cNVHwOzwnQvXu`(~%P89Bj(eR4fdEb##osm2Ii?N zErCbzbqJ+)WNY1csKR;b=YRt9S*D&kUrT+Ij)o#HTf&m7R>oJmr*3Z+=HvWYe2=S+ zzQ={8sl@j?=}BkqsJ1Qaq-}A$RNbG5-BTU9G_u@|jrf7Wj-wo{VqhsD+Uaw)fx?*Z zz8Js<>@NM;+W@{M5dG|JDH>kNIwEKt5n{gk0W)Nj&p&*Zf5dM8$UXj1d$R_iVgGR7 zE(J;WD(uKTxQt@mEM)cj)naP>K9{hKZbYMj+8jLt6)4irKn03oK&hV?!5K#lZLs4C zKnDh+4~%+ablNCv*c!#Bjm8#2JFVQX4dv}6v~;6x{bWZ~$wqX4zGk>*&4}VPBNg^3 za-0ER!+eA@I<13}aV|hzZo#JWA6m+r2)lIqg`SSaR0@UKLkW$q6uXMKba(qtZWzF? z6~FG;fMHxCz1;MwvR;v19^SkGt^~z4)_Ky?wIk5$P~>Up<+cOLGbn=ND-)IWbyRG0 z*stIW)A$Ibk7@TSQzI;0eZ;;ks;0hdK#8%D^;BytinALlJ9e|OsTc>bR36@>^6(Zc z?*}%i91JUI17lKfMN8o|I;(EbSXIsQHS7%T%IS$O9iK`)1@=~R-InfRU`xH?K8zbl zflX{}QE!laQY)()lJV_VrC}Fw^ad*7OZ_bMGsyQ1Oulb14{9j%Y4|Ytt>U^>#dRw~ z({U~V8>rjly@9jD!WV7thN_>G?vw z{UKL@o-=#y+{I%Sse==_&aKRyzhw5j#rgS<C>iX+lAQ=Ey%Yo zDzN7-EzF-Iz@kZI_&Y!#RD#8BbqQ#Gn88e1klD}~7{Dt$V{Pdh@_R)533}+`03-X2h zF^SyF>AAT#-ZI^O)zBqZC6ZCk@$CA)_YzOd+y2WiY}}-OU$+N?QTXo*Ub;kP2Y$As zOBP91lE33@Mb~70GpT=)gB39kOv=o36!kC4%;Y#T=gcg!t;k$jRFqkeX)bWinUlE! z|B4C<<`fhZWjZqpxFpVrasxT1Ey=l5gQ7OYCD|O0bB-j(Brdb)nu0S6`rA~3naB4p zC~!D!=7Ita^&JJi%z}aiPG>;@k^rNC{%j0}+2LfU6AT9+0ZeBYSkF1jW${^y0b`Z( zi3LeE+ci`q>Dm`DPXLCaC@JZ4KmZIU`mw< z4f!L%NOCL!4Au|Z z%@604g3Qc{^I642?%tcT?KfuM zp+4M+C#czn^5@N82<=t4%s&6I+0gOOYWan8Ny!)FFXranK&_67>b!@R3HeJBxx(2? zmPA!vBA1_ExOmZHcBod;>r91rO@y)^WnZ%BF&Gi^7c8(ply7$}bS=q;T`+xm&aAs; zYPLne?D-2QktuHb!}I4oY@ZJt7Pw$X*q1DT8AEo8T8dDezi_@_SHUN8nx0;qKPP|w z6Z!Ue`NHfc(B9nH!tD7A3l?#U7BI_TlzsMs#juN(+2<~La^ZqSu#o105_;m2{QQN` z`HN>iNlLzeI;Qn63V&h#lhL}P80MNi5G|qMFxOr%fAM2a&R(1k0JEmw4eamd9=M;I z#XZP9kjTM;S)5&zZe&PW}QA#s1JT z`;9pmF?5N*p^lFKD7(YXs>6N}me?0By$VE{b+4U)8^f_IyGAr-jm;Kd`~f8$yJ$<% zK?MaC5foP;$VXO92hBlO`^+1&$JlRt2o@9y!Q{CydkQfMW6+Bp61ki1x+`z`v?=ys zL^;hk8$3KQ$|P#z+-w(yboQJ_VXH2lgC64sW9`XU;Hn9#VzoGtGvH5~OolA=!L8o3 zH}yroj@BQ3{k~)l%*=&$Vg6(Ji(Ftgu%0k~&ZA)ZorX6Jc%JKHxZ3b6 zmueVd_{{LF;d$e3!+VD7j7#`04bK`U8{F##@Z(!!mxX;V#2n z{8{62<04~-J7N67_yKnVe~=r?e{cN2__gt%QKx`TKK=dj{W1kEQ{XZME>qw#1uj$I hG6gPE;4%d+Q{XZME>qw#1uj$IG6gPE;J-$J{|Gur_%|6gbtBrZ$eOZq9CF~hz49iBZ)JPqfR3v%774|T0tQ+oq<7? zq(m4|Hix(?@Xa$iE^I9g4#e{{YQmY0Q_z6~wo zE==bf@yj{qE0X+E+_&w^N-j({I}(=1iV`O)rQf!nX%+ns$jZ?av?}^bq@YqN%}@%G ztPdpk+-r&yq$xh7R7vG0eB+7rm_0EblPDRa_EITSDs_}f8{M0Vij^``SxlL^EH?FB z_uis?N*DwjeI6I5+i1PvM6q#$8@EmBZKIpN;e_6n$LQfb1`nszi&-7B+P6CGr{=d- z_w!%0_~S1A=PjYtH&@%%nk{Q%QSFE3wI<8jn2r1<W+KhfwZLN8@?%IY$%%uvRc86zb4+@cht=y35Yg@c5?UMW@2a=K%XtOD@4 z?NU%L;dC2P_q+EM?N!=EDL$YymMRUUN`0v!N@n6&*y%PC1FQxwF|D&b9_^uq)LJUF zl@gCP8lEH28`Rr+dKM)M<9;-iR-z77h&sk3#=So$vdLh|Jzf zpfp0M)BRwC9ApuqjP7EBs!}ORy%_$Ylq7{VnFiJC{C$`4w_f9~PX`|1sg-{jb%kU_ zkcbOOf<+NI&dR?cO370ANX{@(>Vx}j14JnW_vy)^bQSK|Jw&NL?qMTu{9fQZlJFak zWWDnh>bIi+q6?WA!BE9B`nunJ72E+s%t4NKS%rxaY>Ab;c}j!5$|}t0o+|px zQYhE#uoB;+c#jIAFGdPEjEBNjLAWH(|9pcY`g%z1>F2G&^Os~xgX)!fN0QEKyUOc3 zMPIyx0W&C)C#X5-m$9NRPS2w*Lsz1^=pTrA<1zrGcF6&j8KmY?sil++s0$;SYbLbR zPH3&0(1x4~N=>O!TdLGyWO|bsT-RSkX`nC={WMT&Z+(`F|ClrMs7*5k-E|XvK@2cQ z)`2kZnp#guGD~Vdn83T@4<_=ifd`X@y9PJBZ{%Hr1Wy-~APfG@b&4xS^uJKYT2JGZ z_Je6$=GN1xy}W^4nMU4}CYLQC^n-tsJmr%`8lBm_kkZhpZlR$XNK-+p zLecmYi@#JsiMHmoZp+#t%UUE{f>xT9kMp_9(z|o}*BWaJV(M}e z9cGMq-tr^Tf6&-u326G4xD{b;8foK8XwY>GtInGB)^16v+rniIiGp-@L2q=e)FwIN znEHJ-$#rX_Ml5Pr0;n-8QX{su;Hp6GDyp?v3ZqsD>DXs2t^}wl&D=aol!uxvFl{VX zeT#lsYuZTFfFe^+mv96W$^Wun?(i7JB+feXOU?48klhV6*I(V#5Y;=YNQPimA!z0X z@AgpR?hcmldW_|DGavhs+CS}==^v;2Pt53i5@mFBUhB58^15i-Sb1HiR}mH~uM^%I zqsTWyH%!HE8kAa>0>UJZBxAtfCRBPfHzZ&+i~xSXN35I#RedXI%Y>K2${X-9OMl5` zHtet=<_L5lwWE{5H}m3SgGD@R*YyG3Zd(FxV5j$z5Dzy|0LToD}r$cq!zGA-^YD zhJ_)_mdaB*wNeP_GlVN}=`(-rdbL zyIN{@w$|-vTjzeRXuaY}5yo`s5O`m5Q2Do7H8N5cMvc@}oLIR~b5Lm#AehvlSfCEY z4epIao0P`LP!vp~+vE&#anLQOSx5E-YzK{E_O7}*lIsg>%6J+V%=wOdB>Do!mS z#CW9V-0Mkd-gLiJ^tM8>B-AQpv4OO~E%HPOZ3t2`6;Vp~L}GMH?fcB_O8B`u_G~O4 zvGA_RmmSw$d3Og|4WreN$1Ds6f1oN#$g#V(Ck@BT%s#qnacVeS?hA2aVmA12*f2z_ z>dBSH|M1~QG#yCl=>dOXI z2k??Y-d&l4@X+Ahh3Qb0RoV#M=ismfqcqSW5#-HBwYw1Jgk z`mt58c((`fUawW7dUq*E-9-?_66N&(m@eVF~jmh;x{YS^9~gH)7K zvy%0!x?R~%h?QfdR!2Xo+agxk)+;r?^C>8K6+I~!%*a4GQfyM%U_w=bGuTQ=Njw}s zO2=D@bSsY;+Am8I4swQ`c$XDduTwnb9?MmzfYQTNj2>bm{8^3=B-gaYi?O;L`H3~; zk5aNa3wId#L_<2CLQT^Y^p_>5EErU)h)0d{$-+#qR^Clj)ilyiOi=1)IV?{1bkua! zse+V7(m+#Cx@%x=>>^Zrucs(4&45V2L=J3K1y7fv`uEM|hWBI1aH61=*_iftM_^(& z+<32}|B9I_F)#@xn}gTSD)@o5@2dKj+D6whRsEv0jPPJrg;mwBQ-i8O2Wwo4=Dd*2 zf!Va$)r^2O;mFZ`-A9u3msfYkU==4U>;lEw>y}Ha-PpZa;t`(9JT5HiQuWe$v#r0p zzkAg(Jx~%|e7~}frsGwENGFosuNtI9SI|xNhDfU2dfz6WyPye)T8N<5yE9a`tAk8q zml%fL;xMhQl{+X_4TJJ2)qcV=5#-@8wFOrn?Far~_*P9IO>!NqG|FpH9zQv;qF6PS(I8f(Q`^^Kk}6hRZK4?* zYa>^|R2nJFq%@9hYsi3}7{ScMO0JN57SrrHnsL?Bj7ylV^f0_Ms@N z=xx~ExC8!KpI?CnsXeWop4Zop86zgYxRg&W<&(pFpESPDoyaCvq=f%1+&6q6EZ=>b)&#J)wSyd|X$HG=cz?@N(4+?)!pQ_#`%&Zx&>~&MLt0Dco$>|QK zidFBke#0cp=rJ)zPB)6480*ccWigxFsVa;QRTv*?it7U@l;+AtBZ$cOS3sovD}_}m zCnq`;PUFKvKdF2``!HZ))%6jZ+Y`!pA_5du6ZxC|tU~x$h43+hK)4_$dW2Gl663@Y zT)p3&R^N=rnwH-y{@-;R8OUY!EitX+>eJ-*hKb>jw|>jKhIy_nu1tQ_v_P61_LxO( z9=wfr-0xy|)TKSwRzK0vPCkkyGH4o)4Lf;kSniM~PUa3d`NYYNhS6<}Y(m`D{JPQd zx+y^8jHb^m%};S9#vCI>J6oFlquZ!fD_+`~Yp;f7vP=pV$uG2`fDo(N%v#N6rb}@s zR@c1N=%UKCcsPKtTHoV2#zefgqG8;kA^|KBmzox@|3$d}eLtX?e2m54k$xU;uX!7` zY-?nMQXX6cc!4O6$u~s`K=9ij7@^>l)%C}QeL;Bs_KVyhU;Orqc2Juu&1?@G8}>I! zUi7!zA%9!+H^f<5F~TVf(BAwy52`O|@m+Vy>Ks$791=|49cn=aObU}vAtY+^sh>M$ z$5KL__TGLgQQWQ~7J#RRx7KDe$T9x~p#d|0)fae1Vnd4>bH-y!vzcB0VxduR)m*$Z zUuKr8O3y)6dJaaUM|Y0Js>8asOF~sz%7VHfvFa11PX)`kc6nMzh#l7|Piqs*D3Ur5FgKDPCIyq$^mMQ3g@;76$SeYr*(6UQ zm06SEwx*l_;s~Fx`b>xlp=mz}j8Yn>lqv{%^R8{Fy0P#^42@R`jzsi#6n>-M9{J9w zcSo=1HW)U>64(;O`iPVp+x(&7H-ceQNS@NMkxk85y3v?x3?1TUT$(d%808p8uaDVa zj;dhy_uD&i->Chgh2CRnen4h}wG?!xb}O12i}2^%OIuc98t=R`M}s+@__mSIMuMUB zW5Zyv%Bve#A>|Udq*!#Ge5Ub4_#E~!KQfBi@{3lztK>tJ;Cqbsx# zFPx7wq=KZ9HDjA=r?!=t*69Q{BSOs~I{jm}!f_vQhIrQCi~m(Th2}^bL{Ef6Uq+&??e`9C_B1Vs5g z;dVus4wKeYW}R}2)7`_GahuZ}&rdmXwCAZ_)+qGNN1@Ez&iUz-8Y^zj2~zdn`$M| z?_ySyPq#iiiNY$P-y!s(Bxa2t?4UGZ#5jtjh)p#Z^eegH5{sXtoe8=dCN>3&)aV8> zF<%g?&Z_>>IgG1dYn;VdcDu9gB$@mUqFnp^A+7Ik_8Y-K>axB#Aj^`W2eAPiQ-iz@{m z`~*QLQ-mhQOws=|7NN)mz35~`SBV!FO1aI`ha%+(Dfg%8pP|RUmBMh~E-nh-vRDYy z-1_2^?7CE!ii^KPb-`eLc)-fS!i(fVmkDu-CdUIF0Rnu=pTw%~W#UOM4wiJtF!)%5 zmY2m^C+Cn?uvh62;zqX#{q-OTvM1-LJsb93*j}@vc4uH$Su9oUFE-Wb-S074`_#Sd z?p(U`3ai&Ea~0QCCHo-CMSLNOa$9ZhMHl_p54Z6IdRx3`=vuCStuLy{0wX_o1~IPr zQpPp?RbyOJBRxEV-o>RuZ2Ft#?td>q%rpq8uuVh1Z_wJs`h_4z z{tc>tZi43kC^6PY7QKtv6k3n1PkW=iabi(rAZOYXTCA#$ib;xox6o&VBHxIyaua@V7Q~qA5i#uK`!Bkwo(p5zYiUL+(i;`T(?K^J0&mgrMG^zcxtJFefJBuB-@RnG0o+f8q zB-T14U2ljVYSuKeHRC0e#WW>VyvPG<49-&g#ET{pRC*wx@6;KiR7DV_Ge%+XAC?>z z@#1RXJVmH+rW)H4wKWnz-J? zG>w{vR;^SELHYI86LsaM%UZ@u(13$U0}d6T;)d>x)_^63XPC&xQ>zqx)g=w8>ybro zxqU^=2yVB+BD#gxk%~N7lT2C|Vv=cnSd+-Gt`IMb4)(6e2wH1z4kibt2K$u7G7*IE z?ZZ?SVppt*-6Oi#LEtFs@CDvA^l;5k8t0_`RSl_*?^s8=COx2{TfLQRJAlB$_CCg3 zMRjm@s(EkS*5377tqog~8@Kksykgj{M1Gh(GKn2;d(WGBwW)4DMAJ&FbQ1Ao`sV7d^Lhc%QMW86mE-D zQ$?9tHH=ndIBm3WhMIbu7zpl7biJKQ{w;+Pg?l1ZwdoJyNmD4Erv;ow)Ne6l_g=vr zOu`I1!PN|l?d#>LLSIt&NqD$R^+cD)di8S%MNycvK?%#Dlexw{(#hnL$?ElFdjRvm zX|qCf^K>Avn>nsSFksCoqXf2alagxX&S|D@BiE4TX=$9;(TbRLn~=#?uheNtoi+Pr zbx|l~^>f^M1A-ixjav+~dT+XLXF+ckE>_1jQRV7*&b*34)cY!7br*carsG0~l8V~} zSYa)^+Zge2eh^X>;dXS*Ed1^yMmFkR&k7Eeq&9E;cF6jOf2h%`XH3ufHo8@*g`;te zgKRHiD|*aVc#qMbM+PuOh^){EKLD{RtR(vqtoKQT^kz5^2*kyt$E0mC6!Xi_Htk5F zi380Wt@r$qN>RCN<3@LC0zV>);&nojx+0xP3y0R&`BWs8x+AC{HVTfjwMSw*t@Oc_ z1zc1ScHrlyRWIs=daHTnezz)1fr3QgEo64GlQq|*H7C3`K1}Pl-rZp#lNNd|G9LzO zYC_Vk>^Qv3aL>TIrXw));EC3|*fyZ8HPW=Ds);SWJj-q1YFo&Fv_<GbHL=MmY- zz)_D&(JE}yv4E1uj~a>xT2)4_w!A>`e})~Vl$b=s2!|v6g+4?;Om)p0bP=yMXd-Sk z_RUOU8=9heSFASZQcjQhR#V}pNTW=qgq~e}VYcJ0*AYw*C2`=@K}Xj7+&Z{v1xB42+fc55xb#2F}K!4A_ggN zGN~5;P{FpJPN`Pc*mdz#haX8E`ioE!9_j|!HB2asBoSXPH-5oJg7=zoDJw{qh)p}K zvr1Xzo?Z9@+aor;ep$N2roX5@WYYmQN5>Wuno(UXR>uHTwMl?y?O%bZG)3*r%;9Nz zgKXMEKk;MRQHnf##39%$j^YqRo%O*E^aK_U&;ZtO{cEGLP1c8xvaQNxrWuOEOeIVN z0?;mjfJ|bTw{ds&>|zA%BXca`tJushe$XxX003*x99hSE8`H zOF|+)?rPzWU6QQB`s~B@)cs21tb!!r)h>CM*dk(mbM(|4QPm{jNY_$u2@E}p!C4m2 zZ6lAR{cgu&b!kml0tvUPIdwG4sK+HzP<3svBsGkdqz1s@XM%)j0aTUl>RTs&?HbgW zR>hW`X(KL)3fY%|V>EwJG<-;g%3h2W(}Cy{Hq_Euvy)iBvr`ekNFz3&jUw#266Qy_ z?s`|H=lvh+s<;RWBns%Nu6>utkN>(`SBcfJG-x_y`{Dl$*`~d%Bt)uouP|VLlUdy! zMMBi}3^&_UJ39s&31R?7hM4cdT|!vs+TGTxt=Qi-5ff#S_t({}(LG5~TaI8CY$AW{ zYs6a^=aEx8pGO4fxf9v{iGfI%0xd=8(UqCdz=s*D7zIq-NDd=^-Ek%ZiMS%r*K~js zSQ_bNRfTCOVNI0{1@iw!i~T>UznJnREv$EDr`4UX7wNif6@4bnE|9j@HVII)+Lxs7 z6QErTzJzYxj|jW0&llL{5_X}~=r-PqYNOD!3TBcVJZ1*2agb(yR|%0V&>bYK?rLrb zoXcplYh^~-N``iy@HUy6=zm1d@Gjy3{SDzzYl*6xYv8OoqfjyuzNbZi*_$(a3)x+& zx)AmJnzRV=r4SZfV6@0x7oLa|17=u=6Pr$}#ZGl8M!PX0dWRJ&SBw4q-xT{sE%uKt z#frMM#ugleO_5YmEaai}@8+S2^IvyjL%sF)t`6%pFIz|PdTg-C6V)fsi3si6x`p-y zL<>t1ysyRt(8_PI4){b5`wZ?&n?Uv}Fhqmk+jWcSI^wht(x{8pfp4m-5YY@jezWp`A?$pUbAG@%i^&LE)P8SdU1iVkOAQ5O5w2I}e4O2g9z8NV7>%&FZHhSe?Y&;#;xz zihekWm4Gz1@W9Mp)Jjuzii$_)N)swauz|ALA-HzR*pr%hOEHhN_FM^z@K}Dw{kLrm zwD5jcD`vu!@!7FqpFRE=?vJ+sTGYV^vAU1&3&l0>V7SMju;p+#<_KKiK7`TFW3YcY zdUJLc;Fn*v!g7@^P}N-&1)y97`_%<~A-qEf?-UYgQ>|63_>MfrinI8g5fT+wGE(3O zR-A26im}#)w1*T9*h*r>PjsM(!(GafTzD2Me#V|5+^bX2GtTv#b7NQtzrT@pwPby= zI%}PMk7e`z)T|FIV)a#8&o-=9#Q?M0YV2gJwrQNM?kfcwo%OHy>g~QZWcgq%s%L`?x|fxaf-5U3bC*fi>EhT~PTS*MJ;=8a2n_VC$5q>JjJ8My5D+RuKWgT?*`t6oOOwSKvUP?`rtyjR~Usx`YXV{1$B|#(L~u73|ZM-;|)^UQJ7D7pkvUuwJqW zYnY<^uG+|$cU5os(^f7rB^-JiA)C}6DDX3PjY6Z&^T=9nTADEoT3`Pf}>sV z0~^M5)FY}Pzbd7D7NSx>{Y}`^uyVv0gGabMAh(Z#<|~akUxje5Hs^fxLC&WEQQiwW zfULGu96^-rVUhQ-JuLD*CW=Snj>c2#v|&Y*UsLvmp~#@35{~X?sHJrTPZk?Dd|vVe z;d?^K`i9?=uzCNcaA9~=n6c@69NYBScyED%@K|hmT!;=u($0#b@l3&{$M;0bWW6Xd z+53hz6jbR`g^uK> z{h-lAA-@KlOYJ$PRigYciwcpi1=TAbWTB=_Shnf+QP|MG24!nWS{X-0`7q0-DPSU{ zuqoW2LD>F5#OibF(A5A2=&ZVfpKRO)AxGNH0Z9co#h^3K2U&J(T9FTc_SQIN(3)p8~;R3#t^DR}| zDNMulkF@Sov4s`-n{Yp#PqUW)E_{jSa-j*=wL&JYTUn!j5KiN{faW$8PqOsasj)kR z&y+s+Csyni97>jyq?~$JhnL!0{Y%B)1o|_ne@6Dl^So|%5c*?Bw3)V6*d{;qBwY+- z#Pu}%S~{g>@wYixLcW%O6MxvJYieD^6~r8H4Rp~#LlpE=n7+f+JD`@HT z$Qd<>n0jI__8F$EJ`^^3T~27d-f+z1FMP#{vv{Z|f38YZdQwc*r;}Yq=chJjg>e(C zQdE2EzuUkI#s`Dviem}TlX zEV$Q|3-pr-{qJ=2&+21+GSDyi*7+UhJjXe|=bZoKoM+Ayo>?Vd%Jj1gPp=*f>(974RSluD^v7r77&kQGUZ6iq+ip{Zgy_`*5NrHK&hV) z&M5+{lPI5r&Y=gc2OK2XnwgUgO?(bfop3yX#+`R1iSj?`3BFYY-C-vBpcHbcXon}3 zP69klvpAe8LXi-aZ}(t73X;kB$8KQnu3*20-NF8ZDHb~01kq?5uQ}X~3GIfoqgO!ND|c-|gq??@j(P`O6SF2XZ?O zG!4Lkrhz!nGzbTp28TTTJpI+pI}KRwqop*eCd9Dqi9)b}iDev$k|8|{;oyDl;jI9-afZ&sN=;Mg)1ful@zu#v!lJMK${ zJb3~>rJ44H(zbqDMTHl7u`X(=7CBQ#kr!E(E-C!ezbJg^NachW>x?RCja2SbVzA0J z;~QyDdGh9Rc!NPsdR!6O%avMI<|s;-qe^&ioK+DzPy^{w8TYtSiYQ`fsZ+u9n-iL; zDMVheqcIK;sFgNWHimLTctnng zB?yceT(zk369eQ_OsDBA6Od^Z6xOZ@jRK~H2;EaYd2=~l2r{831W}}1sVI}m6gJMe z)ZuPyVGLhigpG#uD6=PY06*Ppol$ShQ;c~<#z$Gl6?xoiR>!Iqh}=Bk=i#fH*B05< zx@~JYoBTqlqtAQ86IP%r>Dlt$@T;tMUh6s^)eUX{jZJ-TZj8-ub5*rXx4-v9R$Z>$ z*n)YZ+j__L6&8A^??agQiY9V!x>}Dt%jYJJ6z_DrfYxfi`w270qH|`tx;6?o5rh+U z?~RK4nAT~Um9=P(37-1|L1$2k4*85UsvZHq^l8+hF>|lzU!DEH{A+iQ-;9@tv?O1N&C_;}IN;S}4-I=-wHs{WOmIL$5f*riH9B|TZklXA! zS#&aStCHr&X;Qfj&6Zm4yjr14x$*<9L4^<-;Zt&51?KwPigwq)GR31;qT_{fu*fU5 z$S<+c<5c2dVsKcp45zK27qQneUTVSl>9(3|KF!#YtEBx0J*872zbN~?PQ@xsptqLq z%GB~l%lX^Ks4|#pFE`u2j+IJNA%3arir#el-z%jc&ovYbJ%y3a)#utAy>gQr=2Jh~@OSD5{1n?dw=G6N zdz70*xfuQY9osPo*_Frjc+rXDMaSKrAIFFRmuZ-ka5O-QsGYS2j1=pg_p>1Z=BJY9 zxrTGEF%+eyUwhqziPulMVe*Z)+?q9g#%-PNa~_>LFE3wrTEj=;%}#eu>*NHiqE1fe zwC%A34DMwsjtS$2Lo^P) z*nWuBwBhi^8^*Xd*fI2a1>X_s{ECoPPX*RQZ8qbS8-p3tVN%QDonJ>lrZuZ)RAlvF zw)#b-tPfyx+nH|x$HA1f$K>3a<@`hctgY}l+Tl_}hm^VmQsj_YOO!i&&nsaH`GwPd z#zDC=E!ONj;vLwbjm2+A#HDh_uQo9&MPNbxBZ3`L6-@ZHZvpNolAX z9fU3xJ+KsZ_q|69F*#e?T*+BmO|D+V4O0Yo&Q`d7A@tK_;zVk2j|E_S9GjE-IL(c3 zW;13{4wGxu3Uokzz>$!%H2{*4A{DeE4Zb7uuGhGWV5_7uH--x0PN^_EZ7@4NxBl4{ zj9d)E0+?g=o-7C^Fh)=M4Z*XeA@Z`P}QMJ+2JC&PS1#TkOTtirlYV|3T*Q@54XKgV8*se7q zLuekpy5)Rx-1(MNOr{9d=f{ga|LnN?vsCM>XTS>Ute2%yTu4v;#Ls#mk_xq`CI6@< zL!YXpgFIEbiE0F*Ye=PP&Vx}o-6}CKTBUATr7y#0x?FRmzb%L@R~kvNYWTNUzb*d1c1#~&!5igItQ7Me)f6j%u-Z*Uqpr@Qx_ ziO%EgoF9nNg zXNBtS>G0Mhw_HeW_SPl0)h4%|`-i>UM2m+y|5PJST)fW;F5YJaFW7RkhdKWe`0s}E z?T9B|245q9-ph=WU%(A&6sn9lICx^Jz;v4Av=y_4T3sJB>Z8=;WqIm&VS^<=qZIYL zQt39S^d+fu3rD|5;$*4xMyYfn5+_n3d?Bg8L>?NzQILH?s&g;r+{ZchbIy-Ar;BqQ zZ2={}yAI#aeJm=%(Is2Q1*82}k z=Yt1MJ%8%OqnoWag!vmjR3pAVUbQZ~W?fEb*5xFkhiuNDq|Ny^V>&uj$&pC)ghIF@aUG}Sx$lPe&<&{9h4{?yksN-3=- z4|G){oCYh)zIG>j14PBCB(a@sPMzk;OcYLbiR2?#guJB~uu*F@aPBa(_Rmm`zIb#G z?hpffPm7&$r}W#DQ%JonDwQy1*4lqN_gxq=dY(;)S~TOiSuxJ)XRTo zRMabK70rLA6daD0uEy^g{6_q%RkRCRhu3Vqx_0X|bz4Vd$Jqb)jqAEDMcZq3hH7_p z)a?$_eof#T%g%mA?=5h3bR96X=mM6~$144^qRs z4`m(TvkvoFukktWhO*wGE2?(J?$l_9bERWlN?2O;xp)?One zYfccIsUMH7u--p*zx&jSCF=JIJeUXEgH)_Nu^w`|?~T0aY3Y5Zz1Hw#`?miw@oU6#=!QuSC`(PIW#y@dYAkC6d>j=c15n%{eC)PQlgb z9?eiP0YRsGu7+j;ODkb%(bs8OzJcXm!}9Trnks=}ZzF4SI?Jatr#n%rGb1WZ&FQQU zWHUU3lg&lfp%%KP{)x3^M?K^}aKBUOBlEej;!Rhv+)pF9Vyy$~FPHEq_b=EF(w+9y zJ^eP@toMH<`lsX5vwf}ii>FiZEQKAsEA|w<Lpubc3U!1q27mlWgNk%^B9N68b zTwUn_yYt;h#D@m-FCf9cgggzG@Gu?jl=;;(e}^z1ORvb3LYVbyD_-&o zIOv)alZeG+q|{+uOMc;26V;xHst*ui;UlJG_1n2GQaXL0hbe&0Dd3)z&HY9Hudp*l zh%Z&_DKSSG(W%5_r@ISFf$%CLZTl2b#;Ko90q-NUSnprqbEnHgg?Y4(KJwKF;ij?p zcEHpQz+fR@mzj-<4=>wkfMD-$#20B$nv@nhG)wGJ_1UjgefDc+AMhmtZRr_As)tGj zbP3|C?ulCSVYTMNYR#C=xg9pvJ@}^R*D^YrOl!jw^ugBx$om&3+(70^0b zn4r9X;u;odSEm=#&vq-e&cxW8SBLLj)zf#c&@`3!emgzs!W}Vc3%VG!#e^2y zOyz&9(`1F8wPU}#e?jMoi8j%{lo0Lm*;xOAs3^MVUmR7uYq`Zy<;u|TQbx#PjgZAr z-`9Y(ALI3n+~FIw(>HpTZ_Mt@A!yh)(z`=J6287VdKWHZ7?}%L{Q-3tTEB)VUC@a? zbyRWWsF8oUe$>dnJPMThDG!__)X;`FJ`L!=VC>#8`^Kh>!M><5e9Bnt)U(qX2-~;a zQA#T$>JCeGB$Qk+*H>#sy4Q>D7@f8xY{35W+fl zYN~eBcpZwkrB~bcD$k(^PKjKvbgZLdV?#a#M};PYDScdrPni~G>FOi4M^QEPWdll# z53i?M<58U5SlRK#%BCWmcT#0|ohrlYv5M|rr%EuKk+n=n=_4WqlXOvCtBI{D)+cZam^RQflt^a=8WHV0+3$h%vS zh3|1-Y_jiGQSacCt@^zO64p)lOc=^9a3p9=vHqj6k5259-qc6!#8^a*sS~SQ7v&ca z7^dAlIk!#EzRCW0-kf=POYDml3-^34Kl~R< z?c4s_Ky2LPe_yv3gHiDB3tqlNMkju@gv%C5R1(i|w!%pn-%cKwkj{#j2Pb4?q!$h> z%*fz4Gv~-Cw5`ZkR#=#kpJC2-%$=LD0{;s0^XKO07iKsz^0@@gfpUX6hb_UeOoO5} z#U(ma-<_a zKi>=(kSANeZ>c|QHV62&su0WFwh0Ld11GJ>w}I8fY1?=CD;)VNRyYc6=4Flri}P3H zudp!~8MbuR&0rz=hF~mIVH7YJ8Nh+^?WSSBBNz$kivfetVVjZw#+&nPi>7iM=&*fP z$mm$4((xExz*pco#+I(an2MxAU~WrTrowP!5FNIB8+w)FZgJm^F<7|DyeJ-v zHNoJxf!x6K2?>sTa{^%41`dQ=**pND$`u+@VL+}jqG2F|!O;4HU?4x=!TMue?;X9_ z_!uw+eZycl1}79|n3rXwXG|VA5aYwPDxb!OV`_dzMt&j22gXE(EyD&Fh3RIZW2!1w z`GpC{&xc$&Rt%nse2gq=Gs!a9f>N9fqn+MS#X;}dAGL7lUuUWcLoET$s~HYFvA+Z~ zi8gz$dvYDUk+`OBq)iDv9_vmsVZ)rSNFlXt~h{=oR^70CnEPlcc(@J)onegt3F!p2Y zOBX)@Cu07>h4x4D?9N5brFrlRX3WT*eeW#Ix5%F}e<39@!)N4ke{NKB2?!unlIQ@@bR2xrBxgDO^V~;LeJa@s8 zyeFM`OX1=OOXe(EiXO1fowI0e-a;_N{^)Z1E!pTXkVN26N5_ARJ>AZ#!+#N$+LtW5 z227g$fSrLG$FVHC#x&-Q%@N@I0VN%~NK27U1qT)r6lVe0M_x@Q&%sywtXs0i*>8Ci z9ux|}<+&wmDhUb`&`TcTxm)kOH+RPLsrC`XIn6m6Ix;>YBx>W_94EST&fEp?RhP^K z#kj#)dpa7p>Vm3REs5t0_|wLoVd-2rh>PsP{3r4jJ0U0#8e#t21rXQmhCdr77(eHp zGdyp2iR)*$*6;$CY#3%ZX*gqe$+*+-k>N(;QvS5z1>+RMQo{$v7Y%P1t})(Vc+Bt^ z{~b5LIM;Zb`1^6t(54_J%WBA;7 zvthYmIX~WTui;+)JL9v)#l|3a-1xQe5O*`bpBvAAZ#-oD#<<_8>l&SK{r$@Q3J0!m s;0gz>aNr6Du5jQA2d;473J0!m;0gz>aNr6Du5jQA2d;47zsG_91%92;3IG5A literal 0 HcmV?d00001 diff --git a/tag_fw/fw42.bin b/tag_fw/fw42.bin new file mode 100644 index 0000000000000000000000000000000000000000..f8571ed9d47bca0a4e223e0064ab13206f0021e2 GIT binary patch literal 31871 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 literal 0 HcmV?d00001 diff --git a/tag_fw/syncedproto.c b/tag_fw/syncedproto.c index dfbac9e5..cb390b2d 100644 --- a/tag_fw/syncedproto.c +++ b/tag_fw/syncedproto.c @@ -133,7 +133,6 @@ struct blockRequestAck { } __packed; #define TIMER_TICKS_PER_MS 1333UL -#define RX_WINDOW_SIZE 10UL // ms // #define DEBUGBLOCKS // download-stuff @@ -149,26 +148,27 @@ uint32_t __xdata curHighSlotId = 0; uint8_t __xdata nextImgSlot = 0; uint8_t __xdata imgSlots = 0; -// stuff we need to keep track of because of the network +// stuff we need to keep track of related to the network/AP uint8_t __xdata APmac[8] = {0}; uint16_t __xdata APsrcPan = 0; uint8_t __xdata mSelfMac[8] = {0}; uint8_t __xdata seq = 0; -// timing stuff -bool __xdata isSynced = false; // are we synced to the network? -#define SYNC_CALIB_TIME 10000UL -#define MAX_NEXT_ATTEMPT_DELAY_MS 300000UL -#define MIN_NEXT_ATTEMPT_DELAY_MS 1000UL // wait at least this time -uint32_t __xdata nextAttempt = 1000; // delay before next attempt; -bool __xdata isCalibrated = false; // is the RC oscillator calibrated against the AP? -uint16_t __xdata calib = SYNC_CALIB_TIME; // RC oscillator calibration value -uint32_t __xdata APburstInterval = 0; -int8_t __xdata calibVector = 0; -uint16_t __xdata APburstLengthMs = 0; // burst length in ms -uint8_t __xdata APburstLength = 0; +// power saving algorithm +#define INTERVAL_BASE 40 // interval (in seconds) (when 1 packet is sent/received) for target current (7.2µA) +#define INTERVAL_AT_MAX_ATTEMPTS 600 // interval (in seconds) (at max attempts) for target average current +#define INTERVAL_NO_SIGNAL 1800 // interval (in seconds) when no answer for POWER_SAVING_SMOOTHING attempts, + // (INTERVAL_AT_MAX_ATTEMPTS * POWER_SAVING_SMOOTHING) seconds +#define DATA_REQ_RX_WINDOW_SIZE 5UL // How many milliseconds we should wait for a packet during the data_request. + // If the AP holds a long list of data for tags, it may need a little more time to lookup the mac address +#define DATA_REQ_MAX_ATTEMPTS 14 // How many attempts (at most) we should do to get something back from the AP +#define POWER_SAVING_SMOOTHING 8 // How many samples we should use to smooth the data request interval +#define MINIMUM_INTERVAL 45 // IMPORTANT: Minimum interval for check-in; this determines overal battery life! +uint16_t __xdata dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in +uint8_t __xdata dataReqAttemptArrayIndex = 0; +uint8_t __xdata dataReqLastAttempt = 0; -// buffer we use to prepare packets +// buffer we use to prepare/read packets // static uint8_t __xdata mRxBuf[130]; static uint8_t __xdata inBuffer[128] = {0}; static uint8_t __xdata outBuffer[128] = {0}; @@ -187,13 +187,6 @@ uint8_t __xdata getPacketType(void *__xdata buffer) { } return 0; } -uint32_t __xdata getNextAttemptDelay() { - nextAttempt *= 2; - if (nextAttempt > MAX_NEXT_ATTEMPT_DELAY_MS) { - nextAttempt = MAX_NEXT_ATTEMPT_DELAY_MS; - } - return nextAttempt; -} void dump(uint8_t *__xdata a, uint16_t __xdata l) { pr("\n "); #define ROWS 16 @@ -261,15 +254,36 @@ void initAfterWake() { initRadio(); } void doSleep(uint32_t __xdata t) { + pr("s=%lu\n ", t/1000); powerPortsDownForSleep(); // sleepy - sleepForMsec(t * SYNC_CALIB_TIME / (uint32_t)calib); + sleepForMsec(t); initAfterWake(); } +uint16_t getNextSleep() { + uint16_t __xdata curval = INTERVAL_AT_MAX_ATTEMPTS - INTERVAL_BASE; + curval *= dataReqLastAttempt; + curval /= DATA_REQ_MAX_ATTEMPTS; + curval += INTERVAL_BASE; + dataReqAttemptArr[dataReqAttemptArrayIndex % POWER_SAVING_SMOOTHING] = curval; + dataReqAttemptArrayIndex++; + + uint16_t avg = 0; + bool noNetwork = true; + for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) { + avg += dataReqAttemptArr[c]; + if (dataReqAttemptArr[c] != INTERVAL_AT_MAX_ATTEMPTS) { + noNetwork = false; + } + } + if (noNetwork == true) return INTERVAL_NO_SIGNAL; + avg /= POWER_SAVING_SMOOTHING; + return avg; +} // data xfer stuff void sendAvailDataReq() { - struct MacFrameBcast __xdata *txframe = (struct MacFrameBcast *)(outBuffer+1); + struct MacFrameBcast __xdata *txframe = (struct MacFrameBcast *)(outBuffer + 1); memset(outBuffer, 0, sizeof(struct MacFrameBcast) + sizeof(struct AvailDataReq) + 2 + 4); struct AvailDataReq *__xdata availreq = (struct AvailDataReq *)(outBuffer + 2 + sizeof(struct MacFrameBcast)); outBuffer[0] = sizeof(struct MacFrameBcast) + sizeof(struct AvailDataReq) + 2 + 2; @@ -290,9 +304,9 @@ void sendAvailDataReq() { } struct AvailDataInfo *__xdata getAvailDataInfo() { uint32_t __xdata t; - for (uint8_t c = 0; c < 15; c++) { + for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) { sendAvailDataReq(); - t = timerGet() + (TIMER_TICKS_PER_MS * 10UL); + t = timerGet() + (TIMER_TICKS_PER_MS * DATA_REQ_RX_WINDOW_SIZE); while (timerGet() < t) { int8_t __xdata ret = commsRxUnencrypted(inBuffer); if (ret > 1) { @@ -301,12 +315,16 @@ struct AvailDataInfo *__xdata getAvailDataInfo() { struct MacFrameNormal *__xdata f = (struct MacFrameNormal *)inBuffer; memcpy(APmac, f->src, 8); APsrcPan = f->pan; + // pr("RSSI: %d\n", commsGetLastPacketRSSI()); + // pr("LQI: %d\n", commsGetLastPacketLQI()); + dataReqLastAttempt = c; return (struct AvailDataInfo *)(inBuffer + sizeof(struct MacFrameNormal) + 1); } } } } } + dataReqLastAttempt = DATA_REQ_MAX_ATTEMPTS; return NULL; } void processBlockPart(struct blockPart *bp) { @@ -359,7 +377,8 @@ void sendBlockRequest() { struct MacFrameNormal *__xdata f = (struct MacFrameNormal *)(outBuffer + 1); struct blockRequest *__xdata blockreq = (struct blockRequest *)(outBuffer + 2 + sizeof(struct MacFrameNormal)); outBuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct blockRequest) + 2 + 2; - if (requestPartialBlock) {; + if (requestPartialBlock) { + ; outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_PARTIAL_REQUEST; } else { outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_REQUEST; @@ -398,8 +417,8 @@ struct blockRequestAck *__xdata performBlockRequest() { break; case PKT_BLOCK_PART: // block already started while we were waiting for a get block reply - //pr("!"); - //processBlockPart((struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1)); + // pr("!"); + // processBlockPart((struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1)); return continueToRX(); break; default: @@ -411,7 +430,7 @@ struct blockRequestAck *__xdata performBlockRequest() { } while (timerGet() < t); } return continueToRX(); - //return NULL; + // return NULL; } void sendXferCompletePacket() { memset(outBuffer, 0, sizeof(struct MacFrameNormal) + 2 + 4); @@ -451,6 +470,15 @@ void sendXferComplete() { pr("XFC NACK!\n"); return; } +bool validateBlockData() { + struct blockData *bd = (struct blockData *)blockXferBuffer; + // pr("expected len = %04X, checksum=%04X\n", bd->size, bd->checksum); + uint16_t t = 0; + for (uint16_t c = 0; c < bd->size; c++) { + t += bd->data[c]; + } + return bd->checksum == t; +} const uint8_t epd_bitmap_ant[] = { 0xff, 0xbf, 0xfd, 0xdf, 0xfe, 0xdf, 0xee, 0xdf, 0xee, 0xdf, 0xed, 0xdf, 0xd7, 0xbf, 0xd7, 0xff, @@ -529,7 +557,6 @@ void saveUpdateBlockData(uint8_t blockId) { if (!eepromWrite(EEPROM_UPDATA_AREA_START + (blockId * BLOCK_DATA_SIZE), blockXferBuffer + sizeof(struct blockData), BLOCK_DATA_SIZE)) pr("EEPROM write failed\n"); } - void saveImgBlockData(uint8_t blockId) { uint16_t length = EEPROM_IMG_EACH - (sizeof(struct EepromImageHeader) + (blockId * BLOCK_DATA_SIZE)); if (length > 4096) length = 4096; @@ -562,19 +589,9 @@ uint32_t getHighSlotId() { return temp; } -bool validateBlockData() { - struct blockData *bd = (struct blockData *)blockXferBuffer; - // pr("expected len = %04X, checksum=%04X\n", bd->size, bd->checksum); - uint16_t t = 0; - for (uint16_t c = 0; c < bd->size; c++) { - t += bd->data[c]; - } - return bd->checksum == t; -} - #define DEBUGBLOCKS // Main download function -void doDataDownload(struct AvailDataInfo* __xdata avail){ +void doDataDownload(struct AvailDataInfo *__xdata avail) { // this is the main function for the download process if (!eepromInit()) { // we'll need the eeprom here, init it. @@ -681,7 +698,7 @@ void doDataDownload(struct AvailDataInfo* __xdata avail){ pr("]\n"); #endif - //timerDelay(TIMER_TICKS_PER_MS*100); + // timerDelay(TIMER_TICKS_PER_MS*100); // DO BLOCK REQUEST - request a block, get an ack with timing info (hopefully) struct blockRequestAck *__xdata ack = performBlockRequest(); @@ -711,14 +728,14 @@ void doDataDownload(struct AvailDataInfo* __xdata avail){ } // BLOCK RX LOOP - receive a block, until the timeout has passed - if (!blockRxLoop(440)) { // was 340 + if (!blockRxLoop(440)) { // was 340 // didn't receive packets blockRequestAttempt++; if (blockRequestAttempt > 5) { pr("bailing on download, 0 blockparts rx'd\n"); return; } else { - //goto startdownload; + // goto startdownload; } } else { // successfull block RX loop @@ -815,7 +832,7 @@ void doDataDownload(struct AvailDataInfo* __xdata avail){ sendXferComplete(); killRadio(); eepromReadStart(EEPROM_UPDATA_AREA_START); - //wdtDeviceReset(); + // wdtDeviceReset(); selfUpdate(); break; @@ -856,8 +873,6 @@ void mainProtocolLoop(void) { // i2ctest(); pr("BOOTED> (new version!)\n\n"); - isSynced = false; - isCalibrated = false; if (!eepromInit()) { pr("failed to init eeprom\n"); @@ -869,20 +884,27 @@ void mainProtocolLoop(void) { curHighSlotId = getHighSlotId(); } + // initialize attempt-array with the default value; + for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) { + dataReqAttemptArr[c] = INTERVAL_BASE; + } + screenSleep(); eepromDeepPowerDown(); initRadio(); // drawPartial(); // i2ctest(); - + // doSleep(10000); while (1) { radioRxEnable(true, true); struct AvailDataInfo *__xdata avail = getAvailDataInfo(); - if(avail->dataType!=DATATYPE_NOUPDATE){ - doDataDownload(avail); + if (avail == NULL) { } else { - pr("nothing.\n"); + if (avail->dataType != DATATYPE_NOUPDATE) { + doDataDownload(avail); + } else { + } } - doSleep(10000); + doSleep(getNextSleep()*1000UL); } } \ No newline at end of file From 2b89919d002a4738028a91035f846f0f1b9222da Mon Sep 17 00:00:00 2001 From: Jelmer Date: Sat, 21 Jan 2023 11:33:06 +0100 Subject: [PATCH 2/5] added prebuilt AP-binary --- ap_fw/zigbeebase0006.bin | Bin 0 -> 17629 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ap_fw/zigbeebase0006.bin diff --git a/ap_fw/zigbeebase0006.bin b/ap_fw/zigbeebase0006.bin new file mode 100644 index 0000000000000000000000000000000000000000..7bb5bc7a4d595e474a55a8bd353bf5b3e26a5a4a GIT binary patch literal 17629 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 Date: Sat, 21 Jan 2023 14:13:53 +0100 Subject: [PATCH 3/5] updated the readme, removing the 'slotted' stuff. --- README.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 76b308de..be0d55bb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # solum-esl-alternative-proto ## ⚠️⚠️⚠️THIS IS NOT PRODUCTION READY!⚠️⚠️⚠️ -This is not a final, polished codebase. Not by a long shot. It's a complicated system, and it is not unlikely to be a dead end. There are drawbacks to a slotted protocol, it's hard to debug, and it might not work all that well in unfavourable RF conditions. You'll need some knowledge on the use of these tags. A very good place to start is here: https://github.com/atc1441/ZBS_Flasher. You'll need to fix issues yourself, troubleshoot stuff. Once again: this is not for everyone. +This is not a final, polished codebase. Not by a long shot. You'll need some knowledge on the use of these tags. A very good place to start is here: https://github.com/atc1441/ZBS_Flasher. You'll need to fix issues yourself, troubleshoot stuff. Once again: this is not for everyone. This is an alternative protocol for the ZBS243-based Electronic Shelf Labels - ESL / price tags by Solum / Samsung. @@ -12,10 +12,11 @@ It is currently compatible with the following tags: * 1.54" ### Aims -- Low power (currently around 8µA with 30 second latency) -- Low latency (tags can check for new data every 30 seconds) +- Low power (currently around 9µA with a minimum of 40 second latency) +- Even lower power when there's no AP around +- Low latency (tags can check for new data every 40 seconds) - High transfer speeds - It can do about 5kbyte/s in favorable RF conditions. This allows for lower power -- RF-spectrum friendly - We don't need to acknowledge EVERY packet, and we don't need to transfer data we already have +- RF-friendly - We don't need to acknowledge EVERY packet, and we don't need to transfer data we already have The entire setup requires a few tags, and an ESP32. A (preferably, but not necessarily) broken tag is used as an 802.15.4 radio for the ESP32. You'll need a ZBS_Flasher in order to flash both the AP with its firmware, and the tags. Using the 'mac' option on ZBS_Flasher makes sure a tag flashed with a custom firmware has a valid mac address; it used the stock mac address assigned to the tag if it hasn't been flashed before. If you want to set it yourself, you can edit the mac address in the infopage. The AP expects a tag with a mac that starts with 00:00, followed by 6 bytes. The MAC-address also needs to be set on the AP-tag. @@ -23,15 +24,10 @@ Once flashed, you can hook the AP tag up to the ESP32 by connecting the tags ser You can access the ESP32 with any web browser after connecting it to your WiFi Network. The file browser is located at /edit. For sending data to tags, you'll need to upload the information in 'data' to the ESP32's filesystem. After uploading, you can access the status screen at /index.html. If everything is working, you should be able to see tags synchronising to the network. After uploading a suitable .bmp file to the filesystem, this file can be sent to the tag by entering it's 6-byte mac address and filename. -## The synchronized/slotted protocol explained, kinda. This is simplified, the reality is even more convoluted - -- Every 30 seconds, the AP sends a 250ms long 'burst' of packets, each marked with a sequential 802.15.4 sequence number. The tag, when synchronized to the network, listens for a short while every 30 seconds, to receive exactly one packet. Based on the sequence number received by the tag, the tag can calculate clock drift and window-offset to the 30 seconds interval, in order to remain synchronized. -- At bootup, the tag will try to find an access point, and get timing information. It will start a calibration cycle in order to characterize the difference between the AP's clock and the internal (RC) oscillator. This allows for a fairly precise reception of the sync burst. After initial calibration, the tag will slowly adjust its calibration value to compensate for drift in the RC oscillator frequency. -- When synced, the tag only listens for a -very- short period of time, which allows for both low power use and low latency. -- The ESP32 can send a 'pendingData' message to the AP, indicating it wants a specific tag to check-in to the AP. -- AP adds this pendingData struc to a queue, and adds these tags to the sync-burst -- The sync-burst packets contain up to (currently) 2 mac addresses of tags that the AP wants to talk to, and an 'offset'. Tag receives a MAC address, see if it matches its own, and when it does, it sleeps for [offset] ms. This allows for minimal power consumption and maximum throughput, as the AP determines which tags are allowed to talk. -- When addressed by AP in the sync-burst, the tag wakes up after the offset period, and requests information from the AP. The AP responds with some metadata, such as size, MD5 checksum, and data type. +## The protocol explained +- The tag checks in with the AP every 40+ seconds. Actual check-in interval is highly dependent on RF conditions +- The AP holds a list with tag MAC's that have pending transfers. +- If a tag checks in, the AP replies with either no data, or information about a pending transfer - The tag checks if this information is already downloaded to EEPROM, or is already displayed. If this is the case, the transfer is immediately cancelled by issuing a 'transfer complete' packet to the AP. - The tag then proceeds to request data in 'blocks' of 4096 bytes. The AP responds with an ACK on the request, and specifies how long it will spend to gather the data. The tag sleeps until the AP will send the data - The AP requests its block-buffer to be filled by the ESP32, specifying MD5 and blockID @@ -56,7 +52,6 @@ You can access the ESP32 with any web browser after connecting it to your WiFi N - Do more with status info as sent by the tags ## Known issues: -- The RC oscillator has some jitter, especially on longer sleep times. I find it difficult to have the tag sleep for much longer than 30s without losing synchronization to the network - For some reason, the screen needs to be reset and put to sleep -EVERY TIME- the tag wakes up. This is a relatively slow process; it would really help if we could find out what causes this. Some glitch on the reset line of the EPD would be my guess... - The ZBS CPU should be able to sleep during the EPD-draw command; however, this currently (for some reason) increases the sleep-current-draw - Some tags work better as AP's than others. Your range may suck. The boards on these tags are tiny and fragile. For instance, a dab of hot-glue on a board is enough to warp it pretty severely, and will damage the components that are soldered on there. Reportedly, segmented-display solum tags work well. From 574d0400501710c4f5f678a72f29e7bfa8961fd9 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Sat, 21 Jan 2023 14:22:06 +0100 Subject: [PATCH 4/5] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index be0d55bb..66b81ba1 100644 --- a/README.md +++ b/README.md @@ -46,12 +46,14 @@ You can access the ESP32 with any web browser after connecting it to your WiFi N - Implement battery reading - Implement RSSI/LQI to be sent to the AP ### AP: +- Important! The AP needs to be able to tell a tag to try again later if it's already doing comms with another tag. The AP can't handle concurrent checkins/download due to memory constraints! - More reliable serial comms (sometimes bytes are dropped) - Include source mac with blockrequest struct ### ESP32: - Do more with status info as sent by the tags ## Known issues: + - For some reason, the screen needs to be reset and put to sleep -EVERY TIME- the tag wakes up. This is a relatively slow process; it would really help if we could find out what causes this. Some glitch on the reset line of the EPD would be my guess... - The ZBS CPU should be able to sleep during the EPD-draw command; however, this currently (for some reason) increases the sleep-current-draw - Some tags work better as AP's than others. Your range may suck. The boards on these tags are tiny and fragile. For instance, a dab of hot-glue on a board is enough to warp it pretty severely, and will damage the components that are soldered on there. Reportedly, segmented-display solum tags work well. From 532ca354e2b389302f418c876ff76ba7898d5306 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Sun, 22 Jan 2023 00:07:24 +0100 Subject: [PATCH 5/5] the ESP32 can now tell the AP data isn't available --- ap_fw/main.c | 78 ++++++++++++++++++++++++++++++++++++++++---- tag_fw/syncedproto.c | 56 ++++++++++++++----------------- 2 files changed, 95 insertions(+), 39 deletions(-) diff --git a/ap_fw/main.c b/ap_fw/main.c index ee24df17..2a950f94 100644 --- a/ap_fw/main.c +++ b/ap_fw/main.c @@ -49,6 +49,7 @@ struct MacFrameBcast { #define PKT_BLOCK_PART 0xE8 #define PKT_XFER_COMPLETE 0xEA #define PKT_XFER_COMPLETE_ACK 0xEB +#define PKT_CANCEL_XFER 0xEC struct AvailDataReq { uint8_t checksum; @@ -113,7 +114,6 @@ struct blockRequest { struct blockRequestAck { uint8_t checksum; uint16_t pleaseWaitMs; - uint8_t cancelXfer; } __packed; struct espPendingData { @@ -169,7 +169,9 @@ uint32_t __xdata nextBlockAttempt = 0; // reference time for when uint8_t seq = 0; // holds current sequence number for transmission uint8_t __xdata blockbuffer[BLOCK_XFER_BUFFER_SIZE]; // block transfer buffer uint8_t lastAckMac[8] = {0}; + void sendXferCompleteAck(uint8_t *dst); +void sendCancelXfer(uint8_t *dst); // tools void addCRC(void *p, uint8_t len) { @@ -231,7 +233,9 @@ uint8_t __xdata getPacketType(void *__xdata buffer) { int8_t findSlotForMac(const uint8_t *mac) { for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) { if (u64_isEq((uint64_t __xdata *)mac, (uint64_t __xdata *)&(pendingDataArr[c].targetMac))) { // this costs 1 sloc :( - return c; + if (pendingDataArr[c].attemptsLeft != 0) { + return c; + } } } return -1; @@ -244,10 +248,27 @@ int8_t findFreeSlot() { } return -1; } +int8_t findSlotForVer(const uint8_t *ver) { + for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) { + // if (u64_isEq((uint64_t __xdata *)ver, (uint64_t __xdata *)&(pendingDataArr[c].availdatainfo.dataVer))) { + if (memcmp(ver, ((uint8_t __xdata *)&(pendingDataArr[c].availdatainfo.dataVer)), 8) == 0) { + if (pendingDataArr[c].attemptsLeft != 0) return c; + } + } + return -1; +} +void deleteAllPendingDataForVer(const uint8_t *ver) { + int8_t slot = -1; + do { + slot = findSlotForVer(ver); + if (slot != -1) pendingDataArr[slot].attemptsLeft = 0; + } while (slot != -1); +} // processing serial data #define ZBS_RX_WAIT_HEADER 0 #define ZBS_RX_WAIT_SDA 1 +#define ZBS_RX_WAIT_CANCEL 2 void processSerial(uint8_t lastchar) { // uartTx(lastchar); echo switch (RXState) { @@ -263,6 +284,12 @@ void processSerial(uint8_t lastchar) { serialbufferp = serialbuffer; break; } + if (strncmp(cmdbuffer, "CXD>", 4) == 0) { + RXState = ZBS_RX_WAIT_CANCEL; + bytesRemain = sizeof(struct pendingData); + serialbufferp = serialbuffer; + break; + } if (strncmp(cmdbuffer, "VER?", 4) == 0) { pr("VER>%04X\n", version); } @@ -294,6 +321,22 @@ void processSerial(uint8_t lastchar) { RXState = ZBS_RX_WAIT_HEADER; } break; + case ZBS_RX_WAIT_CANCEL: + *serialbufferp = lastchar; + serialbufferp++; + bytesRemain--; + if (bytesRemain == 0) { + if (checkCRC(serialbuffer, sizeof(struct pendingData))) { + struct pendingData *pd = (struct pendingData *)serialbuffer; + deleteAllPendingDataForVer((uint8_t *)&pd->availdatainfo.dataVer); + pr("ACK>\n"); + } else { + pr("NOK>\n"); + } + + RXState = ZBS_RX_WAIT_HEADER; + } + break; } } @@ -348,7 +391,16 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) { struct MacFrameNormal *__xdata rxHeader = (struct MacFrameNormal *)buffer; struct blockRequest *__xdata blockReq = (struct blockRequest *)(buffer + sizeof(struct MacFrameNormal) + 1); if (!checkCRC(blockReq, sizeof(struct blockRequest))) return; - // todo: actually do something with the block request + + + // check if we have data for this mac + if(findSlotForMac(rxHeader->src)==-1){ + // no data for this mac, politely tell it to fuck off + sendCancelXfer(rxHeader->src); + return; + } + + bool __xdata requestDataDownload = false; if ((blockReq->blockId != requestedData.blockId) || (!u64_isEq((const uint64_t __xdata *)&blockReq->ver, (const uint64_t __xdata *)&requestedData.ver))) { // requested block isn't already in the buffer @@ -390,8 +442,6 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) { } blockStartTimer = timerGet() + blockRequestAck->pleaseWaitMs * TIMER_TICKS_PER_MS; - blockRequestAck->cancelXfer = 0; - memcpy(txHeader->src, mSelfMac, 8); memcpy(txHeader->dst, rxHeader->src, 8); @@ -468,10 +518,9 @@ void processAvailDataReq(uint8_t *buffer) { txHeader->seq = seq++; addCRC(availDataInfo, sizeof(struct AvailDataInfo)); radioTx(radiotxbuffer); - memset(lastAckMac, 0, 8); // reset lastAckMac, so we can record if we've received exactly one ack packet + memset(lastAckMac, 0, 8); // reset lastAckMac, so we can record if we've received exactly one ack packet espNotifyAvailDataReq(availDataReq, rxHeader->src); } - void processXferComplete(uint8_t *buffer) { struct MacFrameNormal *rxHeader = (struct MacFrameNormal *)buffer; sendXferCompleteAck(rxHeader->src); @@ -530,6 +579,21 @@ void sendXferCompleteAck(uint8_t *dst) { frameHeader->pan = dstPan; radioTx(radiotxbuffer); } +void sendCancelXfer(uint8_t *dst) { + struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1); + memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal)); + radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_CANCEL_XFER; + radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING; + memcpy(frameHeader->src, mSelfMac, 8); + memcpy(frameHeader->dst, dst, 8); + frameHeader->fcs.frameType = 1; + frameHeader->fcs.panIdCompressed = 1; + frameHeader->fcs.destAddrType = 3; + frameHeader->fcs.srcAddrType = 3; + frameHeader->seq = seq++; + frameHeader->pan = dstPan; + radioTx(radiotxbuffer); +} // main loop void main(void) { diff --git a/tag_fw/syncedproto.c b/tag_fw/syncedproto.c index cb390b2d..42e28648 100644 --- a/tag_fw/syncedproto.c +++ b/tag_fw/syncedproto.c @@ -49,8 +49,7 @@ struct MacFrameBcast { uint8_t src[8]; } __packed; -#define PKT_TIMING_REQ 0xE0 -#define PKT_TIMING_RESPONSE 0xE1 + #define PKT_AVAIL_DATA_REQ 0xE5 #define PKT_AVAIL_DATA_INFO 0xE6 #define PKT_BLOCK_PARTIAL_REQUEST 0xE7 @@ -59,17 +58,7 @@ struct MacFrameBcast { #define PKT_BLOCK_PART 0xE8 #define PKT_XFER_COMPLETE 0xEA #define PKT_XFER_COMPLETE_ACK 0xEB -#define PKT_SYNC_BURST 0xEF - -struct timingResponse { - uint8_t checksum; - uint32_t burstInterval; // time between burst-start - uint8_t burstLength; // in packets; due to use of sequence field, limited to a 256-packet burst - uint16_t burstLengthMs; // burst length in ms - uint32_t timerValue; // current timer value (used to sync up other RC timers/oscillators) - uint32_t burstIntervalRemaining; // time until the next sync burst - uint8_t dataAvailable; -} __packed; +#define PKT_CANCEL_XFER 0xEC struct AvailDataReq { uint8_t checksum; @@ -129,7 +118,6 @@ struct blockRequest { struct blockRequestAck { uint8_t checksum; uint16_t pleaseWaitMs; - uint8_t cancelXfer; } __packed; #define TIMER_TICKS_PER_MS 1333UL @@ -254,7 +242,7 @@ void initAfterWake() { initRadio(); } void doSleep(uint32_t __xdata t) { - pr("s=%lu\n ", t/1000); + pr("s=%lu\n ", t / 1000); powerPortsDownForSleep(); // sleepy sleepForMsec(t); @@ -327,16 +315,16 @@ struct AvailDataInfo *__xdata getAvailDataInfo() { dataReqLastAttempt = DATA_REQ_MAX_ATTEMPTS; return NULL; } -void processBlockPart(struct blockPart *bp) { +bool processBlockPart(struct blockPart *bp) { uint16_t __xdata start = bp->blockPart * BLOCK_PART_DATA_SIZE; uint16_t __xdata size = BLOCK_PART_DATA_SIZE; // validate if it's okay to copy data if (bp->blockId != curBlock.blockId) { // pr("got a packet for block %02X\n", bp->blockId); - return; + return false; } - if (start >= (sizeof(blockXferBuffer) - 1)) return; - if (bp->blockPart > BLOCK_MAX_PARTS) return; + if (start >= (sizeof(blockXferBuffer) - 1)) return false; + if (bp->blockPart > BLOCK_MAX_PARTS) return false; if ((start + size) > sizeof(blockXferBuffer)) { size = sizeof(blockXferBuffer) - start; } @@ -345,6 +333,9 @@ void processBlockPart(struct blockPart *bp) { xMemCopy((void *)(blockXferBuffer + start), (const void *)bp->data, size); // we don't need this block anymore, set bit to 0 so we don't request it again curBlock.requestedParts[bp->blockPart / 8] &= ~(1 << (bp->blockPart % 8)); + return true; + } else { + return false; } } bool blockRxLoop(uint32_t timeout) { @@ -356,9 +347,8 @@ bool blockRxLoop(uint32_t timeout) { int8_t __xdata ret = commsRxUnencrypted(inBuffer); if (ret > 1) { if (getPacketType(inBuffer) == PKT_BLOCK_PART) { - success = true; struct blockPart *bp = (struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1); - processBlockPart(bp); + success = processBlockPart(bp); } } } @@ -369,7 +359,6 @@ bool blockRxLoop(uint32_t timeout) { struct blockRequestAck *__xdata continueToRX() { struct blockRequestAck *ack = (struct blockRequestAck *)(inBuffer + sizeof(struct MacFrameNormal) + 1); ack->pleaseWaitMs = 0; - ack->cancelXfer = 0; return ack; } void sendBlockRequest() { @@ -421,6 +410,8 @@ struct blockRequestAck *__xdata performBlockRequest() { // processBlockPart((struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1)); return continueToRX(); break; + case PKT_CANCEL_XFER: + return NULL; default: pr("pkt w/type %02X\n", getPacketType(inBuffer)); break; @@ -681,9 +672,9 @@ void doDataDownload(struct AvailDataInfo *__xdata avail) { // do transfer! uint8_t __xdata blockRequestAttempt = 0; + uint8_t __xdata blockValidateAttempt = 0; while (!curXferComplete) { // this while loop loops until the transfer has been completed, or we get tired for other reasons - blockRequestAttempt = 0; startdownload:; #ifdef DEBUGBLOCKS pr("REQ %d[", curBlock.blockId); @@ -703,15 +694,10 @@ void doDataDownload(struct AvailDataInfo *__xdata avail) { // DO BLOCK REQUEST - request a block, get an ack with timing info (hopefully) struct blockRequestAck *__xdata ack = performBlockRequest(); if (ack == NULL) { - pr("no reply on gblockrequest\n"); - // didn't get an ack :( we'll probably try again later - return; - } else if (ack->cancelXfer == 1) { - // we were asked to cancel the transfer by the AP - pr("transfer cancelled by AP\n"); + pr("Cancelled request\n"); return; } else { - // got an ack + // got an ack! } // SLEEP - until the AP is ready with the data @@ -764,7 +750,8 @@ void doDataDownload(struct AvailDataInfo *__xdata avail) { if (blockComplete) { if (validateBlockData()) { // checked and found okay - requestPartialBlock = false; + requestPartialBlock = false; // next block is going to be requested from the ESP32 by the AP + blockValidateAttempt = 0; switch (curBlock.type) { case DATATYPE_IMG: case DATATYPE_IMGRAW: @@ -776,6 +763,11 @@ void doDataDownload(struct AvailDataInfo *__xdata avail) { } } else { // block checked, but failed validation. Mark all parts for this block as 'request' + blockValidateAttempt++; + if (blockValidateAttempt > 5) { + pr("bailing on download, 0 blockparts rx'd\n"); + return; + } for (uint8_t c = 0; c < partsThisBlock; c++) { curBlock.requestedParts[c / 8] |= (1 << (c % 8)); } @@ -905,6 +897,6 @@ void mainProtocolLoop(void) { } else { } } - doSleep(getNextSleep()*1000UL); + doSleep(getNextSleep() * 1000UL); } } \ No newline at end of file