From 5d013b42733d11e4327868626afbf969f0c41bd5 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Thu, 30 Jan 2020 10:28:01 +0100 Subject: [PATCH] Implemented basic home screen Refactored settings and global state to reduce overhead Consolidated configuration into class constants (I really didn't like all the shouting in the code :-)) --- doc/DeskControl UI mockup - LCD version.psd | Bin 98492 -> 147732 bytes doc/testscript.md | 21 + src/fonts/AdafruitGFXFontTrim.exe | Bin 0 -> 9728 bytes src/fonts/AdafruitGFXFontsLib.dll | Bin 0 -> 10752 bytes src/fonts/FreeSansBold18pt7b.h | 480 ++++++++++++++++++++ src/fonts/FreeSansBold18pt7b.trimmed.h | 181 ++++++++ src/fonts/trim.bat | 7 + src/include/colors.h | 23 - src/include/config.h | 102 +++++ src/include/display.h | 15 - src/include/heightsensor.h | 6 - src/lib/limits.h | 10 - src/lib/motor.cpp | 22 + src/lib/motor.h | 16 + src/lib/persist.cpp | 41 -- src/lib/persist.h | 49 -- src/lib/screen.h | 15 +- src/lib/screen/home.cpp | 115 ++++- src/lib/screen/home.h | 9 + src/lib/screen/move.cpp | 48 ++ src/lib/screen/move.h | 25 + src/lib/settings.cpp | 52 +++ src/lib/settings.h | 25 + src/lib/state.cpp | 15 +- src/lib/state.h | 20 +- src/lib/vl53l0x.cpp | 4 +- src/lib/vl53l0x.h | 2 +- src/main.cpp | 164 +++++-- 28 files changed, 1240 insertions(+), 227 deletions(-) create mode 100644 doc/testscript.md create mode 100644 src/fonts/AdafruitGFXFontTrim.exe create mode 100644 src/fonts/AdafruitGFXFontsLib.dll create mode 100644 src/fonts/FreeSansBold18pt7b.h create mode 100644 src/fonts/FreeSansBold18pt7b.trimmed.h create mode 100644 src/fonts/trim.bat delete mode 100644 src/include/colors.h create mode 100644 src/include/config.h delete mode 100644 src/include/display.h delete mode 100644 src/include/heightsensor.h delete mode 100644 src/lib/limits.h create mode 100644 src/lib/motor.cpp create mode 100644 src/lib/motor.h delete mode 100644 src/lib/persist.cpp delete mode 100644 src/lib/persist.h create mode 100644 src/lib/screen/move.cpp create mode 100644 src/lib/screen/move.h create mode 100644 src/lib/settings.cpp create mode 100644 src/lib/settings.h diff --git a/doc/DeskControl UI mockup - LCD version.psd b/doc/DeskControl UI mockup - LCD version.psd index 45aac42935d60b73ca08f8fbb86bfc48588cd8d9..6071a0add2095aafd5ecf9fa6cb165ba43288f30 100644 GIT binary patch literal 147732 zcmeHQ2VfLM_n*5o5<(#K&Jj9F?o#fO(38+21OfpO5Huv00!c33T?j=)?AKAx#6pJY$9P;`7;oDrc&Aj*K&6_uG-^`ocjZH{N zWf~^_`7oy0&$m_#=k&KII5r_E+1Hm}wC$;x%Y1qanAlUxnD&Ve7|$EP^V(Hxdg;wk z?fU(8lT``a`H!oo!qiDOqY`}Dr~kf*^pe2 zXwJ@=wA^Y=T|RYY`trr;aT#G_MhA^tI(%tfL7v&3rdyhqn{OMwbW~VchGn67IL;|q zAEx6V_Qj*Z;weLSMRJNR(PB00jNwtk(hU(2I+H0pBHCy&MO> zG!2i8(Mf+{ql0iZ(wdPue8#v5QgOH%6_#zc7Yx_yi;9ZEi=x6U)+~KQTwENHiHsZu zh+(#6`S!G>!}4wYL?SYsab{b(HK)LyW69SM-L!?4Li?z&Fe=FLC)F#jz)?`XEu7ae zJl&F~Uz%2+j|eyD9h8AM5;7T{Xf>zVE!LS9OYUf~<|)}0yTz7mDbOXRnskF_=j3Nt ziflth>fOPejf|6{Do$0_#r4l97|StVL9ukP?lNdJ?ii=)6TqUT*2lHAY6dqb&*w zt+~9(Gt%|uTyvf|-)=*DM7Xv`M*8qfi#0FJK02+SAU7vHjhbA)BtJuH$O5@#M(UlQ z=bLM^a&oeRagR!rm#6ond^Y>|CD2Pr&pl$=W-co*>t~p4mO^W~dHfQ{!Kv($N+u@{ zPs+F1)AG~JN#jO?o$#ETjNvik62`?EVvRA0(UIfhBO+qQ86qc4Fq&c_V@weyQxw-M zS5#M>K;Ae@dLfBkBAWrSu?dk;i4msQn1rYaL^dHhK4QXz$OuD%DIzv8B2p%+Ky5&F znl%TeC@oh3LtJ}9-UEpS#Z1bvp`$L7wNdQ(Tqje_*SMz2%9@+Qb+jPOYBQ7MM}^6v zSK0xQ65;R`9G;d=dN(?qEQ2{?q~0af$(B1>PQylPUb-S}1#08;i?Yr6O<5L^6o+b@ zh|Q8|FG{nT5B)#j@I z+$<%eWx1wOW6?d-TeIZko0l~;EzdkU1*X|kx|2<(eR6{%raj^Fba(vo+rQm7x!7vr zqC3vEa+fLTftw`GLz{>#<*IZ_9ciemo;$Lb3g;c;2EAk8&_I(N>UBO(JV@$%k{4;I zg?J@XRa~mLypn)dz^Zns;_^xYUIDAxrHacd33vspYL_Z5uO#3Vu&Q0ExV(~pSHP-v zsp9fV0$u^D+NFxiD+zc7tZJ7kF0Ulu6|kyZs<^z8fLFk(cB$g>N&;R1tJ^4AH_B29f^Jfl47%`F2rM}93m;-ZZ_u^=1~Tn^J89?4#&0}H#KJ|K_ukZ z35T*pnr16KL@{&x6*H04h9$Bzmd`BMbS#IZuoxD}jLbmr5+OYIV#!Q3+Y1Y(EnJk2 zlE@F_(22=&CX3HA zk02V_Vc0n((Ow~mY5bX98}hplWMY9mU#=WW%w1@8T+GO_B|9z>t@(-0i+sEDV#>l? znm;3!zBo$~1g{=rcpfMb-0;V?XJqJf8J5C@V=eoML;g3> zn%^KTA-5r1f;D62tbF@~elv2Z%^iOUxfwc7=`(D(cAlQHGGKkB;!`>Ogzx+so2!?TroYxnm%?8QQ&{=(yf+)*?8nI zg)}!OKT8&02xZIwPYD*g-IAAU$Te>~?t7oZlUjLOmE(;=!y0HL$g?AC)uRGs++;J!a&uXJl{)zpaYDRUV zj?|OGw&E#L@7In+QP`w*=}l*hzYl@K@^XDhMxu0hwq`$sh>ibA9aOq%kmdsG0P=(+ zF+?!=g}G!+sp|fCuRwl=&CMF5+ar+2*`zMy&{HJ3={ZYq^aqjU84h*l*Zwv<({8q1 zo;!s)tj47rKc0u=_SgdWMbbD$SpivAOJRXalD~yNcP-oE@u@^ze2^M6+N2fQEfdZ8 zW-C3!j08pxKuf?uA~2;;PLg<|`A^s12QLATZd6jEknmkol5JV?%}j*xPs_E>Ov`eC z3Q0Eu4^J^B*(S|Qo+6vYR&ufnaA3B@x;#ENCriF-wG}HrNlu0U0y4~*X@%TO2QD#N z?H<4{my=z=TQAJwPoOG<8!FOInCQqL8Bew3ldJ>nmIAl|HnY+W2IazFHb@O#$PH|R z^wwzhYa1@#X^ zuY;nerP~UUQzwu!!SGP5j-HMcDe*npo1mk$Nk63+!0c^P#6 z5zcR0Qb>yLGX>|jEXgq!;d~d)`{x$s<=~v`ZpS=x8aksd$*jNKoSuzy1I~l3GiTs# z>N}FLR>4_{^M#6YJ2VimEYVW1jJJtyaQYA(UTPAnn`AD^HQVjOQt+;pG;0Rlq>@*V zmcNWKvCf3W+BbOem?w+7CNmXqO`THPiuq@z^ET6T-r-DhVmi|-&%ie09_RB9XWGI? z8QXj_V_ko8ruBas-sDY;ZTebKpU$K&ikH0PnA5{appL(00UVHxpD4-;r}7+6ZR^H~ zU06q2osNgX3#~dEUX^3k4RcrGb4FCKrQqYz8Rks0)r{A5=q`tr!Bs+Y=V#>b_toU& zyF6R2pm2_83JP>8>NQR}JXg;;UKP&Tech4yoY=v9JG5p#S3ZdxjicSB1YQnbpRw6} z4vBh?bL8{A%T9YQF15CtEFC2$&X}o7FSIV9bLmd#2hXT2>&UvX-mD)R%!Xl@5yM8X zv1~lMj7?)R*&McjrL%07hp|NwyOynHx3D|eKiGrpG4>RDj;&*_ve(&W_AdL7eZszE zJK1jbGuzJ&uw$&0on1HL;q}n(>+`ni-lonyWQgntV;6 z=332-nmaZ3X&%$8)vVL}OY^qoL(S)!Z!|w@e$yP$oYqupeYC;aj@n+@fm*#bRvWLK ztevTyug%h4qg}4ONqdj>5$!YDSG1e7A8NnU?$++t9@CclFrU^woqhWH4D*TgN%WcK zGuJ26$Le#P&mBGw`8?zEs?R$l{XX{F?RUWM ztiQj1Xa7O|G5(YM=lC!3U*Uh3{}cYN`G4U5jsGA1E1hxwt5I8(=T3|-t zvcP)-p9_2^@aw<>fi*#)L4$%u1Qp`ga`EF{$ICj(2w4(DAE|rJdS$itcoIr=^|Nbb7DT{?0y~2X;>C zoZI={&aZdg)494!uP*Uj%w2Bp@@kiFyHs@T(RFNBbJsh%zSebj*XnM%ZsWTx?pEAw zbGN7Nbhk+kJo$r)KlAYXwMlvSM+?g=Z>D`y?XbW z*sGw|qrJBGI^Mf;@3FlX^?soD`@N6!>Ck6%pPW7q^x4|ysIHSPUYDzTME8-dv~SP8 zllm6+UEBAozBOTk!e)it5cXQw-hM&-V*6$Gd!XO8ex?2U^iS@8ZU6QCe;N=tAa+3Z zfQJWsI-p|Upn0##@csjCH0MlhyR9=}1ifn5$zRkNG~fV{A(7U9q3V`Nbu~t%`d$ zu4=exxPAER!%vPFHe&IJmq#2PIcTJL%c*B)Cd|Bd=FhVR&nld??eeylUwQeu%PVG&n|lbGpyTne*1% zfVr2?ePM3-yz%oMoOj?#Nx>}z`>z>( z&4bsJS|?jyuxV`bZEx8-!kOApIJofU!rzvRUb1FMWzpqDZ!8U6n!j|%vZ2dvUv_Bu zgyqk#@LiF7pLqRCtAkcAUj6SI3^&|& zL)ne9Z+z#bUN>ET(;qiaym|dCA-7m>`To`sw?1{7?`=7^eRX@(?T_AGe@Dh0U)*W9 z^PxLy?@GVxi@PK4e&lZ9p3Hl`y4Q5?lmGDlNB%$d6ptx>>AukWmf!cs{mJ)ldO-KU z9S@W}c-4cSKNS7YQx69{T=?+*N2WaT)}#F&{l}xVk7Yl$driWc*B z^IzQm(u|irS{Ji!-OD{+zIVNLec}2euUz@c&JE)>y!Gm^SJ%GQ`L#R$$^KdR&!hjk z`d@oqpZxl^H)7v-_00iqKEAQT#yd7?HZ9v!x;cCE{Ye-Zn|<}XcOZrWkovGL!=e{cNC^wp-XW4?ZCXWY(rz8U$= z``?cF_QUVSefP<(q+MU`p1S*+J+t=w@cotF@Bbm=hr>VS{&?!AB|p{teElzezud96 z-QI`x_1yQ&uS0%)ZNF*%d%unQZO8AazyJJ4`X9&swEbCo;HHCZ4nB0K&!KgPBM-lO zWWtf1N9P?qaID~1&GB1Kgr0c3WN^uwC&!-LQF=w`fm7B~!s)xtbUX86S#;U9@|5y@ zXYwgjQ1c8q=Iy#(zKKz2C#p5Oal7qm_#0{sACd! zOo9$^j)RMHL_>kfKXpu^j!D!pi8>}x$0X{QL>-f;W0JpPOd_0M!Tj~zS0yH6q*1~` z_!Y~z9-fIt_?g|rf?Ks}71$~`Ffcf@bx`ZjPHlsO+ji>Op+lz*9lC}F^FQezUbycy zZCbZ(6Vj$#NJzVmAt50hX&2H_L%w4{X5P0aA8!m^@eW}0|wleTKe0mbepmBGq2w`aNPYfGsbUw z)^6(Z?X2I;r{B0~`*($ZoS87_f#+_1^TCgH{aN<>#*cR&D8D>&$t@4Pu<4UM2hYY# zoSjv4>%%W@{`C7p6`@S41-HJOrvQIHBWIy+M0;Owa7|bTzsS|a9f^YvXM9r{)qmk{ z)*J3iOzo6zi#|2LpBM-jXx#QKn0U5J#`sw#yZLtq3ypIy$jO566ASj?Wrngb?AN6J z51M|uFRA|n&)t_aU`YS_k}gZ?f8Pv`2?7&#dI|rH6aM)zmwml=$gJ8wuDfJghu=aj z*|RR=(Du3W-#>X|PR;Xr^_%yF?_DQR?E7%Jk zR$n#Y(qEq~w;URM>aL$&fA7a0+i&}+#}U(*N59!nF=s?wpVHA!RZp9h^~{|6i_@3P zS-kJZCtkfK>^i?TQ?{M>c*)9X;h7`4r7yp^@SQyYU4E>-^w!P0Y`+NX*)2bPv2v)u z_E&89YV}Rk!xPsj51Z8*94#DjB={akIDpL*=IZ>N5B zLpm@2pu}E%Yi-P!uLQQO|8ti7@890~&ZL_A`zH;YHl+Xa zdfq;>ZxW9);{-PG=)Z3*d;hNXNdsCBB~^sxedpd#?cUqRTce$$6=s+Q_MU3q_V}WI zZwfVxXuHpL@5T-BbKeW9S@c@uHG7NisDAd6^i6Ny{r$f0>Q`+{iX4~J>cFW_2VU_> z#%*if56z1IxGrfkF zZaLPvXmH3=k6G(KKKxe3(NX=@?zGgN{PX5#_Y~ECdE~l>%f?JRv$TBc_LBO`Pu6wa z7*RLvr(VH-9$Rzx=c8YL`{Ty?Ti1>GdF-G6=<>q5Gl$<3a^%(lccgYLSvWm*>7gV0 zA76HR!kG1C)vF#aySCOc>cMZSpB%7Y(}WSnUdg^{t#;Ll+!HhR{PD)}{LUffu2r26 z{QQt9Z$<0wdkdHSa@}Lwr>!1%{JNfFZk+e%r}uWd?bA#CWxcnfwe&K9eU)C8G@>^ES{bkGDcc(qH$!Fe}_cpHH@XJld-+lOxsL-zm-to(VFUqd} z)U+1EBM`@ZaeSkq~B`X_4;P*HOE#ywEK}gn~quE z|E_H4-mLXmhwBC$ex-fsNc(~TyQ2KfJKl`??#JZlk4m1J@ban6rN15;HS9>`@5vkA zeyLl*K-(C(iEHm%Qh zo2!0Jc<1taD>trx#$31P)9wHMFfRY?lS_;b%?W<(+gEqovgVqbSG}^e?5Za$FAB{6 z^-GTRGT4v)bJ(^m>7%}19;f|R;r-8lzww9fDN2*wod!DV%_h?f$MHzrzVw_{(Aa%+v}#_FN@k< z{jYELY`5>-ZXaf;TK~T)n3y*kCxAPHzxI)E6&F3uCYx#{Ql=fuODBN zyZJ=Odjk8cE~ak%m80g*3w1ncSXLu|n^l*7^0 z$L5``9%xC5U3+89x-sk4B!2$Gg1t{=1^-;~bN&6xKS;W4!AZ-Rdn#XRB_3b0)!(~& zP1_?YyY|2Dvd*(`0~{jkaq6dd18n!+H`8{%QE|t$gSIc9|H|XB>*v0>t^I}e|T%%RUa>C z8hpijTfZ9d`@`$APImJB{Odd3e0hGWf_dds-{g*X)1V$9ayj=+9q+WdJy8urf+f z4^3}pJv0NA_0Vb@G{UtG+ME{cu>Oi9A{{k(evn&6$dQ(R26+5NlFRKrB5%+%*iv;8twS+@0v9# z>G~fyADK(7q|Aid^dFZt*Ey_s;pJ%g6<_ZwN}B|vF-T-uEbWkydj16f9bdUQS}AcI zXoawmOwXo3>dVj-txV^Td;#On5#TdjBq`rqtQ91wCuofg~?5X#MQ*A^^W9c@BCYYA%b!cLt73;6;IvYMM#lPJn z>ZYXMXfYNFqg4ngTOsRw7(5*eo|)xhOUG3X3}C*5S%S51N_-&4`RHunz$dE?Lo@lg;dk zwY=e2lV`yCI~_`xz%}2D8#g^3WP`Q0K>ITQ5sfDg>}-fz%LHB+EtBA}WyVgw9wMMW zO&E5|#g<{~#g@^Q@vJ*|%iO_R<_>4ekOd0+fJ1oEGTM|#&dJPWso9M6y$yHvQi+D! zK~5h#|Hke3`)I5^{zKFJMHaM#@=nKQE}cTwAD#ym?k~0Lq$TY>aM=hF%O)t&Khc_# zk%3MpKLemlBOqzfIh$t1BW2RlWpqw?ldQ{O^B9ZIx5vwQ@%cP2Bn?meU=4hlEj2rh z-$w&y(rWws4D(XOGYLWC^YPs``i^0mJ%et_eclfW8aXm3P%mZVq}zi6DFG>14{NdF zj9&)oiJopS{BEjGn<2;;J(}QB(yVD&*0h4`8HM>yXlZRP(x@<`OJo%Aso7>TN^@of z>LsjH`?6d!&(;|@0I=`~6V~(Rm~CQ$$dtr`Bv7AfPqT7; za7`YcpWy+PR$(WY@nuJ|3kgb&&$OGZ%G7u~wK8d00iHXCqq)b5R{94C#uzNjej#r&>=jr%_i%rlSs0 z567tgLCfO8Y1Rx9vkZ;zPFj~y!_s33 z78|k@#6)eC)D^6q|31L0O&wv1IX}yuEp-Q-Bv^ft?J^5KC%G`!tcw80qFvyfxP>;s z+2MJG<<1SIl?{PxMI0Sau5Jsus*_5~siQAhsHeycKv!IYqm535BPEz@w&qjgDivQ& zb7JS@=JIEYByw^Ep=?}LNNW5~e|cJNp;@+6;c;=15hh)@DK5$or3;UWGsT)5x-7N2 zCpomdxkz!v=As)@Qhufd2_koTo?@Gp?^HE?YPO|_k`+mG$psS;D+-3=$z3BdQjzG8 zfNVn*b$3==*(DujTCA*+^aQ0y&zT^9Y>fKVBr`ptHPb@XnUI^7Wph+f6c6sX;{8<@ z4GHOI*~}BH;#Wu0a=E>jos(hDw#jOM55~d*Tg@3xapG|_8+{L%w>2c0Rv_NXWZq|6 z@%%}C*0>z2I3kjCPoJ2blbeCTl6d=(?>{i;L_uk2=pKoO6?q|&=IN zC7E_S$Ht45X_zj)B41>m?6@rBjLygc~ z=ppnGG=^F{)X+`WO9(V{5=II04S|N%hBk(_hEPMDq2BO>;Yov^A>NQ^7;l(pNHU}w zZZzC%xYclvVXfg=!}Eq04eJcs4SyI87!Daq4CRKihBb!A5vA?Rt{lSVvz6>pj8ulR z5q*972C`=aKb=A53El*8r0c5dDYPq92r!e`WhR>tTysK`NiE|Z}sc{qwMg@|yM*H z4q9A@GqcT8>5JsK&bvOhq{2!$Kt-Y3}Yis^RLa!;ZVT z>bN~L;;8Fg-E@6(PI?Pm^yVFOkM21rbAMfN&(zCi#_3J#Jv!!k4{o`>38&m~GYI5L z>C{E9Rm+JgyTGyE3;t_b;ZmA!ayT+*VoFQ0r{T2&%2ef8#^J`w z(sB&-^mzO}XF1$pbglF>a`h!gSWgeyV<_sLjGJ%zVsec|XN0$j>=`?Yky9if@&R(; zw4`R|WID&Myncyk1vZ%$)m;Xa(`J#!MYzhkrP{4Ii_P|I#89%bl^IiVveN8@R`}XZ zUdA~Sl?oxT5md;}Doo3gM})X{C*VW% zif$=mXelw`i`-#_kVN`C zQ6VG_NV5+L$T`jy5eGPWycvNSi2KS;{Yk`>6?|#4)hS^~q?2;+RQA-uyoK3lVV6XqyY)XldTYe(x||1dGGNs6S(<3hQ$ejj8vJq9a}HGW zsmt>1*=Cf*)y|_o;*8fOfhJ9)+c2F=0Sn0o@l2oBxh3?I5rDFSY8s}4#TP*h!_CNy znWaM9m@v!KR4p1UMKn}}m}Rr^>T`1vZj6m0*Cf@M4jGFN%41?h%3J6WLrQH3kDGHY zIkFdm08tRyPts1T({e z^#<~r4EfRqq9QM6P*8MP6+=sIw6xlQuXK^t)*%q$zRI!t1+nsFi&!^#mE#Ar&INyC z>17-I&-;}xBp`L=OYxHnsp`rXb>)k? z^2Isfi!R|hn~wz7iEWn!0?tWQio?6kNQXy7#zY#i@+BrVnpVD;Ofix2k_36CTR!qR zSm}aKg|}#-lDhH*Q3_ffqON=)?Ne92*wvLU>dKc!mkWs@rKuKmu6%J{ zwyv&xksKR!<%{d0Ds|-xIVQApL0$QxSQM|Wd{I}vs4HLib9nBrf>76sdt4@>t`~P# z6m`8gJ-W(ccaCM@PIrT^>{Hi^%j;SjndVm4i;HU|)b-+`9OOs-9P6Rg_2TlAeCm2} zv$|efT`ztf*NdwwU+67TJuZlqFMA9w?|f;v@`V+nF4tmv8Qas+Tc_%A&hXeKE#AoF z?|jL0zVn5zeEBDb@aml}5#GM@WkI6&&KGmy4Y}{^+IOM7^F<30sViU9l`rZ$U(|QL z@J9x{SpC)Vr4$$aJ71!rq_HsOYEbJho`H)DUqpfN|a*$>YEbD zr%~UOD6WxF-;~H*Ts|~X-<0Tl@ud2uMDVu zP?|nc%k1P?i})I(MSKm?B4^9+FW>bSkz3v}zR7qS6Of3G^tHQST>9b#?)06zLZ0r_ z`;HfebZ>Q$?g!(pN0p%WlK$iLTWlM#orQFxMd|wB8-w_}k4yJHF5UZ_(xnw%8t zYPS>svw?I|`6p8g+@#y}!jSI6F47&_j>HD(l88M0we_)A90RGike9YxweO z@f(>`M-5#Uq}go5rDS#cBsIyM2L<+*Q6+38)fatyVMot>t_5 zsLDar2em(_c0jcQsvS`6fNBR+JD}PD)efk3K(zxGs~u2hu+&))RRR~X1TI#~q}Gch zuvVR!is~yAE^C6wF9ahQ0;(f2UI(7vD*RZePJ|{<@=xitrm*$ zzMczz` z!bxo3unJZu9Asre87mjQW0kB{IK)m173`F-n_nGfr9u@uDFB1j2uE0nP|dHZS+#JK z9T#fZ3E_KI$0~4Dg)4EZ!|{7|MmUCJHMV+eHQ0XOY1N=eX>_c1q*dZvPOHUv9m=CJ zYb6}_d;w)rxxfMRF>C}OV5`7ZhmE050^bPYXKc>geaLM*^%wBxgx!leR13dgBmP9{ zmEeoYr}|Y3d!_n_d19SwkWP8~kVo9eTv44Vowogq=y=Zi4S7Vv6W;HjNpw8r{Ruph zgr~d%z&qPW-a+J*H5q~6uQs9$rxYHH$k8>H5 zoJkhSa-I1nJm%G~W1xAGm*I}rTwV?IpKv_kRY9YuY&q{;@b(RjgtX`5cav~FekwPO zOA)dOVVzhgYsb2=zN`=H;`nuC;;#qm!P@g*2f!(R?PS~gJJV(sZK)NsTZ+b2Gxq3N z!Kb9CD72huGPjfqnjPb6k$76ryc|`DlT!jKs>87q$13SqkK;*P6Ly^-AYT(!R3m7^ zFt~oSUSM0QLF={}fz7I6K934JGAo%TbVr?_eN+aZge`Rf+grmlw`HQ}(2{yVTOw!U z-Sz-yvraG+E@<$H0Ztal!Qo>QjZbQP3urpG^ zF)j=+d0I+1&Kr=uT&lP}j?zVfl-umpd9hAA>;C%h{8*-)|N85{^TdgH=U@J*7D=^8 zszo{%?`dc1U;mvaJFsErU;dpZP8RI^%Rg|^2L4Yd{Gnd(m1vaN5y#H(l)53k9pBpF z*p9WuuMPcT^H%>mViiB^ zKw6Q2@ffFqG`bMecDSd#EC??<(ry!k9cP$dXbC(qF|nHYhVB)F+mH$(C82UEzw3^} z-8%{%4(h>6s$<&FB65^oE)&sqP>snEgiGibx&vH{i*wP#z0XcW;MJ%*0Tc^4h7`pfe$)vR8cwI~yQ zXEm6IIl<0iv?^bf;Ed)zerL6ql{tx7nL2(&a}XzyLh~ba!1@965T&@T;c3q6)1ZGE zY4zA@0b38aQ#d~*r8A6M1P>CE8juY?v=aoVN zR32&%X*4WTmNiL-K$0XM+Fa{IFx0lxqEvrnohYvcZQod4HQKQX8}a6j zSBbJnhBS{S=b@i@ubV=B@iC#gxKb!9{#^k7#V3Tb#nq0h5}efvXOM?_6rU7M7uWHt zdf=QyN}W)Ht*-b7p`y4H*U$u)>(iir8fo>|z$uOu#iww7N=j!!Rk0vcVk7#LLy*uR zN-3@q4iz&;4g(yt17#eT zmxTK~Hql!m&qVYV$@^#sL4FOzR>ys%H>etz-3w|5qWU z6323O6fG%WUyYP19M7^N=uM0rhRz*C>S16WrUY(hDtKxQAIDUH0_DJSL>|SGPf6#Z zu8;;*qXaR|d>Y3xY!rVziz7xQI3p~1qa2E4J9Ex(TrpNJ=Ild0MYKx^4jZLb3BO|R zo?0RNhEyt1j8u!T43_KwPp=kAaHQxo(O{6yQMYvBQouTi6K?61yl;>cPrBrgENYQT zk#nk>NS~xxg*KpCx~7(46zQ5uZE+H^rMfs_`DzXDBBsjqp|(9Cr7G)Q!j1!*Qe0D= zX-cUQk20*X9Vne-M|sMYD?u8`i(sj>Nk-J#6uqoO+sb-C<6P1YG3OA6FXg&KDFSqi zq;!(&8>Q4kGpS9%J*Qs+y*Z)d5upR2@)tK-B?N2b$Iab)2Y<6V-9@g*!gR z-1tR5N2k_MmB7U-fz_YUZ!O=eM^z50KB)acwF9ahQ0;(f2UI(t+5y!LsCGcL1F9Xk zSnYs1gLU!FZKyT-Uy#7+b@W@y_v%rVgQ^c|e^Bj!Y6nz1pxOb|4yblOwF9ahQ0;(f z2QF4Spw3|ZFU)eNb-Z{bu=-B=t>t_5sLDar2em(_c0jcQsvS`6fNBR+JD}PD)efk3 zK(zxGs~w?eh7|rS?e1hM9EwxLDoDkvhFW7o!_JnvQf8A*Icej+7P` zr_(1|EV(n66__Vj^6fILK{ps51i74x(K2t{)+`PPb3}$ENmf2 zC4tNmmcwjp0#fpsos%?pPcoS;17A7dD}$xr%)*AT87Lr&6|!8UTF;g6Oc2cjAsd^D zeG!OSa81=uVW}*c&2$&Sd@kQe50d2)93s_iK1pAeJFl>a7}Xde%F9OCc5F6&R=`Xs z-@+Dw!^x;-9*(Kt(axg5lTo5$(CMRek)8x9HfYPKM!8dsJ|9#g-Kj>M52_LFR6S~` zD1kX>&Ba`Pq=6PTiMQ@zG1Ut?6QP2lZrIU6h0H6uP1L$Mx^vOy`KSS}g*(4Zw2?c1 zP0~?lP(#fX`#l%2c*>#0Q(iIgl=C8<8mW=RQ=Ldb z$Ufm1DIKGvW3KtdF=!2bhCoAWLy)1ZA=J>x5N}8{j5ka)BpK2THyUm>+-kVTu*UGX z;R(Z&hP8%g4bK~1G^{ghH~e8ZU^rwbF_at58fpx+hB^a2(j@o^fkLnlB6Jse2z`W6 z!hE^D?8?Dx9$UelWiRTw>UtVlp}=56h@qXKy`iJvBlrrf1f4KKm?s>NMFU0$=mz47 z3y4@H(zuTHY|2|Es8k3nlbdhZ@SrtFA|7Nxb*_Lu>! z)}^*3?)oH%|A%e$*KCStGGxV*Z>r9gCzE02ckNt!Olt<4$P%DRCdtG^lI@6y49A@^ zQkR*QYco^dkWQj?c55LLj5-4Wm~;_zh!GF5FfIBxF(<+x9YG5OBPlB)itot+=_5b^ zdlTQsNU*UI^@upRyhwxNEW&Z-U@OwWS7fv!*XTGiInH7nXR(emM;TFI6;*W5k8;qD za?p=*(2p|0eb7gNajJP#48P(vk3!X9HXIyBM^IiguX?oX$AoiJuaA!Ad7M_X2~Ma! z8bZLHQ;Lq$4da3}8sw7*$~8JTGm7-cIZe&UE66pcq~$1mQ<6E{DI!9A${W`V@^_8m z4dvYNMeTCpC*+#*Gt5@HdFhQNSxX#pH_A}iV}6n6s?OU0h?q^ zGod=@7N!{K$OUEMB9Gw>7!#q3hAM&z;l)7L_|egDF%HFuaVSPijKpb-%wJ3#?*`6_ zH7-~Zt}I}OU}GJ%isiM6jnqXEAF)x=(ZNqF?*Xx5w{w=4SZK3b@+O$m?1fe{7iTQG zmw{I%R^n0|UN~F^*Pbmq10+1EivzexqaxZ2*#@-KX2>=MWudlIPf7fe^wHHRQEo#r z1!ODGEhJkZS`!!x49OyRe}SnW3nZJ0C=(HYwU8i9I`klY1ZhEdEEj)7IJzm-Ef#51 zoV-z59Ma@=q9f8^Qczf=qq|3Py@M$cOMoSzYVgL1M8C|>$;G8ZVdyBlv~>Aof#6_@*&q-IL}lWwl(^>${Y&IaXziv7257ADRG ztTc6hk5(Ecj(RQKJkXh`S9srHVD03QO$#iYJpH4zbmAlh-DvpTm+y#_I4u;7R5D*; zV>zs?{Lm`?+lF&$*az=XzhbUu}j^Jxl;Ov)7OZI~}f z#kLI7L}oSh6Dhf?Ip zg_f1lurAIyFHwF?)kvOjXjxeeYvfH?+_iMkM{$7Tx$P6s_PM;3?M=1`pO666COef_ zJ>GocX%q7t4qr*0ZdJ+#UCcQTNQ$e2`ti98hDj_(H6X?h>xaR1t9OB3_ zx@Iw-#&QhIk>k$721gEN1G$W_BUcD612<#vR^a(zE&)yojz;MkUJSpE(S^e=kZ%*@ ztcbDzKCiQ`Cko&*IJ#aq<5Lv6t~iUPk*ltoq6%Dalt_T?C;;?;!taG7$@MY{+k&&E zNr26&-o5e^k-xuoIodtV<}3B2L$9JBx< z4e#6Z9P|Jq6-fpy?*|yyc#q@I124*P=z$ZbITMAURRi9fI8IZbG;0`$H6d%y9UG%H zshpPU8x8fmwFWd66mdMspb?uZ>w&>xrwzb%m_CeMM4ESvOXWLl2z#+FX4waVhaeknVnkXMMxuX5+l+JonjXuk?U?QF*F^9GFS) zWhqKUV-EPVT(&g1>=>(oAKAAOJuJdXVUgU4lqtDF@DUI2xF_4x^q7Mn1jI-G@+=x} z)@UP5V0keF508tB1nc3ZxF|yu{KPmD<_Kk<8FoPYcV!G7gUAIWA1O{`Il#mWxo%Qu z9Z+HlO+2O~Iv95*PJoE{yeglgra>_gBJdeBvFLKy0Avb~F}4UaX(Vk(zfDJCluEb+v!lR<`=RzFLm^57(so;YqH61L_G^ErPs044`Mt1-Z z8&TZcU(ZOo%)<4WlEUTU|LH+?@}r z>mVL&2#<}4LrlZL0ZM4h>Nbt#u(}Q~qbWQlA`YfTN!-QiIzYMAl`H9`)#WWF8eM+d zQlsL0F@PEscLo`$56NaiZxlUAXE0ZdigSNe|93^jF+3H!D|fxAZOC7wgBp;AV8~rQ z=lI&_(c*gQ3CXs_SWg|ym>|%)YWzk1556tH`|oAhkSP3SE^*V37sb3(vxFyhZPw|wi_Fqp@ zai*#L_u}p`|G#SI)Vr3Rsr|QUNvZw!zo_9C>znY^dXWUys$at661aHfaQkOfY&FAo ziP6^TdGm5O$+Sbllj^2x;=^~c`r>1(y10^^Ef$K`NFuCbEWUnv?R0H&c)u#f!p}}; z{k~&$#U}t&?E$iCx@LUm8X%QVXW_d5Si;JRYgu^%k`>cguUh6CKOKbYnI!)jy{py$|b_lrXSC2w|0M93ZSI26w)fHDcn!k>5 zylTdV5a4Nk8UHh@C@uwj4ae~SU&Z0Z13Y~C^duSlG{jTM2&o=hEehN05D_m1Xap~! zjVH8I06gVDW2~xJV3oyhHHJ2F`t*?`079cOs!*{{4S0vmO@pe_97BuYmjiABuMWnp-q0} z%+GH;BpV`P*PwDW#pRGIevM*QO=q#FDfA|}^LKzEp;lolV?vX7(DeB55E=8^7GiP* zI3e81KBKW3GDUu_W|#s8|gYOh!;5srQ(R{r>t9s}MfN)OwY$R=SlcILi(S<*ZIPE1YH}!Y7Wr&u~?Vyb4w$lnZCrF<{Fw`kWmXs*qR7s)aMaJ}P|S z$om3U)yS)2RRU2zB7Et{`;r|NV8@YPDVzf3!@>?lUM2D>cs?DW)^(8A44se)i!JHJ1 zHh^)J!58d=aH0W>t30W-PrAV<+vQW_pK{Arw)#iVB$BVlrMOq-Z)Im1m=w>|5WCEj$U4h zloD*zua$Y9K~qQzPQV_N@wRYs^a(q~wdj~+N0fNm(RyFF*&TH}qmJdCh5nHaI!APe znRci?p*B^UpznPcnsg90t_<2E2=dSkVd1#i)S6VEX?neXInxYHC{NW)?0t;Y3Wsnj zt6=3qo$!QY7)0QTRL$rvmB2fd%Jjzpc$k%<_ml~>!jmq5<;ceXEUpyC66p95SgmRg zaAnBH_$#h1RnTM9)>dDW%Gy$=ISMlfA5f?fo>CC5Pt_#A}& z-Ak`vj0)?2f-yagkOraAosQ7NJDw+`-T;mUQhywK%SdF3WTaZ0!(|4c8sQ`NQqM9* zOeTN;5o(lx01?z-lRXb=HZC*}n5ZN`w-{oZI1u=eVUH0WFv^iqE_~{NhXjcP)!d&* z{ESVBzRz(dQIvBr-q6n8@*R*Ss7ha5%LgZAI?!aRIB$i;OS2? zO&2tnX0ks~*ar$ETA6~VCFIXEpis-SZK>O`i6GH-GH7+7`^SFHbaNDHQ_+Jb`bF65Mw!$G1eMVX>aYLd@m zj70-?V%t9*xW}PK*)qsh-Ec5C4IV3rAY1!BC> z;t1_Hj2cPuFQV1(n-!JhRcRRaNmbG*l(QQzD|mVpbgb?_>j%Wp{bZVM{QBeT>;s2%v}N4vY5Msz~f@x#N3s5 zEs41+;k}5tyP>r-=I$A-*_gWh1Bu~Oa%-w{TJMybcME!^fF?T5sF?SPU?#Qn+odV^< zCPc~|d6mek;Q4ffl7qey0x@^sRD#cH6XKC39&_hBAV#d9g!m_TLg_dnwrS!qckojU zZmLX(rAs9e9OAeVT(ybE+&LVkLK}fY%$37cfj=t69S$*f$g0}JW9}Yds!WKvHv>~) z!dxJDtKxL3AQShp5OX(SdXw|dW9}YdPMR<&>;~g11H{}-nCEtbag`^vHs;}7V3h5G zm^<3THDB54h`DnaldNT4mHCLdH^^6N6k_gNUP}DqCdAyOc0^2F%6H03smXG_Yr7y~ zEa7hkUzFEaKIw^^@7n%O8cG<_JEtzW!cdzMEmyr%!c=jsa_Tr@?p(*6Fo?NBOXYqb z_mpar65#7uaojsvJ%$>s^Qn;da zL(K-C=d-grBlJA~?(z+q2PZU$xhsGxHfU~&Ls)CYsSPZxev*%*}#T0h`Bou>NjZarkMMDE8u1z=DuRXgYyw{@BCoh2DYNn zn7b=J0;8Sp2K(2QbM~4#|ImY zxx1o4&YekQh<*>DW1}(m257|G44~bFBiE%SWA3i#(Cs*&Jr8ths1?n|++ES3S`(fJ zUN%C|acD5jlbxH5xw}$8%zX%TTlOGG40#f?;+l=QJ1Ep{Ko5ShGh*(%L(FeB=B~gz zi>~FsMBi>E=B~i3+@N`WKDgy&W;YviS76qVbwX1~sLjUQ9jM$6Am%O>*=)?+6%R4@ zA=HJln~Ax*Vj<=pN6j(6nV7pP7GmyPAtp2vb9co;%zb_xq#Db5-QuEc72%w37)UCdpH>0QiSiRpFB zU5VV{n7bQN%VO?LAvGFvZwjf=m^(*O?=j8YSNK@{0+tnG?ncDija5d(+@*RT=59pH z-H4dG@vIRscOzo%QXXRNM#S8Wh`AffjflAeTh2oi)`*xp@~e$!fPK`6NS=g;n7a`% zcjQ+YiTV*EV(wBNV(v!7+>u{tJO#>!jflBB@+y&6!Sm?|B?o;a1Y+*MsRW~)#9SbFtKxL3AQShp5OX(TdXw|d;~g11H{~onCEtbag`^vHs;}7V3h5Gm^<3THDB54h`DnaldNT4mHCLdH^^6N6k_gN zUP}DqM#S8uc0^2F%6H03smXG_Yr7y~Ea4;O&hZ<|Cq1D&aZ|RxlZFz8^vs^<{n?Wpk{&3T$3pPp=ZOC@&%fdehp&o3gC(bnu$FS zmyAEPfSDS^+!cW3$VbdQz7)p>F?R)A8S*jyimzKBU}cS_y{TpaYu_N|?m(ztpqWB3 zcf&})4Mog7enE->G53I!x&r#_3cUN@i zb`PM<1v)iUe6ulkS9GXWzq!D}dMZs1G?->qK(jG-R|<%^x2JB)Qb3~pEYRxFY|Pz3 zp>_d!@T>sD+&e{CVfgF?#2kB9#^K30dJ~2p;~mR* ztq}oN8&SWRwSCBNHICO$FLV}1fFtnY91|foeB--`a&>jM-b^S>{=aetG zr5?L+|>u8F&2By%5dt>?2DAQ89BqopbHZ*?Ps_vI?Q@-k=#WE>-@p)@8%Yn!dN!_vj-TBg<+@ltzE;knc~C#R0Ni(dpGR z`cKoUFH1N3Oj;_-xqrGb?PzpKwsy-9MfAc6<8!~++uF<4=O15Ajhw9=k|-ss-d^l2 zo)BFAxWYN|R-CiIB|dJk@M4+z=@s|u1DWe+M;ax|KhISL&b4ul+k4p0Ic^?F-xtV( z-W3I`%^=9q^F2!aQMxmY5(1a!G!E0+`fCsS7YH3aqQmz#vh-?6@*$m*i2ozcKsUIKQcaRKE}!-A+IExUDaZtblFaNnpGpBFlZ%4vCu4F_mAD zR5pzYqJ~vaSzrOIMw(iVnLn zxzJnOuWD_a-zbm8@DC=^4<7qDAD&bi=jR-s{d|$5`_;E+IG!8+P-#yV4uVCTnWi4; z5xQ^G(i<rm#O`|&dDY&jhvnuJ~Db7xlbItibfHOlz~5H z_=dK4s51&^Wzfd5_r9-=9Q(88d4QdZV@7<^xVKzitg^f8 zExW&0bR_J^l>7;fb6xhG%$21tOglwfzMtQ=;ER-}*B_~tjQ2br7-3gX8*=t@C#OyF z{dZdeThewl(5S9%+{VcDr1Em>PT98A?&mICyOW($vHIe%QcX^xX8jiTx>FC1rR*yW zD=LI8{@fI_);eFg=FF;o@jZVou-~=B?s&2+#_r@b5C2-P=f~CsWhPTL*Pr-|f5MM{ zD$@-7k&|9ZX53k-iMh)!^$z_`dxb_ViOS563u-4Xn_!i-Zbg|$HZF*q{mu&BJ3POw zq_;Xo89X_1XHBL|lWZ?luHIkfv*bOJyjLdrgGPZ#^|R9J65kJM=8b<5Q(x`y_Z>M4 zSJwW1IWd_=DW_r!-j#iOO4g!Hcaq$2%axPzX}g}Q zyM?vw@-@f49~Y5wHrl7Efh_fo{wzScQTyAE-)typA3xwmF?+b*eh&)jd4N;ik$yU|72HS^U8v~+h1nH1lBB~ zk!ui*j*CBZI`q}D3*33rrS=O#_g6dmUMZ1o7#tRMC^#xe^B~S|qmK%?r!!WH!qe_chmTCe#PX=CenIMapO1( zL+f>+>24e+<1FKUoK^Pj$a!#d$JruVj;k?4v~9-ifSCsPW#3CzfxM``G4s)cT$+Gv_SYRE|5pwzUKdP%oYa#}%QNFKMUqOIh6*)c`35LnOC1=7(##dX zSp$ZkOEg%43AU^gu9Cjl-8f0%WaY57j)^MhTP6hMfWfP4r`ST;p`EyBMJHT^u19(t z+f^0ZY06g#IJ}&8iqx+Rw^eBdlu$U8F)`bIb6geRa2kE$u5eEvG0tSbk<8Sb@EH)| z_8Wj*;GP3vYd3J!fRkIm!T)dC8YuuN0OBg~$}RV$22d}%fl|^h{u)I>xD5<vRLq`&xw_;dnZycs znrWsCbKn8!ig6!%)4L@Xq>kBk#m)7^Cxm#TmdN0mYhr3r>VZ@Xn%Kc_qKP`cnI`J| zj3y4&%ON`eB^=Ip7!lMHup(%ffe1Qq4Ku8S#u+FfCJ-|sm|?@gaw`@xBKVOVrxzg> zG7|V2V~{>KhJ;Z)-Ho7mV;LvN9V>94bWTEDlo31K7)zZP#v`~Se>XpYU&|K$#>UBf zP&qIoa~>a5jt?rwXH?FRk-WhNmE(iT@fnpf1hBt`4=M*Yfyx>D`Fv10KB!zLe*qs< zjt?rwXH?FBFNB0LC`GL^4WCgteVpVM!3}i~RDx;2RD4F|^Z;EPUKA#dub^^f(RT1^ z!EjU!rhzgQV0o`VVwHmmHDLJ$C|*k)7$&8eD}u8I3_+J@umTfoStlH*TzBInA15n^ zwRKDcDrZ7a4j8<;c0OAONDosqsGJF|Lf0cbj_s-n?li?!0uC>yolojlh8w3e14{Tf zl`%2fU~^m*;BXp!;;wK{ATiElz=6ua)SU1c5aRY5fL-9817T}7aMgg5TfkwJGijj| zfD{1r50d5bY5?`J8z@k@zebS|ZUe&|Xa-XD>BlX%oS4Oe$^n*rm2lf%g=JLEB96q5 zA0}1tLFJft8kf>F;p^a3%$|a|y5s|ugTxAWnnC3(0uR7Sjr-V}-YvNxUd*;D4zuHc z%1MTDGPqu5Uw%he82K_}ByaFQzIY&C zJVw3@0qn2gfqcPDAYTT5J`d!J2lCa)U%&(T;(>he82K{b3n8HlN>S@f!(-%2A18T5 za6=sgm0(&h6_1fGJwO+SH-L%bE6A5wv>m)!FdS8bX`oC6Sl%m;SiYb_4OqSbiq}#H zhDmAWir}mPL(nA}tiS|Y)(Hpl)!jJB!^z5FZ5CUOK-k(1Ts7e27I0X;Oj;-fAO%4EgJik98bH151`6csuTdm~+rV%K znt_yk`fG{TEO-AVL5D diff --git a/doc/testscript.md b/doc/testscript.md new file mode 100644 index 0000000..953e73e --- /dev/null +++ b/doc/testscript.md @@ -0,0 +1,21 @@ +# Testscript +As I really do not want to explain to my employer how I broke my desk, this document describes the intended behaviour of the software in various scenarios, focusing on the safety guards. + +## Startup + +1. Height sensor must report at least 3 values within 5mm of each other in 500 ms intervals to be considered stable. +1. If the EEPROM has not been initialized yet, the height offset setup screen must be displayed. +1. Otherwise, the home screen must be displayed. + + +## Home + + +## Moving + +1. If the height sensor does not report a valid value for 100ms the move must be aborted and the move error screen shown. + +### Move error screen + +1. This screen can only be closed if the height sensor is considered stable, as described in the startup sequence. +1. Pressing any button will close this screen and return to the Home screen. \ No newline at end of file diff --git a/src/fonts/AdafruitGFXFontTrim.exe b/src/fonts/AdafruitGFXFontTrim.exe new file mode 100644 index 0000000000000000000000000000000000000000..f9af26089c0a3a2dac9b096812b45e5a14aed22c GIT binary patch literal 9728 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' + {1144, 18, 26, 21, 2, -25}, // 0x3F '?' + {1203, 32, 31, 34, 1, -25}, // 0x40 '@' + {1327, 24, 26, 24, 0, -25}, // 0x41 'A' + {1405, 20, 26, 25, 3, -25}, // 0x42 'B' + {1470, 23, 26, 25, 1, -25}, // 0x43 'C' + {1545, 21, 26, 25, 3, -25}, // 0x44 'D' + {1614, 19, 26, 23, 3, -25}, // 0x45 'E' + {1676, 17, 26, 22, 3, -25}, // 0x46 'F' + {1732, 24, 26, 27, 1, -25}, // 0x47 'G' + {1810, 20, 26, 26, 3, -25}, // 0x48 'H' + {1875, 5, 26, 11, 3, -25}, // 0x49 'I' + {1892, 16, 26, 20, 1, -25}, // 0x4A 'J' + {1944, 22, 26, 25, 3, -25}, // 0x4B 'K' + {2016, 17, 26, 22, 3, -25}, // 0x4C 'L' + {2072, 24, 26, 30, 3, -25}, // 0x4D 'M' + {2150, 20, 26, 26, 3, -25}, // 0x4E 'N' + {2215, 25, 26, 27, 1, -25}, // 0x4F 'O' + {2297, 19, 26, 24, 3, -25}, // 0x50 'P' + {2359, 25, 27, 27, 1, -25}, // 0x51 'Q' + {2444, 21, 26, 25, 3, -25}, // 0x52 'R' + {2513, 20, 26, 24, 2, -25}, // 0x53 'S' + {2578, 19, 26, 23, 2, -25}, // 0x54 'T' + {2640, 20, 26, 26, 3, -25}, // 0x55 'U' + {2705, 22, 26, 23, 1, -25}, // 0x56 'V' + {2777, 32, 26, 34, 1, -25}, // 0x57 'W' + {2881, 22, 26, 24, 1, -25}, // 0x58 'X' + {2953, 21, 26, 22, 1, -25}, // 0x59 'Y' + {3022, 19, 26, 21, 1, -25}, // 0x5A 'Z' + {3084, 8, 33, 12, 2, -25}, // 0x5B '[' + {3117, 10, 25, 10, 0, -24}, // 0x5C '\' + {3149, 8, 33, 12, 1, -25}, // 0x5D ']' + {3182, 16, 15, 20, 2, -23}, // 0x5E '^' + {3212, 21, 3, 19, -1, 5}, // 0x5F '_' + {3220, 7, 5, 9, 1, -25}, // 0x60 '`' + {3225, 18, 19, 20, 1, -18}, // 0x61 'a' + {3268, 18, 26, 22, 2, -25}, // 0x62 'b' + {3327, 17, 19, 20, 1, -18}, // 0x63 'c' + {3368, 19, 26, 22, 1, -25}, // 0x64 'd' + {3430, 18, 19, 20, 1, -18}, // 0x65 'e' + {3473, 10, 26, 12, 1, -25}, // 0x66 'f' + {3506, 18, 26, 21, 1, -18}, // 0x67 'g' + {3565, 17, 26, 21, 2, -25}, // 0x68 'h' + {3621, 5, 26, 10, 2, -25}, // 0x69 'i' + {3638, 7, 33, 10, 0, -25}, // 0x6A 'j' + {3667, 17, 26, 20, 2, -25}, // 0x6B 'k' + {3723, 5, 26, 9, 2, -25}, // 0x6C 'l' + {3740, 27, 19, 31, 2, -18}, // 0x6D 'm' + {3805, 17, 19, 21, 2, -18}, // 0x6E 'n' + {3846, 19, 19, 21, 1, -18}, // 0x6F 'o' + {3892, 18, 26, 22, 2, -18}, // 0x70 'p' + {3951, 19, 26, 22, 1, -18}, // 0x71 'q' + {4013, 11, 19, 14, 2, -18}, // 0x72 'r' + {4040, 17, 19, 19, 1, -18}, // 0x73 's' + {4081, 9, 23, 12, 1, -22}, // 0x74 't' + {4107, 17, 19, 21, 2, -18}, // 0x75 'u' + {4148, 19, 19, 19, 0, -18}, // 0x76 'v' + {4194, 27, 19, 27, 0, -18}, // 0x77 'w' + {4259, 18, 19, 19, 1, -18}, // 0x78 'x' + {4302, 19, 26, 19, 0, -18}, // 0x79 'y' + {4364, 16, 19, 18, 1, -18}, // 0x7A 'z' + {4402, 9, 33, 14, 1, -25}, // 0x7B '{' + {4440, 3, 33, 10, 4, -25}, // 0x7C '|' + {4453, 9, 33, 14, 3, -25}, // 0x7D '}' + {4491, 15, 6, 18, 1, -10}}; // 0x7E '~' + +const GFXfont FreeSansBold18pt7b PROGMEM = { + (uint8_t *)FreeSansBold18pt7bBitmaps, (GFXglyph *)FreeSansBold18pt7bGlyphs, + 0x20, 0x7E, 42}; + +// Approx. 5175 bytes diff --git a/src/fonts/FreeSansBold18pt7b.trimmed.h b/src/fonts/FreeSansBold18pt7b.trimmed.h new file mode 100644 index 0000000..81efb2d --- /dev/null +++ b/src/fonts/FreeSansBold18pt7b.trimmed.h @@ -0,0 +1,181 @@ +/* + + Generated by AdafruitGFXFontToBitmap + https: //github.com/MvRens/AdafruitGFXFontTools + + Source: FreeSansBold18pt7b.h + Filter: [0-9\.mSTOPMenu] + +*/ + +const uint8_t FreeSansBold18pt7bTrimmedBitmaps[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, 0x87, 0xFF, + 0xC7, 0xE3, 0xF3, 0xE0, 0xF9, 0xF0, 0x7D, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, + 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, + 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xDF, 0x07, 0xCF, 0x83, 0xE7, 0xE3, + 0xF1, 0xFF, 0xF0, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x01, 0xC0, + 0xF0, 0x3C, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xC1, 0xF0, 0x7C, + 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, + 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC0, 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0xFF, + 0x8F, 0xFF, 0xE7, 0xE3, 0xF7, 0xE0, 0xFF, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, + 0x0F, 0x80, 0x07, 0xC0, 0x07, 0xE0, 0x03, 0xE0, 0x03, 0xF0, 0x03, 0xF0, + 0x07, 0xF0, 0x07, 0xF0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xC0, 0x07, 0xC0, + 0x03, 0xE0, 0x03, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0x7F, 0xFF, 0x80, + 0x07, 0xE0, 0x0F, 0xFC, 0x0F, 0xFF, 0x0F, 0xFF, 0xCF, 0xC3, 0xF7, 0xC0, + 0xFB, 0xE0, 0x7D, 0xF0, 0x3E, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x0F, 0x80, + 0x3F, 0x80, 0x1F, 0xC0, 0x0F, 0xF0, 0x00, 0xFC, 0x00, 0x3F, 0x00, 0x0F, + 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83, 0xF7, 0xC3, 0xF3, 0xFF, 0xF8, 0xFF, + 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x01, 0xFC, + 0x01, 0xFC, 0x03, 0xFC, 0x07, 0x7C, 0x07, 0x7C, 0x0E, 0x7C, 0x0E, 0x7C, + 0x1C, 0x7C, 0x18, 0x7C, 0x38, 0x7C, 0x70, 0x7C, 0x60, 0x7C, 0xE0, 0x7C, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7C, 0x00, 0x7C, + 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x1F, 0xFF, 0x0F, 0xFF, + 0x8F, 0xFF, 0xC7, 0xFF, 0xE3, 0xC0, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0x70, + 0x00, 0x79, 0xF0, 0x3F, 0xFE, 0x1F, 0xFF, 0x8F, 0xFF, 0xE7, 0xC3, 0xF0, + 0x00, 0xFC, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xFE, 0x03, + 0xFF, 0x03, 0xFF, 0xC3, 0xF3, 0xFF, 0xF1, 0xFF, 0xF8, 0x3F, 0xF0, 0x07, + 0xE0, 0x00, 0x03, 0xF8, 0x03, 0xFF, 0x81, 0xFF, 0xF0, 0xFF, 0xFE, 0x3E, + 0x1F, 0x9F, 0x03, 0xE7, 0xC0, 0x03, 0xE0, 0x00, 0xF8, 0xF8, 0x3E, 0xFF, + 0x8F, 0xFF, 0xF3, 0xFF, 0xFE, 0xFE, 0x1F, 0xBF, 0x03, 0xFF, 0x80, 0x7F, + 0xE0, 0x1F, 0xF8, 0x07, 0xFE, 0x01, 0xF7, 0x80, 0x7D, 0xF0, 0x3E, 0x7E, + 0x1F, 0x8F, 0xFF, 0xC1, 0xFF, 0xF0, 0x3F, 0xF0, 0x03, 0xF0, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0xF8, + 0x00, 0xF8, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x3C, 0x00, 0x3E, 0x00, 0x1E, + 0x00, 0x1F, 0x00, 0x0F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xC0, 0x03, + 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x3E, 0x00, + 0x1F, 0x00, 0x0F, 0x80, 0x00, 0x07, 0xE0, 0x07, 0xFC, 0x0F, 0xFF, 0x07, + 0xFF, 0xC7, 0xC3, 0xF3, 0xC0, 0xF9, 0xE0, 0x3C, 0xF0, 0x1E, 0x78, 0x1F, + 0x1E, 0x1F, 0x07, 0xFF, 0x01, 0xFF, 0x03, 0xFF, 0xE3, 0xF1, 0xF9, 0xF0, + 0x7D, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83, 0xF7, + 0xC3, 0xF3, 0xFF, 0xF8, 0xFF, 0xF8, 0x3F, 0xF8, 0x07, 0xF0, 0x00, 0x07, + 0xE0, 0x0F, 0xFC, 0x0F, 0xFF, 0x0F, 0xFF, 0xC7, 0xE3, 0xF7, 0xE0, 0xFB, + 0xE0, 0x3D, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x83, + 0xF7, 0xE3, 0xFB, 0xFF, 0xFC, 0xFF, 0xFE, 0x3F, 0xDF, 0x07, 0xCF, 0x80, + 0x07, 0x80, 0x03, 0xDF, 0x03, 0xE7, 0xC3, 0xE3, 0xFF, 0xF0, 0xFF, 0xF0, + 0x3F, 0xF0, 0x07, 0xE0, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFB, 0xC3, 0xDF, 0xFB, 0xC3, 0xDF, 0xFB, + 0xC3, 0xDF, 0xFB, 0xC3, 0xDF, 0xF9, 0xC7, 0xDF, 0xF9, 0xE7, 0x9F, 0xF9, + 0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF9, 0xE7, 0x9F, 0xF8, 0xFF, 0x1F, 0xF8, + 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, 0xFF, 0x1F, 0xF8, 0x7F, 0x1F, 0xF8, + 0x7E, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, 0x7E, 0x1F, 0xF8, 0x3E, 0x1F, 0x00, + 0x7F, 0x00, 0x01, 0xFF, 0xF0, 0x01, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x01, + 0xFC, 0x1F, 0xC1, 0xF8, 0x03, 0xF1, 0xF8, 0x00, 0xFC, 0xF8, 0x00, 0x3E, + 0x7C, 0x00, 0x1F, 0x7C, 0x00, 0x07, 0xFE, 0x00, 0x03, 0xFF, 0x00, 0x01, + 0xFF, 0x80, 0x00, 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x3F, 0xF0, 0x00, + 0x1F, 0xF8, 0x00, 0x0F, 0xBE, 0x00, 0x0F, 0x9F, 0x00, 0x07, 0xCF, 0xC0, + 0x07, 0xE3, 0xF0, 0x07, 0xE0, 0xFE, 0x0F, 0xE0, 0x7F, 0xFF, 0xE0, 0x0F, + 0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x00, 0x3F, 0x80, 0x00, 0xFF, 0xFC, 0x1F, + 0xFF, 0xE3, 0xFF, 0xFE, 0x7F, 0xFF, 0xEF, 0x80, 0xFF, 0xF0, 0x0F, 0xFE, + 0x00, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, 0xFF, 0x00, 0x7F, 0xE0, 0x1F, 0xFC, + 0x07, 0xEF, 0xFF, 0xFD, 0xFF, 0xFF, 0x3F, 0xFF, 0xC7, 0xFF, 0xE0, 0xF8, + 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, + 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x00, 0x07, + 0xF8, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, 0x87, 0xFF, 0xFC, 0x7E, 0x0F, 0xCF, + 0xC0, 0x7E, 0xF8, 0x03, 0xEF, 0x80, 0x3E, 0xF8, 0x00, 0x0F, 0xC0, 0x00, + 0xFF, 0x00, 0x07, 0xFF, 0xC0, 0x3F, 0xFF, 0x81, 0xFF, 0xFC, 0x03, 0xFF, + 0xE0, 0x01, 0xFF, 0x00, 0x03, 0xF0, 0x00, 0x1F, 0xF8, 0x01, 0xFF, 0x80, + 0x1F, 0xFC, 0x03, 0xFF, 0xE0, 0x7E, 0x7F, 0xFF, 0xE3, 0xFF, 0xFC, 0x1F, + 0xFF, 0x00, 0x3F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, + 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, + 0xE0, 0x00, 0x7C, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, + 0xC0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x0F, + 0x80, 0x01, 0xF0, 0x00, 0x3E, 0x00, 0x07, 0xE0, 0x07, 0xFE, 0x03, 0xFF, + 0xE0, 0xFF, 0xF8, 0x7E, 0x1F, 0x1F, 0x03, 0xCF, 0x80, 0xFB, 0xE0, 0x1E, + 0xFF, 0xFF, 0xBF, 0xFF, 0xEF, 0xFF, 0xFB, 0xE0, 0x00, 0xF8, 0x00, 0x3F, + 0x03, 0xE7, 0xE1, 0xF9, 0xFF, 0xFC, 0x3F, 0xFE, 0x07, 0xFF, 0x00, 0x7F, + 0x00, 0xF8, 0xF8, 0x3F, 0x1F, 0x7F, 0x9F, 0xF3, 0xFF, 0xFF, 0xFF, 0x7F, + 0xFF, 0xFF, 0xFF, 0xC3, 0xF8, 0x7F, 0xF8, 0x3F, 0x07, 0xFE, 0x07, 0xC0, + 0xFF, 0xC0, 0xF8, 0x1F, 0xF8, 0x1F, 0x03, 0xFF, 0x03, 0xE0, 0x7F, 0xE0, + 0x7C, 0x0F, 0xFC, 0x0F, 0x81, 0xFF, 0x81, 0xF0, 0x3F, 0xF0, 0x3E, 0x07, + 0xFE, 0x07, 0xC0, 0xFF, 0xC0, 0xF8, 0x1F, 0xF8, 0x1F, 0x03, 0xFF, 0x03, + 0xE0, 0x7F, 0xE0, 0x7C, 0x0F, 0x80, 0xF8, 0xF8, 0x7D, 0xFF, 0x3F, 0xFF, + 0xDF, 0xFF, 0xEF, 0xE1, 0xFF, 0xE0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, + 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, + 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xE0, 0xF8, + 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, + 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, + 0xFF, 0x80, 0xFF, 0xC0, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0x9F, + 0xF7, 0xC7, 0xE3, 0xE0 }; + +const GFXglyph FreeSansBold18pt7bTrimmedGlyphs[] PROGMEM = { + { 0, 5, 5, 9, 2, -4 }, // 0x2E '.' + { 0, 0, 0, 0, 0, 0 }, // 0x2F '/' + { 4, 17, 25, 19, 1, -24 }, // 0x30 '0' + { 58, 10, 25, 19, 3, -24 }, // 0x31 '1' + { 90, 17, 25, 19, 1, -24 }, // 0x32 '2' + { 144, 17, 25, 19, 1, -24 }, // 0x33 '3' + { 198, 16, 25, 19, 2, -24 }, // 0x34 '4' + { 248, 17, 25, 19, 1, -24 }, // 0x35 '5' + { 302, 18, 25, 19, 1, -24 }, // 0x36 '6' + { 359, 17, 25, 19, 1, -24 }, // 0x37 '7' + { 413, 17, 25, 19, 1, -24 }, // 0x38 '8' + { 467, 17, 25, 19, 1, -24 }, // 0x39 '9' + { 0, 0, 0, 0, 0, 0 }, // 0x3A ':' + { 0, 0, 0, 0, 0, 0 }, // 0x3B ';' + { 0, 0, 0, 0, 0, 0 }, // 0x3C '<' + { 0, 0, 0, 0, 0, 0 }, // 0x3D '=' + { 0, 0, 0, 0, 0, 0 }, // 0x3E '>' + { 0, 0, 0, 0, 0, 0 }, // 0x3F '?' + { 0, 0, 0, 0, 0, 0 }, // 0x40 '@' + { 0, 0, 0, 0, 0, 0 }, // 0x41 'A' + { 0, 0, 0, 0, 0, 0 }, // 0x42 'B' + { 0, 0, 0, 0, 0, 0 }, // 0x43 'C' + { 0, 0, 0, 0, 0, 0 }, // 0x44 'D' + { 0, 0, 0, 0, 0, 0 }, // 0x45 'E' + { 0, 0, 0, 0, 0, 0 }, // 0x46 'F' + { 0, 0, 0, 0, 0, 0 }, // 0x47 'G' + { 0, 0, 0, 0, 0, 0 }, // 0x48 'H' + { 0, 0, 0, 0, 0, 0 }, // 0x49 'I' + { 0, 0, 0, 0, 0, 0 }, // 0x4A 'J' + { 0, 0, 0, 0, 0, 0 }, // 0x4B 'K' + { 0, 0, 0, 0, 0, 0 }, // 0x4C 'L' + { 521, 24, 26, 30, 3, -25 }, // 0x4D 'M' + { 0, 0, 0, 0, 0, 0 }, // 0x4E 'N' + { 599, 25, 26, 27, 1, -25 }, // 0x4F 'O' + { 681, 19, 26, 24, 3, -25 }, // 0x50 'P' + { 0, 0, 0, 0, 0, 0 }, // 0x51 'Q' + { 0, 0, 0, 0, 0, 0 }, // 0x52 'R' + { 743, 20, 26, 24, 2, -25 }, // 0x53 'S' + { 808, 19, 26, 23, 2, -25 }, // 0x54 'T' + { 0, 0, 0, 0, 0, 0 }, // 0x55 'U' + { 0, 0, 0, 0, 0, 0 }, // 0x56 'V' + { 0, 0, 0, 0, 0, 0 }, // 0x57 'W' + { 0, 0, 0, 0, 0, 0 }, // 0x58 'X' + { 0, 0, 0, 0, 0, 0 }, // 0x59 'Y' + { 0, 0, 0, 0, 0, 0 }, // 0x5A 'Z' + { 0, 0, 0, 0, 0, 0 }, // 0x5B '[' + { 0, 0, 0, 0, 0, 0 }, // 0x5C '\' + { 0, 0, 0, 0, 0, 0 }, // 0x5D ']' + { 0, 0, 0, 0, 0, 0 }, // 0x5E '^' + { 0, 0, 0, 0, 0, 0 }, // 0x5F '_' + { 0, 0, 0, 0, 0, 0 }, // 0x60 '`' + { 0, 0, 0, 0, 0, 0 }, // 0x61 'a' + { 0, 0, 0, 0, 0, 0 }, // 0x62 'b' + { 0, 0, 0, 0, 0, 0 }, // 0x63 'c' + { 0, 0, 0, 0, 0, 0 }, // 0x64 'd' + { 870, 18, 19, 20, 1, -18 }, // 0x65 'e' + { 0, 0, 0, 0, 0, 0 }, // 0x66 'f' + { 0, 0, 0, 0, 0, 0 }, // 0x67 'g' + { 0, 0, 0, 0, 0, 0 }, // 0x68 'h' + { 0, 0, 0, 0, 0, 0 }, // 0x69 'i' + { 0, 0, 0, 0, 0, 0 }, // 0x6A 'j' + { 0, 0, 0, 0, 0, 0 }, // 0x6B 'k' + { 0, 0, 0, 0, 0, 0 }, // 0x6C 'l' + { 913, 27, 19, 31, 2, -18 }, // 0x6D 'm' + { 978, 17, 19, 21, 2, -18 }, // 0x6E 'n' + { 0, 0, 0, 0, 0, 0 }, // 0x6F 'o' + { 0, 0, 0, 0, 0, 0 }, // 0x70 'p' + { 0, 0, 0, 0, 0, 0 }, // 0x71 'q' + { 0, 0, 0, 0, 0, 0 }, // 0x72 'r' + { 0, 0, 0, 0, 0, 0 }, // 0x73 's' + { 0, 0, 0, 0, 0, 0 }, // 0x74 't' + { 1019, 17, 19, 21, 2, -18 } }; // 0x75 'u' + +const GFXfont FreeSansBold18pt7bTrimmed PROGMEM = { + (uint8_t *)FreeSansBold18pt7bTrimmedBitmaps, + (GFXglyph *)FreeSansBold18pt7bTrimmedGlyphs, + 0x2E, 0x75, 42 }; + +// Approx. 1571 bytes diff --git a/src/fonts/trim.bat b/src/fonts/trim.bat new file mode 100644 index 0000000..4487c57 --- /dev/null +++ b/src/fonts/trim.bat @@ -0,0 +1,7 @@ +@Echo Off + +REM It is a nuisance to keep the filter up-to-date with whatever is printed +REM in the actual source, but the space savings are worth it. +AdafruitGFXFontTrim [0-9\.mSTOPMenu] FreeSansBold18pt7b.h + +pause \ No newline at end of file diff --git a/src/include/colors.h b/src/include/colors.h deleted file mode 100644 index c920710..0000000 --- a/src/include/colors.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __colors -#define __colors - -#define BLACK 0x0000 -#define BLUE 0x001F -#define RED 0xF800 -#define GREEN 0x07E0 -#define CYAN 0x07FF -#define MAGENTA 0xF81F -#define YELLOW 0xFFE0 -#define WHITE 0xFFFF - - -#define COLOR_INITSEQ_BACKGROUND BLACK -#define COLOR_INITSEQ_TITLE YELLOW -#define COLOR_INITSEQ_ITEMS WHITE -#define COLOR_INITSEQ_SUCCESS GREEN -#define COLOR_INITSEQ_ERROR RED - - -#define COLOR_MENU_BACKGROUND BLACK - -#endif \ No newline at end of file diff --git a/src/include/config.h b/src/include/config.h new file mode 100644 index 0000000..4a28294 --- /dev/null +++ b/src/include/config.h @@ -0,0 +1,102 @@ +#ifndef __config +#define __config + + +// The default color set provided by Adafruit +#define BLACK 0x0000 +#define BLUE 0x001F +#define RED 0xF800 +#define GREEN 0x07E0 +#define CYAN 0x07FF +#define MAGENTA 0xF81F +#define YELLOW 0xFFE0 +#define WHITE 0xFFFF + + + /* + + TODO make constants for these values once they are used + + #define STOP_RED 0xF9A6 + #define MOVING_ORANGE 0xF443 + #define MOVING_ORANGE_LIGHT 0xFD8C + */ + + + +class Config +{ + public: + /* + + Display + Settings for an ST7789 based 240x240 pixel LCD. + + Note that all screen class implementations are only tested against + this resolution, changing it may have unexpected results. + */ + static const uint8_t DisplayWidth = 240; + static const uint8_t DisplayHeight = 240; + static const uint8_t DisplayRotation = 2; + + + // Control pins. Data is sent over the hardware SPI pins. + static const uint8_t DisplayPortCS = 10; + static const uint8_t DisplayPortRST = 9; + static const uint8_t DisplayPortDC = 8; + static const uint8_t DisplayPortBL = 7; + + + /* + Height sensor + Settings for a VL53L0X time-of-flight sensor. + + */ + static const uint8_t HeightSensorI2CAddress = 0x29; + + + // Values above this will be disregarded as invalid + static const uint16_t HeightMeasurementMax = 1500; + + // How much the measurements can change to still be considered "stable" + static const uint8_t HeightMeasurementDeltaStable = 5; + + // How far in advance to stop the motor + static const uint8_t HeightMeasurementDeltaStop = 0; + + // How far off we can be from the target height to still be considered on target + static const uint8_t HeightMeasurementDeltaOnTarget = 5; + + // How long we allow invalid measurements until the move operation is aborted + static const uint8_t HeightMeasurementAbortTimeout = 100; + + + + /* + + Colors + + */ + + + // A useful RGB565 calculator: http://www.rinkydinkelectronics.com/calc_rgb565.php + + static const uint16_t ColorInitSeqBackground = BLACK; + static const uint16_t ColorInitSeqTitle = YELLOW; + static const uint16_t ColorInitSeqItems = WHITE; + static const uint16_t ColorInitSeqSuccess = GREEN; + static const uint16_t ColorInitSeqError = RED; + + static const uint16_t ColorMenuBackground = BLACK; + + static const uint16_t ColorPresetText = 0x9492; // Light gray + static const uint16_t ColorPresetBackground = BLACK; + static const uint16_t ColorPresetNumber = 0x528A; // Dark gray + + static const uint16_t ColorPresetSelectedText = WHITE; + static const uint16_t ColorPresetSelectedBackground = 0x2BE7; // Soft green + static const uint16_t ColorPresetSelectedNumber = 0x556B; // Slightly lighter green + +}; + +#endif \ No newline at end of file diff --git a/src/include/display.h b/src/include/display.h deleted file mode 100644 index 01cb3bd..0000000 --- a/src/include/display.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __ports -#define __ports - -// Settings for an ST7789 based 240x240 pixel LCD. -#define DISPLAY_WIDTH 240 -#define DISPLAY_HEIGHT 240 -#define DISPLAY_ROTATION 2 - -// Control pins. Data is sent over the hardware SPI pins. -#define DISPLAY_PORT_CS 10 -#define DISPLAY_PORT_RST 9 -#define DISPLAY_PORT_DC 8 -#define DISPLAY_PORT_BL 7 - -#endif \ No newline at end of file diff --git a/src/include/heightsensor.h b/src/include/heightsensor.h deleted file mode 100644 index ccc9abc..0000000 --- a/src/include/heightsensor.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __heightsensor -#define __heightsensor - -#define HEIGHTSENSOR_I2C_ADDRESS 0x29 - -#endif \ No newline at end of file diff --git a/src/lib/limits.h b/src/lib/limits.h deleted file mode 100644 index f6b3c3b..0000000 --- a/src/lib/limits.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __limits -#define __limits - -// Values above this will be disregarded as invalid -#define HeightMeasurementMax 1500 - -// How much the measurements can change to still be considered "stable" -#define HeightMeasurementStableDelta 5 - -#endif \ No newline at end of file diff --git a/src/lib/motor.cpp b/src/lib/motor.cpp new file mode 100644 index 0000000..77208c1 --- /dev/null +++ b/src/lib/motor.cpp @@ -0,0 +1,22 @@ +#include "./motor.h" + + +void motorStart(MotorDirection direction) +{ + // TODO +} + + +void motorStop() +{ + // TODO +} + + +bool motorIsOverCurrent() +{ + // TODO + return false; +} + + diff --git a/src/lib/motor.h b/src/lib/motor.h new file mode 100644 index 0000000..9c275d6 --- /dev/null +++ b/src/lib/motor.h @@ -0,0 +1,16 @@ +#ifndef __motor +#define __motor + + +enum class MotorDirection +{ + Up = 0, + Down = 1 +}; + + +extern void motorStart(MotorDirection direction); +extern void motorStop(); +extern bool motorIsOverCurrent(); + +#endif \ No newline at end of file diff --git a/src/lib/persist.cpp b/src/lib/persist.cpp deleted file mode 100644 index 92b83ed..0000000 --- a/src/lib/persist.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "./persist.h" -#include - - -bool Persist::init() -{ - PersistHeader header; - EEPROM.get(0, header); - - if (header.identifier == PersistIdentifier) - { - if (header.version >= 1) - this->readHeights(); - - return true; - } - - return false; -} - - -void Persist::readHeights() -{ - EEPROM.get(PersistHeightsAddress, this->heights); -} - - -void Persist::writeHeights() -{ - EEPROM.put(PersistHeightsAddress, this->heights); -} - - -void Persist::setHeightOffset(uint8_t newValue) -{ - if (newValue == this->heights.offset) - return; - - this->heights.offset = newValue; - this->writeHeights(); -} diff --git a/src/lib/persist.h b/src/lib/persist.h deleted file mode 100644 index bb7799b..0000000 --- a/src/lib/persist.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef __persist -#define __persist -#include -#include - - -#define PersistIdentifier 0x42 -#define PersistVersion1 1 - - -struct PersistHeader -{ - uint8_t identifier; - uint8_t version; -}; - - -#define PersistHeightsAddress sizeof(PersistHeader) -struct PersistHeights -{ - uint8_t offset; - uint16_t setting[2]; -}; - - - -class Persist -{ - public: - Persist() - { - memset(&this->heights, 0, sizeof(PersistHeights)); - } - - bool init(); - - // How many centimeters up the reflective surface is from the ground - uint8_t getHeightOffset() { return this->heights.offset; } - void setHeightOffset(uint8_t newValue); - - - private: - void readHeights(); - void writeHeights(); - - PersistHeights heights; -}; - -#endif \ No newline at end of file diff --git a/src/lib/screen.h b/src/lib/screen.h index 45ee1f0..59bedf5 100644 --- a/src/lib/screen.h +++ b/src/lib/screen.h @@ -2,19 +2,13 @@ #define __screen #include -#include "include/display.h" - - -#if DISPLAY_WIDTH != 240 || DISPLAY_HEIGHT != 240 - #error "The menu assumes a display pixel size of 240x240" -#endif - +#include "include/config.h" class ScreenManager; -enum Button +enum class Button { Up = 0, Menu = 1, @@ -49,16 +43,14 @@ class BaseScreen class ScreenManager { public: - ScreenManager(Adafruit_GFX* display, uint32_t* currentTime) + ScreenManager(Adafruit_GFX* display) { this->display = display; - this->currentTime = currentTime; } inline void tick() { this->getCurrentScreen()->onTick(); } inline BaseScreen* getCurrentScreen() { return this->currentScreen; } - inline uint32_t getCurrentTime() { return *this->currentTime; } inline Adafruit_GFX* getDisplay() { return this->display; } @@ -76,7 +68,6 @@ class ScreenManager private: Adafruit_GFX* display; - uint32_t* currentTime; BaseScreen* currentScreen = nullptr; }; diff --git a/src/lib/screen/home.cpp b/src/lib/screen/home.cpp index 54e16fb..3948a60 100644 --- a/src/lib/screen/home.cpp +++ b/src/lib/screen/home.cpp @@ -1,17 +1,52 @@ #include "./home.h" -#include -#include "include/colors.h" +#include "fonts/FreeSansBold18pt7b.trimmed.h" +#include "include/config.h" +#include "lib/settings.h" + + +#define HOME_FONT_BASELINE 25 +#define HOME_FONT_HEIGHT 42 + +#define HOME_MOVING_REFRESHRATE 250 + +#define PRESET_MARGIN 7 + +// HOME_FONT_BASELINE is not a mistake, as there are no characters used which +// go below the baseline. +#define PRESET_LINEHEIGHT (HOME_FONT_BASELINE + (2 * PRESET_MARGIN)) + + +// TODO move to shared file +// Formats a height value as "0.00m" (always exactly 5 characters long). +// Buffer must be at least 5 bytes long. No null character is added. +void getDisplayHeight(char* buffer, uint8_t value) +{ + if (value > 99) + buffer[0] = '0' + ((value / 100) % 10); + else + buffer[0] = '0'; + + buffer[1] = '.'; + buffer[2] = '0' + ((value / 10) % 10); + buffer[3] = '0' + (value % 10); + buffer[4] = 'm'; +} void HomeScreen::onShow() { + this->showTime = State.CurrentTime; + auto display = this->getDisplay(); - display->setFont(&FreeSansBold18pt7b); - display->fillScreen(COLOR_MENU_BACKGROUND); - display->setCursor(0, 25); + display->setFont(&FreeSansBold18pt7bTrimmed); + display->fillScreen(Config::ColorMenuBackground); - display->print("Hello world!"); + this->drawPreset1(); + this->drawMenu(); + this->drawPreset2(); + + // TODO if the height does not match either preset, display the actual height } @@ -22,4 +57,70 @@ void HomeScreen::onButton(Button button) void HomeScreen::onTick() { -} \ No newline at end of file + // TODO turn off screen after timeout + //if (State.CurrentTime ) +} + + +void HomeScreen::drawPreset1() +{ + this->drawPreset(1, 0, Settings.Height.Preset[0]); +} + + +void HomeScreen::drawPreset2() +{ + this->drawPreset(2, Config::DisplayHeight - PRESET_LINEHEIGHT, Settings.Height.Preset[1]); +} + + +void HomeScreen::drawPreset(uint8_t number, uint8_t y, uint8_t value) +{ + auto display = this->getDisplay(); + uint16_t numberColor; + uint16_t textColor; + uint16_t backgroundColor; + + // An exact comparison is enough here, the movement code takes care of that if it's "close enough" + if (value == State.CurrentHeight) + { + textColor = Config::ColorPresetSelectedText; + numberColor = Config::ColorPresetSelectedNumber; + backgroundColor = Config::ColorPresetSelectedBackground; + } + else + { + textColor = Config::ColorPresetText; + numberColor = Config::ColorPresetNumber; + backgroundColor = Config::ColorPresetBackground; + } + + display->fillRect(0, y, Config::DisplayWidth, PRESET_LINEHEIGHT, backgroundColor); + + display->setCursor(0, y + HOME_FONT_BASELINE + PRESET_MARGIN); + display->setTextColor(numberColor); + display->print("P"); + display->print(number); + + + char textValue[6]; + getDisplayHeight(&textValue[0], value); + textValue[5] = 0; + + // Calculate the center position + int16_t textX; + int16_t textY; + uint16_t textW; + uint16_t textH; + display->getTextBounds(&textValue[0], 0, 0, &textX, &textY, &textW, &textH); + + textX = (Config::DisplayWidth - textW) / 2; + display->setCursor(textX, y + HOME_FONT_BASELINE + PRESET_MARGIN); + display->setTextColor(textColor); + display->print(textValue); +} + + +void HomeScreen::drawMenu() +{ +} diff --git a/src/lib/screen/home.h b/src/lib/screen/home.h index 7e8460f..d8c31e4 100644 --- a/src/lib/screen/home.h +++ b/src/lib/screen/home.h @@ -2,6 +2,7 @@ #define __screen_home #include "../screen.h" +#include "../state.h" /* @@ -16,6 +17,14 @@ class HomeScreen : public BaseScreen void onShow(); void onButton(Button button); void onTick(); + + private: + uint32_t showTime; + + void drawPreset1(); + void drawPreset2(); + void drawPreset(uint8_t number, uint8_t y, uint8_t value); + void drawMenu(); }; #endif diff --git a/src/lib/screen/move.cpp b/src/lib/screen/move.cpp new file mode 100644 index 0000000..8833c96 --- /dev/null +++ b/src/lib/screen/move.cpp @@ -0,0 +1,48 @@ +#include "./move.h" +#include "./home.h" +#include "fonts/FreeSansBold18pt7b.trimmed.h" +#include "include/config.h" +#include "lib/settings.h" + + +#define MOVE_FONT_BASELINE 25 +#define MOVE_FONT_HEIGHT 42 + +#define MOVE_REFRESHRATE 250 + + +void MoveScreen::onShow() +{ + auto display = this->getDisplay(); + + display->setFont(&FreeSansBold18pt7bTrimmed); + display->fillScreen(Config::ColorMenuBackground); + + // TODO draw current and target height +} + + +void MoveScreen::onButton(Button button) +{ + // TODO stop the motor + this->getScreenManager()->show(); +} + + +void MoveScreen::onTick() +{ + if (State.MoveDirection != Direction::None) + { + this->getScreenManager()->show(); + return; + } + + // Don't update every tick, it takes a significant amount of time + // and monitoring the current height is more important. + // TODO should we also stop updating when we're approaching the target height or is that overkill? + if (State.CurrentTime - this->lastRefresh >= MOVE_REFRESHRATE) + { + + this->lastRefresh = State.CurrentTime; + } +} \ No newline at end of file diff --git a/src/lib/screen/move.h b/src/lib/screen/move.h new file mode 100644 index 0000000..3cf5bc3 --- /dev/null +++ b/src/lib/screen/move.h @@ -0,0 +1,25 @@ +#ifndef __screen_move +#define __screen_move + +#include "../screen.h" +#include "../state.h" + + +/* + * Move screen + * Shows the current and target height + */ +class MoveScreen : public BaseScreen +{ + public: + MoveScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { } + + void onShow(); + void onButton(Button button); + void onTick(); + + private: + uint32_t lastRefresh; +}; + +#endif diff --git a/src/lib/settings.cpp b/src/lib/settings.cpp new file mode 100644 index 0000000..fe2d161 --- /dev/null +++ b/src/lib/settings.cpp @@ -0,0 +1,52 @@ +#include "./settings.h" +#include + + +#define SettingsIdentifier 0x42 +#define SettingsVersion1 1 + +SettingsContainer Settings; + + +struct SettingsHeader +{ + uint8_t identifier; + uint8_t version; +}; + +#define SettingsHeightsAddress sizeof(SettingsHeader) + + +// Forward declarations +void readHeights(); + + +bool readSettings() +{ + memset(&Settings, 0, sizeof(SettingsContainer)); + + SettingsHeader header; + EEPROM.get(0, header); + + if (header.identifier == SettingsIdentifier) + { + if (header.version >= 1) + readHeights(); + + return true; + } + + return false; +} + + +void readHeights() +{ + EEPROM.get(SettingsHeightsAddress, Settings.Height); +} + + +void writeSettingsHeights() +{ + EEPROM.put(SettingsHeightsAddress, Settings.Height); +} diff --git a/src/lib/settings.h b/src/lib/settings.h new file mode 100644 index 0000000..0715721 --- /dev/null +++ b/src/lib/settings.h @@ -0,0 +1,25 @@ +#ifndef __settings +#define __settings +#include +#include + + +struct SettingsHeights +{ + uint8_t Offset; + uint16_t Preset[2]; +}; + + +struct SettingsContainer +{ + SettingsHeights Height; +}; + + +extern bool readSettings(); +extern void writeSettingsHeights(); + +extern SettingsContainer Settings; + +#endif \ No newline at end of file diff --git a/src/lib/state.cpp b/src/lib/state.cpp index f66ee9a..9f40292 100644 --- a/src/lib/state.cpp +++ b/src/lib/state.cpp @@ -1,16 +1,3 @@ #include "state.h" -uint16_t currentHeight = 0; - - - -uint16_t getCurrentHeight() -{ - return currentHeight; -} - - -void setCurrentHeight(uint16_t value) -{ - currentHeight = value; -} \ No newline at end of file +StateContainer State; \ No newline at end of file diff --git a/src/lib/state.h b/src/lib/state.h index bcbba35..9261962 100644 --- a/src/lib/state.h +++ b/src/lib/state.h @@ -3,7 +3,23 @@ #include "stdint.h" -uint16_t getCurrentHeight(); -void setCurrentHeight(uint16_t value); +enum class Direction +{ + None = 0, + Up = 1, + Down = 2 +}; + + +struct StateContainer +{ + uint32_t CurrentTime; + uint16_t CurrentHeight; + + Direction MoveDirection; + uint16_t MoveTarget; +}; + +extern StateContainer State; #endif diff --git a/src/lib/vl53l0x.cpp b/src/lib/vl53l0x.cpp index 8016da5..d1a7162 100644 --- a/src/lib/vl53l0x.cpp +++ b/src/lib/vl53l0x.cpp @@ -89,7 +89,7 @@ bool VL53L0X::getSingleRangingMeasurement(VL53L0X_RangingMeasurementData_t *data } -bool VL53L0X::getSingleRangingMeasurement(uint16_t* distanceMillimeters, VL53L0X_Error* error) +bool VL53L0X::getSingleRangingMeasurement(uint16_t* distanceMillimeters, VL53L0X_Error* error, uint16_t maxValidHeight) { VL53L0X_RangingMeasurementData_t data; auto result = this->getSingleRangingMeasurement(&data, error); @@ -97,5 +97,5 @@ bool VL53L0X::getSingleRangingMeasurement(uint16_t* distanceMillimeters, VL53L0X if (result) *distanceMillimeters = data.RangeMilliMeter; - return result; + return result && data.RangeMilliMeter <= maxValidHeight; } \ No newline at end of file diff --git a/src/lib/vl53l0x.h b/src/lib/vl53l0x.h index 5351c50..77b71f0 100644 --- a/src/lib/vl53l0x.h +++ b/src/lib/vl53l0x.h @@ -44,7 +44,7 @@ class VL53L0X bool init(uint8_t address, VL53L0XResult* result); bool setMeasurementTimingBudget(uint32_t budget_us, VL53L0X_Error* error); bool getSingleRangingMeasurement(VL53L0X_RangingMeasurementData_t* data, VL53L0X_Error* error); - bool getSingleRangingMeasurement(uint16_t* distanceMillimeters, VL53L0X_Error* error); + bool getSingleRangingMeasurement(uint16_t* distanceMillimeters, VL53L0X_Error* error, uint16_t maxValidHeight); private: VL53L0X_Dev_t device; diff --git a/src/main.cpp b/src/main.cpp index cce1ea0..e1ed73e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,44 +3,43 @@ #include #include -#include "./include/display.h" -#include "./include/heightsensor.h" -#include "./include/colors.h" +#include "./include/config.h" #include "./lib/debug.h" -#include "./lib/persist.h" +#include "./lib/settings.h" #include "./lib/screen.h" #include "./lib/vl53l0x.h" -#include "./lib/limits.h" #include "./lib/state.h" +#include "./lib/motor.h" #include "./lib/screen/home.h" +enum class InitSequenceStep +{ + EEPROM = 0, + HeightSensorInit = 1, + HeightSensorBudget = 2, + HeightSensorTest = 3, + + Last = HeightSensorTest +}; + + // Forward declarations inline void setupHeightSensor(); inline uint8_t testHeightSensor(); void initSequenceStart(); -void initSequenceSuccess(uint8_t which); -void initSequenceError(uint8_t which); +void initSequenceSuccess(InitSequenceStep step); +void initSequenceError(InitSequenceStep step); void initSequenceDisplayHeight(uint16_t measurement); void initSequenceEnd(); -#define INITSEQ_EEPROM 0 -#define INITSEQ_HEIGHTSENSORINIT 1 -#define INITSEQ_HEIGHTSENSORBUDGET 2 -#define INITSEQ_HEIGHTSENSORTEST 3 -#define INITSEQ_MAX INITSEQ_HEIGHTSENSORTEST - - -auto display = Adafruit_ST7789(DISPLAY_PORT_CS, DISPLAY_PORT_DC, DISPLAY_PORT_RST); +auto display = Adafruit_ST7789(Config::DisplayPortCS, Config::DisplayPortDC, Config::DisplayPortRST); auto heightSensor = VL53L0X(); -uint32_t currentTime = 0; - -auto persist = Persist(); -auto screenManager = ScreenManager(&display, ¤tTime); +auto screenManager = ScreenManager(&display); /* @@ -50,19 +49,20 @@ auto screenManager = ScreenManager(&display, ¤tTime); */ void setup() { + State.CurrentTime = millis(); DebugInit(); - pinMode(DISPLAY_PORT_BL, OUTPUT); - digitalWrite(DISPLAY_PORT_BL, HIGH); + pinMode(Config::DisplayPortBL, OUTPUT); + digitalWrite(Config::DisplayPortBL, HIGH); - display.init(DISPLAY_WIDTH, DISPLAY_HEIGHT, SPI_MODE3); - display.setRotation(DISPLAY_ROTATION); + display.init(Config::DisplayWidth, Config::DisplayHeight, SPI_MODE3); + display.setRotation(Config::DisplayRotation); initSequenceStart(); // Load settings from EEPROM - auto initialized = persist.init(); - initSequenceSuccess(INITSEQ_EEPROM); + auto initialized = readSettings(); + initSequenceSuccess(InitSequenceStep::EEPROM); // Initialize VL53L0X sensor @@ -74,7 +74,7 @@ void setup() if (initialized) { - setCurrentHeight(currentHeight + persist.getHeightOffset()); + State.CurrentHeight = currentHeight; screenManager.show(); } else @@ -89,9 +89,9 @@ inline void setupHeightSensor() Wire.begin(); VL53L0XResult result; - if (!heightSensor.init(HEIGHTSENSOR_I2C_ADDRESS, &result)) + if (!heightSensor.init(Config::HeightSensorI2CAddress, &result)) { - initSequenceError(INITSEQ_HEIGHTSENSORINIT); + initSequenceError(InitSequenceStep::HeightSensorInit); display.print(result.error); display.print(" @ "); @@ -99,19 +99,19 @@ inline void setupHeightSensor() while(1); } - initSequenceSuccess(INITSEQ_HEIGHTSENSORINIT); + initSequenceSuccess(InitSequenceStep::HeightSensorInit); VL53L0X_Error error; if (!heightSensor.setMeasurementTimingBudget(33000, &error)) { - initSequenceError(INITSEQ_HEIGHTSENSORBUDGET); + initSequenceError(InitSequenceStep::HeightSensorBudget); display.print(result.error); while(1); } - initSequenceSuccess(INITSEQ_HEIGHTSENSORBUDGET); + initSequenceSuccess(InitSequenceStep::HeightSensorBudget); } @@ -126,11 +126,11 @@ inline uint8_t testHeightSensor() { uint16_t measurement; - if (heightSensor.getSingleRangingMeasurement(&measurement, &error) && measurement <= HeightMeasurementMax) + if (heightSensor.getSingleRangingMeasurement(&measurement, &error, Config::HeightMeasurementMax)) { initSequenceDisplayHeight(measurement); - if (abs(measurement - reference) <= HeightMeasurementStableDelta) + if (abs(measurement - reference) <= Config::HeightMeasurementDeltaStable) closeCount++; else { @@ -149,7 +149,7 @@ inline uint8_t testHeightSensor() delay(500); } - initSequenceSuccess(INITSEQ_HEIGHTSENSORTEST); + initSequenceSuccess(InitSequenceStep::HeightSensorTest); return reference / 10; } @@ -160,13 +160,84 @@ inline uint8_t testHeightSensor() Loop */ +// Forward declarations +void updateHeight(); + + + void loop() { - // TODO + State.CurrentTime = millis(); + + if (State.MoveDirection != Direction::None) + { + if (motorIsOverCurrent()) + { + motorStop(); + State.MoveDirection = Direction::None; + + // TODO go to overcurrent screen + } + else + updateHeight(); + } + + screenManager.tick(); } +uint32_t lastValidMeasurement; + +void updateHeight() +{ + VL53L0X_Error error; + uint16_t measurement; + + if (heightSensor.getSingleRangingMeasurement(&measurement, &error, Config::HeightMeasurementMax)) + { + State.CurrentHeight = measurement; + lastValidMeasurement = State.CurrentTime; + + + // Check if we've reached the target + switch (State.MoveDirection) + { + case Direction::Up: + if (measurement >= State.MoveTarget - Config::HeightMeasurementDeltaStop) + { + if (measurement - State.MoveTarget <= Config::HeightMeasurementDeltaOnTarget) + State.CurrentHeight = State.MoveTarget; + + motorStop(); + State.MoveDirection = Direction::None; + } + break; + + case Direction::Down: + if (measurement <= State.MoveTarget + Config::HeightMeasurementDeltaStop) + { + if (State.MoveTarget - measurement <= Config::HeightMeasurementDeltaOnTarget) + State.CurrentHeight = State.MoveTarget; + + motorStop(); + State.MoveDirection = Direction::None; + } + break; + + default: + break; + } + } + else if (State.CurrentTime - lastValidMeasurement >= Config::HeightMeasurementAbortTimeout) + { + motorStop(); + State.MoveDirection = Direction::None; + + // TODO go to height sensor error screen + } +} + /* @@ -176,6 +247,9 @@ void loop() delay(1000); display.sendCommand(ST77XX_SLPIN); // toggle backlight pin + + + For motor sleep toggle slp pin */ @@ -188,19 +262,19 @@ void loop() #define initSequenceTextSize 2 // Default font is 5x7 -#define initSequenceTextY(which) ((1 + which) * ((7 + 3) * initSequenceTextSize)) +#define initSequenceTextY(step) ((1 + (uint8_t)step) * ((7 + 3) * initSequenceTextSize)) void initSequenceStart() { - display.fillScreen(COLOR_INITSEQ_BACKGROUND); + display.fillScreen(Config::ColorInitSeqBackground); display.setTextSize(initSequenceTextSize); display.setCursor(0, 0); - display.setTextColor(COLOR_INITSEQ_TITLE); + display.setTextColor(Config::ColorInitSeqTitle); display.println("Initializing..."); - display.setTextColor(COLOR_INITSEQ_ITEMS, COLOR_INITSEQ_BACKGROUND); + display.setTextColor(Config::ColorInitSeqItems, Config::ColorInitSeqBackground); display.setCursor(0, initSequenceTextY(0)); display.print(" reading EEPROM"); @@ -216,23 +290,23 @@ void initSequenceStart() } -void initSequenceSuccess(uint8_t which) +void initSequenceSuccess(InitSequenceStep step) { - display.drawChar(0, initSequenceTextY(which), 'v', COLOR_INITSEQ_SUCCESS, COLOR_INITSEQ_BACKGROUND, initSequenceTextSize); + display.drawChar(0, initSequenceTextY(step), 'v', Config::ColorInitSeqSuccess, Config::ColorInitSeqBackground, initSequenceTextSize); } -void initSequenceError(uint8_t which) +void initSequenceError(InitSequenceStep step) { - display.drawChar(0, initSequenceTextY(which), 'x', COLOR_INITSEQ_ERROR, COLOR_INITSEQ_BACKGROUND, initSequenceTextSize); - display.setCursor(0, initSequenceTextY(INITSEQ_MAX + 2)); + display.drawChar(0, initSequenceTextY(step), 'x', Config::ColorInitSeqError, Config::ColorInitSeqBackground, initSequenceTextSize); + display.setCursor(0, initSequenceTextY(InitSequenceStep::Last + 2)); } void initSequenceDisplayHeight(uint16_t measurement) { - display.setCursor(0, initSequenceTextY(INITSEQ_MAX + 2)); + display.setCursor(0, initSequenceTextY(InitSequenceStep::Last + 2)); if (measurement > 0) {