From b932c9e2982dffcfb822a9486a16e7504b03c4db Mon Sep 17 00:00:00 2001 From: Sebastian Nieto Date: Mon, 21 Sep 2026 23:03:58 -0300 Subject: [PATCH] feat(ui): implementar pantalla triple de temperaturas con gesto swipe y transicion animada --- diagrama_conexion_ds18b20.png | Bin 0 -> 25962 bytes sketch/obd2_gauge/meter.h | 34 ++++- sketch/obd2_gauge/obd2_gauge.ino | 82 ++++++++-- sketch/obd2_gauge/tri_temp.h | 249 +++++++++++++++++++++++++++++++ 4 files changed, 349 insertions(+), 16 deletions(-) create mode 100644 diagrama_conexion_ds18b20.png create mode 100644 sketch/obd2_gauge/tri_temp.h diff --git a/diagrama_conexion_ds18b20.png b/diagrama_conexion_ds18b20.png new file mode 100644 index 0000000000000000000000000000000000000000..f1ad8d43771a2d52126f64ba00a01b3753155c72 GIT binary patch literal 25962 zcmce-1yEdF(=Iw8LP7!r3&CL^xCD0z4DJrWg1fsz2yQ_JcY<4RhXf5Cf_rcoU~n7c zZj$dk=d1fm{dMZxTSe6#W@gP^yVqJzcR&4fsDhk0DhfUd2n0fvlz=FKKo8kKpa;L7 zA_4DQleWJFfsC0YA@5Y&()JddwN*3;kPl2_pFHU95_(qI+`YfAZ8Z~P5M%n0tMc@8@cw>tK?pUrzBXFz2i z-`@vh^Bd-Qe+3;pj%B^SqP=r?0$je4Netir%k`)a@BSK0%KZ?y{C-JcdjBus$F� z#ZTmO+5_Nf_w@hL;LmqF&Q?sA&rbQnI!G~}@z&Uuz!gCtY`V*|wAL`64UheJSYwS!}yP7&I+9tkL~NwT;u^LT?t{=-u3nzXGM+K zqKaYXMbeMfW>@Nt{rOvUFPSfOx=s;$?H$VHn{qcihq$8anPM-Ry^q7@F2{PK390qU z3AMPawzB3p8TFdImd_#wM4q6lM0B0bONxqge=mmEOiix;Zd=%8q!JHKonl0Jge1DI zEvm-hHT_$<+?0R5qSkusy?4dFVTDffWOAIi&PY0+=MJ@J#H&{ucL>X|fgcKW!Z0<4 z3SAz!$Nt)zsp?344h6Jzm)nzcE%j!nokD>pj*iux$Y!u(hvcJ+8`HxI0iHqTNf7o&(~iTGWrBBe|A7n+>*M#aG~ayK_q?HsnR z-?}w8yxiQ8Yj(L_D3%q620`MXP@S^(s{TTxaeZ!ly3l@TRaF)HS&pYwn6R<6_WM)# zRI_DtTXJ$T(QN(A(o*Wx3CXYWvVjnNk*gQ4j}8sP0s}8AI}v*AKC8>_oOER+5{4G=Jea z&~ujF>|6bJaq#Ja#rdA2K8KH$^v)cYr}NbL9v&H+X*5I?2^dWOB;d3$4?j!Ck~AEY zPOQ*v@`8rw@;%q5tG9%)*k5y<{#m4Su(gO%G8Lz76FLcp(ZHm2(~<%R^dtLYQ)zNW zMw`)P@w4^_RYo=z78VW;9;Y>p%_yP}v!e>Esq036oe$I1@DC;?`9h=$FbvOc)%eNh|2mlqMPJ&oWoU4h{}d^jsC)se#NjgldEN=omR1ADC%Ag&J?hRR9c%G zU&0q}92&1DO5CLBq`^E5wyyQ%>7i!FJ%Y%nyA_COX(+wXR z!ri{Iirm%buAthQ8a56NrDvyX6e_gPqUdbHU>DPoW=xFKernFk4XwE26tZl69J-l} zFMn-4OIxw@)xGD0#!J9=HhyW+db)HkzG}AK^7zM;yjBf7O=4E8P*|LazB^djRVMP@ zrfojabM@A_p3@-EXUE5GBbpKtg*s5${x^HGP4f=zSa^7$J|o?bE)Ny*9* zFR2!0&eq? z!-et5z`A~n5pZVEC!5)?9Q&$XZe6TUV%2=*+F}>`ij`VX#Bk^r>rb>!Cml*&4Nkq| zToh1uOoH>`_o}=^hEG6#+BDxh6CNXFL%@oR+mm_9Ln-VYr@OlOGRYf5sVIS&o_EeG zOaOe)S}PYzhI2qDUWIFqq+^DDIy;+4skbOtJc#Pm0XKQ~I?hjz!Z@sKCaStKypd~8 zJJcpQIO>?{?7E9UBZq~Ne)3fTZ^Z&k*Q&;EN@>CfcUPW>uVew^1XUBUv2p7T+@8lr1@c9+9;5t#$<72h ze8D+C3OCs;CqLA?^DUmWb7CpFK_Ka~wb&Rt~YI`tT6HRGA*i7&6qCI6YJG~k9D%@-`T4n}>;HTzZ%od|h5-+H>jKPYQS z(+xDJSB%q%i;1$E&wmcVXD=lo$W3|XEg|J`d6akc+GgGfK2?D0xdXgVA0O${iknOM z=iv7AHx9zMxq!a!*r}3Q_KGnEqozTvtrr@=;twJyKHz1H#fmd7U<+wo7iuXM25Prg=&eYrC38@zsu}Hae;V zJk#oidW#MAmdn@NyI7{P9`;`=ULDda%}vKw;PD@wYE@b=V5B$yHZfK2zS)3wb#^73rP<6jRxm%GAm@ncS^&c3oP;fcfrQ8CLvtNtLPA2W4}MHS0*6!AmMmk%IsdJ!)g4!L&fWmuD65Zc-2r5=7_YA z{E>_T{TEudv$y97#0$+n7oSgH@G*yrNg(7J*gtaLqQge1olrR+ssmgke8D&C$i{*o zqNiS0t9>^AYP))KuGZOo4W|AQ%N)QaZoFkqPkljcF9AKqMBn2=)u1mq-pw2Z!)aqK zIUo>ey_*^b&I$h$KZC&D)xG)KsWqB0_Qhho!4ODCd!1F<$()Ke`Xf-*h0wpE@QY*F z|1aZh;J5z|BYlh=Pml+xb19VBT;N$CMO;d0Mz=3PrV}EWl)`U;?ySki&C2yxx`h6L zVRc`?bjKGMSweamd)?5E0JY^evsft!Y8I~Ak>5u&;48wbnOhoR7%)>hdhJ?K zJ?A8y+49qAw1Ofg>5k)|L~3RxLWsI(N#?)6NJ^{x4hPh~Sf?Y@#mFN$_{g=TO3*Wn$GpV##dShSkQYnvH@ z>KM8L0AJT~fx%?SlJEwv@)l=(f}3XM)kMu4$@AJ;Rn(zXjLw&%n|{LCrAqAK%X=uxS$?Z0 zyf7f%HAc%cyPeBI;uQcEgHur3p(p-p!i*Y*z#)5slad#FvbU2n^33X&-8ni2MkukT zX}?kI6NZ{v97te4q2;s8iz*zWsM<$N;1CInw~k$3c5MP)px;z{MnmV4Y_E=DW|7pX z>@+&1*L5%;Bc#7aFOq{Q%D5(n;1(xtaIBWb(Nfzl9R$qNPh}3DcqaLF4yk86Vw#HC zH{I6FE!e?vw6j@hv+2_Jr9#)1?c|}Gc{m3F@!T7)oy-dHM9`kG{wB(Ql~<=0?& z^F)G_Qt!)n4lgMZaTnhq^={cu8aoqEo3DWDzYgxQeoTxp;jnbdCSnI16Y5BR{>Qf7 z`vVQiyzxt9zt5c;V8P9$&p@DVk(`pqwqQ_qq;rMq6)_S9qa_kZC?=rh1xoy)GTVLL ztq@Pm!a7Hmd`0vOTd=Yhbq4ij!}LB+&-(F!mGz6ULe@04Z1g1_@lzl*&v}rHhmGbE zwSBn5{qNbiC@5hl>+QDd;fwz~vsW6t6BbI;B|qW+l(Sh3)AvTtHtQ@McsR%d0tK$L zI0V6ew0w6*5OaCD2OK{EWhq#x4^R8Tyj#j)o}{AkaS7>7C;0x*2)=Kn1FU}sHQo<` zf6w47HClURvT**b^dU%rI;an?jl7+tjmGhL^*%y>@&V{;nEoHwQ*gqW^Sq}Yu!CxG zBX+4J2C`~oQtm1!1Y*c?-dR7&<$X?}g!#;TNp*$>9S=j4n8oW_M!?LSWA3d{MfUNu z4A{fv=UY;x;a9rnaqu<@)##bLm=D_O4=~srRs>Pvutsk!S+K~J;DM1nt1+P%47}9d zFTqpHa@Ha&oXZS1lGC-7Sy!*Gmp>UizBdxKW0}RFCnxTSPFi}htTpV2^M)3`3OtvG z<}0D~{WCN+o~>v8Q@wB0))4Z>K9+mkr%zrM8-wS!O!@|rU)T=XPb9PkOQMa`N%}&l zjVj2rW`L2}oY@{sVPG z${io$&>$taiHJ1Xr}OD!w#ov#2@G>7d=UtZ`QQ%uFUeK`CJigTH|g951mU>&h8Csy zWw>f?^vXRubIj~$<{hv4+DfX@G9FX&+;cWU%$o}SxYtJmDP!{sm_-r01IHirylA|R zg!dKvdu^6HX{mBH*qyvZEh38K#F zOyT^RT{-vHCY+Zzn&XZZoD;&8uLfn(`&e6iK)%vuWV%S^kev3Ox=f|gHMH{XQuGIV zk4ixqF30<&?wL6q;{fePJUvB*5(?VO`~V;u?0<8f+5IkD84^;c?&$9y5@J@6>h#?% zJDC|Hrtz8W?b~HE^&$tl1=kGoh@$<})_K&}sj{8{{lT>P7s%b9MZ^0Cnr|^y?LR6n zWtNc9#}r_Bel8XIgH=Lz^1X{gC70IGr8r~D$99ibzo=9)7|0KO@nVmRW!p`UYJ0!z zFi&__Cl*c}?cj)d5%=K@t&THOP?!Jp_(5JlOBN%zvhQ8M;OnwZ!j94b+$85L;LM|9 z%%FWo_jpGZ%5Nh9nQUFe@tJD42ZaCVnSc9(hW0(RpP@Y?55fq8Y!2%=G&?~<(g^no zt~qq6u_cVFjOnJ@y%EEMgHDq*OS5(66HVUeV2k;}+b@TPZ(^i@ zBqzOwUz=tYB)Y;gbUaVD46LkLfKQPH+Vb-$2VRX-)9c5?U)%Q`)fN?*dkDDw#JWGu zq!gyJ5){7<jTy^4Hn-%Gxui`?v~i-w(m&VnMK>A`zEuM<&^F9I1eDu ziTQ0#p8DQpb@s|dzUD9+EYt_W?s{T$!jq@fXduwjMD}(Xa&kvlolfVIb1E?10H#4@ zQV8GvqM-c_A;ysA*WHUE$FP19!F&ykB&?Xgz{Z&D7^yV6+9j(@Vgm#-p z=Mh4jW7nxCH2<=3D+O zCC6EK+$%<1XIPzS@EPOYY)unvT^0h4ImjeR?FK8Nnihz+-Dc=6LR7N78Z(2?GY()8 z9fE|^m~UYqkdH<~>7_M$dOA2)S0jO1u|O>{UOrQsNjII%ebUdTb=*>|L~Xqe$hsZc zy{Gb29qT@~B1}(4;)0_H^*U!lLn0z0Cd=Qqcuib=Su+AlU4J&RHWr1vDJv7Cpv0NA ze~<_O_gb?BT9lW+&{8h-OE3Sd;?{sR@duYr`erzjQj=wq`EI{uufAZmRA-{mnGFh| zryta7A3ccsJ~~?IY%xWVGh1uX)$Ws9b-vmg4bTigj#~BR7>$G<@0u;1q1|<GrtjF4vKey|_>h`(~4u8fN0?aI@<6_Ri*U$A8 zt9orAJF~gb{@IFI?QjveCu%BNiE_mQ<_N?Pj43ArBPO)SygtJlH8LVrkfKT-xbisf znOj)UzIX*jS^mK#eNSm0L z7)$ct>8_+97S?)e#fkxp!z^O0)a~0JH*s59D(!Q95wlQNVnTv*NzY97J?9g^avZdH z)A8~mrn9{v6faRkJ_Zn9x^bG#@1B*xg3oWpYTW6-7BfwI+1||~ncSKsE<_v>2ss^* z%qK*%2zfV+LIrAyoHL^43xN{>4?B2ZbwShVZmz%+Xc7ui_JQ+tiHPUj;koB{wj-zY zDL|aOdUdpRaO<1-1i98??wv8bcg@Js5{AU(n1!qXlsEiVqM2U4b>ME^`>oOHlw38D zZkd#vtXitQE~@e+2cJB#nrXpS+qqnGZt?r*w{LzSIT?ZHJG|fEG10KbkpWvMt$O2{ zM^p%yykqT!2*hP#m!V9cKpb3|)VoRNVN}$0{ZYR|L?ncojiSN3pnP^myz2553n8x(Y)q~j|3RGjLZ&`kgbw7Cm=~_K5E0w-&cAUHQb!|!)R37>+ z*_fiuX!<%RN>+)uJ{+Lp*O6;~-+ypJhaS(Ry_Vq%{nZ{5Msz0I4;!)gug zdk#QB0H4DKAD4hUPAYV3qgHys9RhpB58Aw!6xCSs5s}z zIFXN;@r=p|A@=P)>~GeOmu70K>eSG22wyAYYr8*u7$7dN2;fuEl>Z9qy~7`0&Zr(K zEtR5dxjPbZB;My-b8sJ;Jtdw$=JDNdTeymxcJLQ^iHC7UaZq{1C>nSUUrZD9=A+;$ zbT8oub2z>7Y9nQ3UF}H5QfJaJn`tWR;ZbP$Uk`D3!FMVQ_smF8Q#-|HdllMk6{2btL5w2_Q5*sH$1@a=V@LNsfBuXzCq9~FMzFVpff8Fi$r(t&(E4Uf`y?gb zNY7B~*1@KsAv~^ol92EIw_R_oxI2H~1&ubq+EHErC^E;AtfE-1tH&5;Y-~(3yB+eZ z8^?qQ1S%A9IGY)xdJvgf`|{}{h!OmEmrXpa4i5`hn-u#i6V!aJd-Ir1wT?We_zVqz zSm$w5iPL5UaaRTUzt6$<@<0=CK7oPjeq`jVtiM=Ue(}TS!8rx*{BNG1%1XFpLet;8 zd}<`+%cwrbm@H$VKGbFt5TvQ8>1v}(o)g{IcS^u9im{W*oZZk-`jDEm(wV5OpwU_g z;uHCZ6#bt_b~113DfsBHsXTEpd}U)9d`F@hOxQj_s8 z_9|c}6mMi#ZTX|i6*%6$an8~^*z)@cm2=_gx22@0WzhV%UWetm z4$k%S@2*CRA11xg?^CVE_c9Id=gr;|G&=S|4bJWb{YM1qpM^q z$|qV7Ge+SD!cg%wM(C%R;j=|!sW$osMymla6>0)9Y@>Q><)gFoPhN0g($ACjN7&)4 zpO7RAqVmd@-0p64gaiN}0)ebQrzyH{jFrGezz*d$XXgni6|sf-d$5WnRT^cMCch@P z%oNRRnXZR-`;=lNQ>78svqv^R@j*1}n zx-H5MChC&5|8d7062ootoXSZ$Panr#vCju|ynjPMOc%mvF};KyYSsVt+BFo1*M%O+ z^w(_EkIGV~hO|UR5DzZ7(fqW99r8(>i#_?G%b;ln%0h>t9lI=Va`oCP49pKXSXj#Z zJp6MWSEKo`MUw~)iPkJSA!`9y4R9mqTKD{UQ7|oN4v&BF|cC z1Mvge8s@7$RBqt!^1walJ^~|Uij7m2k&Os7v%4#|>id1G1Dh;gEbIGsI!f1d*f}Qyovc@ zK~KUQM5n^6#e-=Vc$=MIyGnI3MVPNl(s^rkM{BK}dyB((hEm2I9llCS$aFwuhlTIs z#m~6LQi(EBa*B-0$CsL#GgGz;#|IwMg+8*P?ov_~PF8#vsx=JfI}-%pqZ5xW^FQYY z*%5I8WvZQrS7n~Mi$1Efb3l3U5dB4~ z4=kfvdE!otIwZ;+=hYz}JHx`@AlpbU+GCV^K2?bE;PN_Z@4b`7Qb*tS{qLQA6=#QT zicAI})a>j!6qrS9Y^?0|F2x%puZ1C)MJKQ%z99X0X{5|gbW9o0IvPA8EP2Q%^PzE# z&f(f&2(`ei^>O~GY((U~pxZ9VO9LC5_qiVI-5*{D>BHR&UyFU)T)+CAtpLzx!1mJq z0Oua7qvO>&S}LiCWyn^TE_M$!Ynol;Ad)G=S6%A@_zu(F2{a*H-4Uql-996=AF=i;m{=^(3k=9{I24Cb`Hny!_ zmBva+QZ99nZ<_;a4x*h7#?}DEg>w-jlJQ3$_~Y@Z9+7f9HC$;i$^)ZwF+4vjEg3`c z8s_Q(r2|fDqjvfH!N%T5yGue$%=Z4FkoVh4`wGKV-RimB1uHWB7~n4q#b3T-Jh*@J zSXNdBSaKMj!cT%fs>7?z^_zyDSFr=@=fv2$uAjC3goXbCjF%Yy(gy{ed#c|v*cUXoWZ;9u|GZ`y_p&KxH6C_BXG_A zH7LvVo_!U9#Q#hD#;xu;Te}KZv*y_;Bz#4T0mvnE1y0YG>aI`QTTp@DG|tb@XDyJA zk!ZU{>KIw|HRcpUBYB@KeA(iR3QGG3B=UY0pMnAt8MN{t&%)E^?s#0Urn{e;?o5?P zL)x$THipYe?q@ug&wf+Slc&vPJ1m2RwaQ8rU0iPrW72-xyLW9qGB*VX_kfY?B#%gMp1Vb|p>W3x;W%SRD!ZYA*8^|o4^QT?X%32`*1&2Hg>=NTPt z0=?$ZemVhQkxQR2P?nHB5T*R4v!1JI^E?Omw&$*Q%_Hzi-@v2wdxhJt^5arGWdb%E z%{r@OJY}k-w$(m0HO4eH_t{T+J*OmWRklIoo1Yema|_JXISz^^z7zZ zmgS+$uKVNP1xeqMEf0+7g^K0?q^j-rj=Ux9NV=Y#sfr3X8EEZ~MimUM)K~+Apb``{ z^%avXs?AzcjH*T2$qU8?R`dXAwdk}|X*~eQhA3q-^3C!pzv>X+LhD$PBYHPU_xtP z5|nXyU%ZqejF#GY7Z`OHTTL^Mjvw%QIpuEJcu&EN*@quErjZR3R2%>H*UxtH|(~;Nr zDyCwoYboDl_h&*`k?xPp;|s4gDXqGyt4M?Bn}r}Ko^gJa>N=OheKVrke!g864?~A*wH%xR`D=pnE=f6^`DLWVo z(ZBSt0cE|tkBWcOjev90(o4?w1dUd&*mK1r{4gR#%Ow-1(FrGUcaVW}1E zmgkFkhl30A?lXCjCt1&_A9Wi_eVBabFXdEA1_ny4O(orvH%Csz*59HLZy$NJPp<|+ zj22)z%dU}sQl0-&3H^O6rT8|?Tastqmue{Ld{bcTg;JuyN^8=mTlBv>VX7;-2yb5T9kXTVUj z(&3Lr%6j=vcJj9f_^Ey2ktl9#wSg#Xucsjv>9P3S?p2H_(eN{n7xKL0naI~RcnO@$ zj}aB&HN%+2NvCMeyVNW(u5uAD!jl%~mDnlhg{JU=l$*|Gx|{X72@{)gkO#OU=rd54 z4x{qI5?byoRo+}(&4rU)XBpTAdC5D@p$%WMWBT!Z`v=n4 zNDOEVMeV-+`@iMqzkx!i5V1KTz}&qsw3p`Ie0U(C#Xy}q7$ShnIpyf)UDi*&W^DYI zHeGM|vOjCqwK|EtYL_fK5wS_&NqLR;gL62P$(V+nIo5XG)^VU$C9mUPUs9y`jkIxm zENxPA<1XwRd>Vz+87a*8iTm6Iobyg6R{Z>yH0B5*Y_h~%qC6!{EsVM!%hw=X6&m9; zVM8FVryTj_oBf}J$l)D$9W_lcIV7#v$U2Q^>1K)+g9+7%6K{cv=Iy-Ml%ypA-F9HR z-aNidMN7SEKn){<;At}8rq^$HDD*oAr#d%P$EPv0mh$v>K9~22n5auh;v}W^pH+l; z$B{N6ray(0G8jGNeS@B)J5w$^xxa32-J4XLxORuln;7y*=9l<)KRhU1!jM2M%~f$Cp4RPQoL@-Yu;)bWL_y_q>rk_fBa|>T zG$^Y`rf*bS$}ma2#_1CXG-{X5n4>5V!?^ODgW=&++7l>#8wAF8;EmC3hT*>Ns$4UpIe!wUKgvp=9 zI_mG#6xHZ1bXs6hK_js)pz>Yr*z zgu#DTF1Q~h;bU@Wsbjc2WIRjEL!~q+L?&V_tW}&*a~S_dm)a=AOa)GnoL11Bm`+uO z8{une0;U2xD$iUW!d=q7nz$ai)wtVSmyb=}W{b^A`62Ej(9hzvvn-eA%J>)Q)hkvv z0;xletGy?epBdJN-s?q$xG+T$anxfrpYO_sTi{gN?E2htymLEC2$VT>D5RCw8>@Pg zN5$DTm~e1^0Nm-Ad;y66Hr&UEs?0EyNA5w9XM7-jr>NNag%ln82zjLciRHGQdBgsI zt?1P-&1YXmEO;+XU0Let^+OXKMd$?wM|&*m_0v7h|4m)Q!?_2JmnLiWey7a^DO`6E zb5ej|x^R?sOYKQOpYw0+CiiwaGDBtxD@2_4Hp(g*9pC7KvXZ{_|AT@rK%~)>%&NyQ zf4{)IcEJ~g2Jn4kYKK3OIKUwOt?xW*4|yPp{SUO<^M3Bws;wSA*XFWeIYgHjPe5VM z{^Wl^N6Y=UjtVWz%-U|OWv?$OK^N%%47}fn|4B;>GCS=n|bJRtvGI)=>m?0}OE5AZXdrI?e|!?Ox+AyAFIzi`PFApPSXZwO zSc`R2v+jw(3jIkO`m{473~@*$=P5sm-^bwl95sw;Gf)0_@~x416_nvj(xd8>J@#C7z zU1_TN&-0f;BlzWrs&x1<*z;UST2AqVQ zvht~;2ZE~QnCxaBkOIu)_JiBxppIbR;kfodpOOD!Z1yc}d(AXe*GqZQ2xl$2kzux4 z%Zo*Xo80VsuG;dev4q~|J0 z69fS zDAnCO5~Eb5;RHhU9FyR$d!2a6H8QF8P~`H6i*sDC!Iv36|8J!hx$=t9Y-oN=991MY zf|@R3NEH&}XrXnrW4V^7d-U>etyKqCq>hZ}s`KPDQyb-IRS*-Gd+1tOiWB^a~C~ zZN#s(NbY$$zm~TZlWhC~GL4_8UP_L|cDP1`@f|cNarJ=7iI+5F(Z)o6{ zB+bkY69e&?83-1oXY$l0dOmIC{lND0ax{(ZuTE2VaZqY?unB4VWw($h=)28}EJ*q+ z@If`&?^K72&D4Lv-wVz6WVB|mL3m^{t z8%7<3zgB?UCwKqN@c$Ww`-h1CkN5sR8ho!7PII&#-;5kaKKUE%0@Xt#wc$1iFM`wy z4z#K!;hB7Qd>8mVDWr)Z1phEh_KhLt4K0-ujNqmhFhbc;9jLD)$D z!1bSEb&znvBmh02qo+VIZZglBJz;eR6PwVw`d@M$&*8T3u2CCbP)U6we%V!%n-KXm zLDUJrRYb8xaDNas7loKMy5Ll{)ut^cSb7Z+3d-`nKa396j2=SIjs0@67C<*6d8%Ox#NC-`jNaCTh| z?s^jN2{8g0?KPj$n~;812e@?dSM-&B@70tVPJAV_fIi0S0&xwpT906H4R%Hgw(>2sm+oFlTvrq(7df%wAp6((>WMhu|ic)ycMp!OoA| zF|TXn_TEEOIvYJC%o_HIIjycNCnIQ@U|$MMOSN0~H!E{%RFk-Tc2fj7jZFZtH-cy+ zag-V8-ud+@6QHOPV`GIOG&D4H>JhD$4Mgv?=0dYelqb&Jw4jh;M#&GL zTlCUI{C+3lijr~zO+71%H^}&|Wt#Qd^V_)uQx!Jzh5jQVdPMK`-8^dT%_Sj#uFu2G zZF@gvuiU+wvZ*BSQ;>SOw&I+wI5OgQ0a;k0;|4>lRNmP{F)gmZGc=&HCZR+wH=`Zk zv}>FWuO}=ysS`J8k1j%&hLP^~DpNyhS|}uEj0#PM%k$dT*ZO^fTVu|rn~HZg9uTLa z@fnW2W&y9Ydaphb)`Gio@|55L^^%04&_dDUeAm|fl9L?5IB1+ReYOHMRHF9r^7BVa zd}4rP=xs!l9J?3?D|<}85oJ%0dsJFV!r4$Ia7#a3Y=6&TpY)64EdQH2>pSuFC-jz< zCA3hw%d5*wF87U%vbp;5X6yOudhOw_=LydOk&0CE3)9lB0m?a%PCX$+ATPi8Jf{Qk zU8GTaF~?n*h`dz2-o?!AQyu9@W_zJxZd1E2DaN5&3j@KFTskx7il$Ln%5a;{tyTJa z_ViBjjO{HIeuvzk)1kqkjbXhOgP9y=L3yS4O%4`dDeSOZ)rYb9;ENSn^>rTHoo}1# zY>kf@1~A{~wtC1y9{9g2(_*IrCr*|jA!SlewXfQ{?(c5*512G<>+)59eS2rPSh~mS zVp(GM`{!t5b$)2K0eEMjgtxiUa4IA4`34{d1Ej=_mn6rgwo8|`!@M{skEL;$G#V|^ zHy1pWl4~3b&cbvW!>kN)EGJNb37v#A)0v_uj_7gmavxoKo#B&cZJl#{7|J9t9nm@& zIV(%n)MQDv+St8>)Y$cfom#5P-8v&-3}w_Sa1YC#^E#T!!+$mwIAnXsbOXZN==-Cp zQZ$8&5_mUPnd<#W$?&Cm{eUX1dYi}gZZNv!vuB~tUont_(d8?ujHQ4E+_${ES`?Uj z3%=8^jCuzzD1Zr6J{NXVFdruwdJG1gx{a-6{xEG;^_aVbTI;QlN^T$I`#pUC1TM7L z3TB&4Lm9!3Un$rDZ~7q_Z>msHd_&Bg9%*6x)hHjV{?n=S|Bu7C7P4TsA%BSiWG-co zg!PBS%79lO5dZ3y;23*i@Mf{kfq^)Y=iMg$1(+{{4D;zHD%Wx!yhBKyaN_sXii`_! zEk{G;8$u+ytR6r+Nc_-UN0U$erL>RY-&6^WwB*{s3A8yX|c;< zKf2U@T3XfyOND!ulm}>gycp`27#NfgJerC{hwEXD-pzjmtYnkM4TOcQ!4MV#dE&mza0SCMmP z7mhtYPpie|UHJJtCTB+iVvL?&Aob2hs_s~(Rx&idczSgB;o+p^=;6q=$fO(rCdP@~ zc!X-xL)s{hfoo>jxpSMJP2mQK3KR)a)iZ+;kqWbQD49&8VB%=?;?gpe*z}SB@1vi& zqMNsk7Dyn4K|81QQoj}t48B&$v!t;i$``Zt<*_2jbtrQv6 zYi1iAq@B8(4ua{6th)mWzpr&rB_-S3D_Z!g1gIOQMZ>`<-dq9 ztodPxi$&+)_mLHuv&v;A0mZ|o^Gt+FCtSZ1xn1O(@eIz&GE>u`+#VbF56M?cBTjAz zHaV>pYJnNP=uBH^X6_mZ4*}$4Mbz$i#9svb%1pg&n~5tcX1@IxpWpkrbbD??%^B)R zoKGnDhDINzsdwkeX^fF~lvtGJ9yzMAm}_2gZl(E_k;+l> z9D`;G&S^6_RE;bqrxg=SGj9HE+7ZDaS;Z1ceGy~Y`ikhWaJCHX8}O8f@9C9!|CJ_r zJmMFZVQ8UPry62%#Q0&xQhQ*eUGH9Dx+A}U9%A^-;anqv?@`H_QcSwGf+KcE#-5J#ag6=dl2JKH_iQ~>vi20xC7K# z)6vj0?g`6R1UR#G=Le3SoIT=#T2~`^six=6#MdR>nVEHiB)nezO6v}ybdl;BPp(;} zrdF$>o3$1;i12H-hZ7p~xK6W9lr{{bWBj7@L99p zE~=Q4A5$-aDBC1;E;=iaB=ukw>PA}M9t?Lk4e@FVwic-6PTuNrPOF03o2PcztT*is zlMK@-zqe&rd!Q+EAJ66#wTSbo%k(X4|MG@}0qR%|YJKZfn7b%9_rN7Kt$LpTB@1Nq z;Aetp*xpw^WPWiOm(no`z60r?FMBB;wvV8KEuKS9B0LW-hy4d5YWAdkCx{d@=fmw% z9dK}%EZMBqUAAo3QRHu0EZEF0`PjZSz8iR{S0To@AhGv?ZX~UA&}m&K6s`*`R}eLv z0%V~fBw%hV6w&HQl9fZZZSzbtt6Or0H;sy^`?aYx>}(vW_>BcE=@~+!9f!ivuX!ud(pF|)G{O|n2Y#%NVtjXKl%a&SL_9Tl-8lX2z)?F$zkS@BzXI*T zxLliYsonko#-)euHm8l+fklYA;!~v1(eM`PHzLRpFMK`ZD0}fi-KbQ=OR7hz(#BsUN@5;dn-r?3v#m{5cqYhA}7S%wtb`&a`Y%qjsB8^V(m zThf{=Mhq(~_i@o0PY8>(tvfo}*%B-%uHlG)k=5f6aR`#X9gIX!x~kfZ&!;v86}u?Z zL!(HgV;^c{N@Fvr--jp0c;|qK>aQPfLp|QLWZ(Jes<6NT3i-&mZQ)y# z2dYG_Z2VD)8A=y~l|EO7t3+p3wD%BYS>j&CZH>yWj;_frLYLr^bQ6U`Hm}dJFvqrD zQFav#@P2=Zmvmn&NpT2X7%&fsPkis@^yED%Zg>5GS12e({CfK2ZS=-;HF!1lxv<#) zPkEF*!AzWglu&u8=%|`QwUQPw$Y8m5(&n9v&95{JkGuu5&Xp)=DF-P9G|w((o&=2ia#+2L`Y4>_ z#7%Xb0y=;?XK}0YFBNr~gZfOk7Rr51?~dmI0V*~xiGX<^KBJ}jFRjMN zXQ=y5#_Vz`NAp-@%T5gmyhy!;sKy{hnDwb(|}m zlbg#la74j|N1e1F(4O)=UeYA|>x+NGf zPMfr@@kDAYQC> z)}+sG?Q_Js31z*lGE`IXWfOQgT_eqS2c^SG2^dM3Lwdik;fv8}w%iF0BzIh(tNDBb z>Bq_bd2X{h?GF5C-I_Bjez-%!+jzVI=r z+*IX2=1TRphwraG&NEaubN^I3)84G|oN@q^1LCkO61hkpB~WAx)9SHAJ5M_WF@0{E z+47rPD8<#-@{&^*_KK!H!{6m5pp{t9hXSB(4ExkarR5A&8(1+<;TrXHC~hOR7IE(9 zoiqo6e{?G1@xUqI8#iq)FW{=t+meq@92_7wKIp`usHv{XhwuycPAKeVNyBq^yJ{gnh|KLv;u>vfIU1)#+45hp4+Vt8Y-0e>mm zdNE~#yxC}=%HU<7|KYRay@k^4C}fHbq^F@(Ie6RW>;4;%TGy{1osKda{g_JVYMEVP zrPRtqHJ9QG=Ho&p!rYAy?W(~Y>F5sP&V=+%HXX1+X3uBht&$Hw)g_ zsAR72UF6|IMZd9fC?mypa^lw^>fho#Gliz416RC}vN^`jo1X3(s2AfdIiC zBj|Xb%OA&ddE+j`V5nGD)MHt8J5?vN-M%WA+T4PRac#Dj!Xbx^Vh@n@K`bLHKWWif z=Bgxp&wdXij={BezYh2+GMycZm-d;!i?>`D!)Oj1KW#65FJ~KfcVwz| z6gKLfB*V)yQLc}s)ga$yO!kw+Los!z{h47g_vyT;o#&d#DpwU$r0gp0@W)l4Pzc|a zyWfvfuz;1trvpo+9C$MH6R~Dp@1|pr8v_q$s6IIzTzd}=EpKDG)R`i0T75)}HC@O( z*DCg!ptAjB_(xbvOP2ivR&;8q#IjqqqDo@Y_jdu%WznQxT_s`i8U%w?1sl22d4??2 zvSEIFpL~o@WtSbk%OCgnV1b6(5LbZ*O)(pNY+dSH7f+VGiJg0Vf9?y6Ygnvd4^p1h zsyUE6Tn1~FkcPJty7b0c1jGbevZ}n=yI{P^3gZmR__mAa!?N(TS3gFt$y^ekzP2$$ z0~|PiTzg*hYGQ>1c(k^2uY-`(p|741bU%h7CaK(IPfC~&4{2sGunErn2lFX`;|YnO zY!fS|2mDSA6;*hY@)1;n*Q1jvgdGHb7ik?-XyMF zapkBokq4qLfEBeUBhmV%jq*uee9RWawz_f>{e_7)CZL{Xw&Z;%L1u#2!9hE+?bzBa z3G?8!y2=%oW&_+q)+;0mzG&X@RqpXx*=KmNm!BS7yJq=P|g9 zo!?g)L&|^H?yC?yHgWvPo)K?228rJm4#1mcJY?mnY)2i^Kqj=pA|6SL`1b4-Wmfis zKi{fRF;~GJpgFX;R8ycFQ+>N?n*rNT{hC$)V*ly(LB)G?Vsxh}gCRFKza_beFHB2d zN=4?`UYyVspTiTSs)b$~@v=0Waa}5$b)8z#l> zk$gT$<|60mpEucchMY{TC3QI^#tAn(6Wu1|IG+A8A)Y)vnByF}&=8YQA$btwrYx(R zyz3dHsofLfvvT@k)i>UXlBt2#7>;Z;uN>$}IJ zeW%kNbk}2?KpTDgV{(Ywe>e3+Qs7O*$)i4u1s0T@XVclthNwc*o36Na>lpsrZP3~R zx5zn7mrn-ed|tG&`_dvg@u1RgM)GfsJ5KJzPkskCq`!UZ zem`YOdErEEyBMd7rUh)|%x%$zW=epIRt3q{l(^&(LL@v<;Z_24(ZCFmsQf=1AaXyC zvfMt`mp8cf@Rrht_uv5E((%j(0#McpM0J8ikaQxeQ4a0Ghu}i+vsWoupPz>y(58MY zeZR1hYwtBu954xRRiyUiE0Juv%qu`ziIr{JTF~(Z%ftIi0d=X8Qb$L3CFnizyL9{r z>_H%~(FjhkJ&K08ZrZPLW>0W^&kj@S6fpCoZSZH!*kOl2V^Vk#(~F+Dv}X)LSxaWZ z4!L#-g0W>W_hSqpQk-+kM4h?W`ApxPy;rS^6<7ZECF{0{9>)|;b!(`lS%KiHm)qE_ z>$d_A))s(W@N%#6Wh9l%iZ<+mvDDJ$RtxcP)eA=Kcoi#nyEfUl*(P+|Nd>WO&KhKH9PV_Ll{)9!D0bs!a_CUZ1>&p4-!nRY=`%)5XZ+3X9 zgr;jDi)SAb_>N*C{FKHkt9}g%__BG_AOFT9p1-+^_lix8c>f*@1ondE_Z*UZX@rtf z>5Y`dXT4i=w$vk9D=J?i7PYGjlSmAKosGqK6RD$vBt^oY>QydPDMp_}W}Om~!J>yC z>Fp4VoH{HEWLW=mbwfzNkp-ArVI0U?*i)y_EnwxYBzKXH{)Ei;lsBVwkvP~Hc`Rl* z>@YHWb+^dqo7@uIF-Zq9SPj3#3-q_fo|^W71O4CQvNdT+xur4dUKeTo2#-u}WTo@0 zt&>}D#ORH_Mq1__CmX!cObQqqaCojEt+Flc_<#xwtw2P!P)pbdAwl}_b2!RjE+WAR z#NauqxH^#M)>@bd87wPmu#B|6o7ougQ&{AP#F{#0gx_ijud%dAIo= zS%|E-XOXAiMEBGIv_6TEVwNd}Dy+aNvy=eh^|jz#U6U-!o3oHXtf!2R##mTdFj}y1 z+BUa)qg}Dp#T4Q-@3vGN$uNc+ysVrbrkA^O1MS02Z+W}=0+!q9so;sd%N&75rOdPt zd8aDqi9%JOAF(t{=s{%L_sN-9VSNnW`N7?>!7a6C`7XW^b1^zwykdONc48Mf%O}D8>_&6S*~AW+m;ja;q1f7 zD{DyEqqHbbcR83yvqV3OlOR1vhB4ZGI@&1S63bfRLDgZBZ0Uo{D%?s~BC zW*9e7htaSz4nuAR@PwBU&bWAPEf6oEQ_7BU&1JOiN=@E&Ute5!8lM6#GbZ8~c*0K( zqiIedo=)v*StQkPD%OTU_{~(>0Zs-)q(YObTK`&H`25?~Oenfx{{pYAnJw2yfHG7S z+(yb&0>~&>&ArDm*t8@HU&4BNy3tV8^8lqMI5O2cr)D|UF9&(&*?9|!0^D7d`B)UvmIHxTC^9~(-i@brt9F-FMiXsbFyiVC}))gK#*8o zAdh6!+&533Bn;$tW?D(tRZ9NO9(nlRqvr!W=06)A9DJ2fq)2=AR8VZzo%3g3`4}+% zqZb$d(u=~4)S^Q?U-;7Km1>7bq#if(g|kOF9AY#-8jTAi>a14b>*-t*T#=BJaMCSc zl!1#=`N>L&Mds{^_jgktXsJ# zD2iHUR(zMk?ak_Qoeb7%yO^5#o9<46IevdoW$7bU>+)%}GLBYg0doJk4hpqfVna4{ zs>M7kv2HBaTFm#o(8_yQaVaL6ui^Lap?CXoYn{;{hutdP*4+~0cR#Ymz}yfB#suf1 zuR5^GV1^Z?=a(oaP?v5EYN}_wz+DuKYd^O(3Vz(0{@l^hY;QHk+7^Kw9v_c!9+SSh zJM@AlAb8x<$-622ti0aMQzi?>`zgNi?I6atJe+KMQ4#GHc_loBI|XLgJq+^Do#KmY zps@%eId-CRjHxql#V!flK5|taAr{w|KL?o>CE@5VxRY^+_{f*F^{@+?4ub5o0#ckQ zDCaA-2J@TTpn=_&-b~S#`}5UG)qxdW)H3U)y*IjAbE!m>r*rg3{!>j|W?w~kBUX8% zXJvBQTtr1Sm}ETC*@1WaaGfb|0F91ptEh@L5rhID-{DK}V?Mg;*-Jl|+#*Bc`Z^kw z-MaKN)xRNXil`+$-0F0IzEcch7B6&7p8(pZog1pv$q7riwZ5}$N~+D*N;*t%JfnDFP!1)?+1=_&HL)1W$n4rGvPPom^YtyY#~+sbwt># z&-)>Sj>f{&g8ok|zri<{W9{u$%_@WTfmycRt)3@pQ5|A2yI0EURxi)~wzjJNCB|<&eG;gS zkfmnhGv5(D1PPConrKJ2GE&`teRQ!Mb?@qJWoPc#R&qQ_es%ERa}{aY+tvG-kF<>R z{Zh>KZ<%O3L>fQ3tgB{7BU`+1(X4zJwhy7h%~v zkgBt^HcJ-zjG9{C%YNl`&D zZI6jI5}A!ysMdr&k2OYD4r+B9qjWp4RcA*87oib7Bi`h1VXid}zRoy9Vfg_`KfIh~ zkg2Ok)0T97$%p1mB=`0!uNm z+*n1buT1#xd)=et*KFdkM^Y<@-hl?|X;m5bVoUglp~hgR*)E}i^c_}U84Q*V9GD4? zLi8bV)b*5p2I>sez`d6u1@S#|GNs1aTcY{Aru-i&0??n0cN2Ne$~WKCse~CbQ;OV^ zom+dPtBa02R~Ld-hth+87wZ7v@k!?sVNMj~>|>MU&B5*jJuyd`PrLv?zh%ITwSD>| ze}CPC&z!NfC(7{TW)c`C(giQkQHrM2JFxDkeCr<_?dtvHa8QX%#ro|pW|;ipn_M{m zXp@EaQt)rp?FVfBCn}kD>!j8~&MWb{cg_SuV>66?Y{mFrO9G1)js>AxKa;$mr$Yvd zMd0X1_6LN`2%AT8O>Au3M=RB&rfc%?m`Fdeb>IED^WI%z3Gn{leTD8q{~)kN$Y&DC zJsl_th#_C&J%8V*6;to}xcue4Cs&VBQ?~t-RATc0eCQAsmgZ_Ilogf$y4yh6hR>6w zay_G00Qj8V^ysx7#8VIe4a)ug(+4GEOf&%v(5aV&vG@?K)Q~Mt1et>mRR|lGfEabX9rJP`DCBZ z1t`oswHx3XSFMWs_aF_c!z@MmH+H+x?z>AIJgerp($2|4Yu8*;3Bbm2j*V$>$<^k1 zWnx0mbHCn6OkE$8#7cJ@oO!HTzOuukleGIL=J-!N5ci%&acaTsXFaVudt2AG+yM_7 z75d$+006m5fOpJKV(t5KORk2Ipo)QkBlTLWvu#B^8|qSpu43K|^-u3) z#QoyxoZlYP;i#7m8xqt70l#`^`HT^TN=Z4UD*-`3j&VgU>tQxYr2Xv&NAgHnfJes} zmDn153!MKj9`zh>14amfbAAi;O{g>CpdE_e?~r-GsFh<|>Tlz<{<}L)g9SQpWdGZX zyAd$lub*FME;V4w8W7N&&EaC)*w2#GjOPi-lo&2qk_8jc`jl#TZRCZ}1D!`UX}hc8 zRaLJC2hTcA0eZaOi823&0wFl4n@of8C|=;;$yO;E(-@YWaDN@vvoaXGld~Gdf;AS% zo)%i-ou#cQ0&eDA`3$Nx8lP!SvxGx<+FFk?9NpB<}fk ze=-M|eYYdd1c&$%27u`~^t`cqd*##vF&Phn)xquxw-u-9`!-XM{x{gp%QCDe;+wAU zQ&Tl&q(Rf(QEhzR*weC<53&0MWypJ&Hqmo?i?DO~{^QFI+i}nXmMB+X$cHve>WKp| zmE@1^zI-@PzG~RqXvGAS!Q{_6A;LQ20hGFFf5@EiuPH&~Z#U}ywe#!$uLzP>LZ0rz zCdRW6`fLk8;8e5`e^>3^;1&I+pcg1V6ds+Yf8z)|-Jf6b7<)?#s4xfrN*WJ;bwxxt z!l0QKzj%~Ryp8x%siSW5=cVUg#e|mSz}f1$-R5plK;=2rS2cgbPV>;@Ki%zAFP{EC m3XK0p!9TFT|Ke{)C^S$95z!&rnlj)URN9&bkW%%>ul^Ue= 0 && fullScreenCell < 4) { fullScreenMeter(fullScreenCell, pidInCell[fullScreenCell]); + } else if (currentScreenPage == 1) { + drawTriTempScreen(0); } else { tft.fillScreen(TFT_BLACK); // clear screen for (uint8_t c = 0; c < 4; c++) { @@ -1264,9 +1266,14 @@ void initScreen() { arcMeter4(c, pid); } drawQuadrantDividers(); + // Pagination indicator dots [● ○] + tft.fillCircle(153, 2, 2, getThemePrimaryColor()); // Page 1 active + tft.fillCircle(167, 2, 2, 0x5AEB); // Page 2 inactive + } + if (currentScreenPage == 0 && fullScreenCell == -1) { + badgeEcoDrawn = false; + updateEcoBadge(true); } - badgeEcoDrawn = false; - updateEcoBadge(true); } /*-------------------------------*/ @@ -1486,6 +1493,10 @@ void updateMeter(uint8_t pidNo, String response) { // update parameter on screen bt_message = ""; return; } + tempEngineCoolant = data; + if (currentScreenPage == 1 && fullScreenCell == -1) { + updateTriTempCoolant(data); + } break; // temp °C (ECT) case 2: data = A * 100.0 / 255; @@ -1542,7 +1553,7 @@ void updateMeter(uint8_t pidNo, String response) { // update parameter on screen if (pidNo == fullScreenCell) { plotFullScreen(fullScreenCell, data, warn, min, max, digit, unit); } - } else { + } else if (currentScreenPage == 0) { plotArc4(pidNo, data, warn, min, max, digit, unit); } bt_message = ""; // reset @@ -1833,7 +1844,7 @@ void handleArcAlarmFlash() { } } } - } else { + } else if (currentScreenPage == 0) { // 4 quadrants mode for (uint8_t cell = 0; cell < 4; cell++) { if (last_valid_data[cell] != -999) { @@ -1898,6 +1909,19 @@ void handleArcAlarmFlash() { } } } + } else { + // Page 1 (Tri-Temperature screen): check alarm conditions for acoustic warning without touching screen graphics + for (uint8_t cell = 0; cell < 4; cell++) { + if (last_valid_data[cell] != -999) { + uint8_t pidIdx = pidInCell[cell]; + int warn = warningValue[pidIdx].toInt(); + float data = last_valid_data[cell]; + uint16_t col = getMeterColor(pidIdx, data, warn); + bool inRed = (col == TFT_RED); + bool isAlarmActive = alarmEnabled[pidIdx]; + if (inRed && isAlarmActive) anyInRedAlarm = true; + } + } } handleAlarmSound(anyInRedAlarm, toggleTick); } diff --git a/sketch/obd2_gauge/obd2_gauge.ino b/sketch/obd2_gauge/obd2_gauge.ino index c4baa9d..9550619 100644 --- a/sketch/obd2_gauge/obd2_gauge.ino +++ b/sketch/obd2_gauge/obd2_gauge.ino @@ -181,7 +181,12 @@ void updateRearLedTheme() { */ const uint8_t pidInCell[4] = { 0, 3, 1, 2 }; // 4 Cuadrantes: 0=Refrigerante, 1=Velocidad, 2=Revoluciones, 3=Voltaje -int8_t fullScreenCell = -1; // -1 = 4 cuadrantes, 0..3 = pantalla completa de ese cuadrante +int8_t fullScreenCell = -1; // -1 = 4 cuadrantes / multi-pagina, 0..3 = pantalla completa de ese cuadrante +int8_t currentScreenPage = 0; // 0 = 4 cuadrantes, 1 = Tri-Temperatura (Agua + Int + Ext) +float tempEngineCoolant = 88.0f; // Temperatura de refrigerante de motor (OBD2) +float tempInterior = 22.5f; // Temperatura interior habitáculo (DS18B20 #1) +float tempExterior = 16.8f; // Temperatura exterior ambiente (DS18B20 #2) +uint8_t dsSensorCount = 0; // Cantidad de sensores 1-Wire detectados // User configuration here to change display > /*---------------------------*/ @@ -343,6 +348,7 @@ String sendATCommand(const String &cmd, uint32_t timeoutMs = 2000) { //---- Include Header File -------------------- //#include "image.h" #include "bluetooth.h" +#include "tri_temp.h" #include "meter.h" #include "config.h" @@ -504,6 +510,15 @@ recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6] Terminal("[ OK ] Settings: Theme " + String(GAUGE_THEMES[themeIndex].name) + " (LED RGB)"); delay(40); + // Initialize 1-Wire DS18B20 digital temperature bus + initTriTemp(); + if (dsSensorCount > 0) { + Terminal("[ OK ] 1-Wire: " + String(dsSensorCount) + "x DS18B20 Temp Sensors on GPIO 22"); + } else { + Terminal("[ OK ] 1-Wire: DS18B20 Bus Online (GPIO 22)"); + } + delay(40); + //start bluetooth Terminal("[ INFO ] Initializing Bluetooth Controller..."); if (!BTSerial.begin("OBD2 Gauge", true)) { @@ -544,6 +559,13 @@ void loop() { updateBacklightFade(); // non-blocking smooth backlight fade handler handleArcAlarmFlash(); // non-blocking arc alarm flash handler (blinks when in TFT_RED) + // Non-blocking periodic reading of DS18B20 digital temperature sensors (every 1s) + static uint32_t lastDsReadTime = 0; + if (millis() - lastDsReadTime >= 1000) { + lastDsReadTime = millis(); + updateTriTempSensors(false); + } + if (wokeFromSleep) { wokeFromSleep = false; lastActivityTime = millis(); @@ -564,6 +586,7 @@ void loop() { static bool isTouching = false; static uint32_t touchStartTime = 0; + static uint16_t touchStartX = 0, touchStartY = 0; static uint16_t lastTouchX = 0, lastTouchY = 0; static bool longPressHandled = false; @@ -580,16 +603,22 @@ void loop() { if (!isTouching) { isTouching = true; touchStartTime = millis(); + touchStartX = touchX; + touchStartY = touchY; lastTouchX = touchX; lastTouchY = touchY; longPressHandled = false; touchBeep(); // Crisp acoustic feedback on touch screen press! } else { - // Keep tracking coordinates while held to ensure stable quadrant detection + // Keep tracking coordinates while held to ensure stable swipe/quadrant detection lastTouchX = touchX; lastTouchY = touchY; - if (!longPressHandled && (millis() - touchStartTime > 1000)) { + // Long press check: only trigger if finger was held stationary (displacement < 30px) + int16_t currentDistX = abs((int16_t)lastTouchX - (int16_t)touchStartX); + int16_t currentDistY = abs((int16_t)lastTouchY - (int16_t)touchStartY); + + if (!longPressHandled && (millis() - touchStartTime > 1000) && currentDistX < 30 && currentDistY < 30) { // Long press (> 1s): Open Config Menu immediately! longPressHandled = true; touchBeep(); // Confirmation chirp @@ -605,16 +634,47 @@ void loop() { if (isTouching) { isTouching = false; if (!longPressHandled) { - // Valid tap detected! - if (fullScreenCell == -1) { - // Switch to full screen for tapped quadrant - int quad = (lastTouchX < 160) ? ((lastTouchY < 120) ? 0 : 2) : ((lastTouchY < 120) ? 1 : 3); - fullScreenCell = quad; + int16_t deltaX = (int16_t)lastTouchX - (int16_t)touchStartX; + int16_t deltaY = (int16_t)lastTouchY - (int16_t)touchStartY; + + // Check for horizontal swipe gesture (displacement > 45px, mostly horizontal) + if (deltaX < -45 && abs(deltaY) < 90) { + // SWIPE LEFT: Page 0 -> Page 1 (Tri-Temperature Screen) + if (fullScreenCell != -1) { + fullScreenCell = -1; + } + if (currentScreenPage == 0) { + animateSlideTransition(true); + currentScreenPage = 1; + initScreen(); + } + } else if (deltaX > 45 && abs(deltaY) < 90) { + // SWIPE RIGHT: Page 1 -> Page 0 (4 Quadrants) + if (fullScreenCell != -1) { + fullScreenCell = -1; + } + if (currentScreenPage == 1) { + animateSlideTransition(false); + currentScreenPage = 0; + initScreen(); + } } else { - // Return to 4 quadrants view - fullScreenCell = -1; + // Valid TAP detected! + if (currentScreenPage == 0) { + if (fullScreenCell == -1) { + // Switch to full screen for tapped quadrant + int quad = (lastTouchX < 160) ? ((lastTouchY < 120) ? 0 : 2) : ((lastTouchY < 120) ? 1 : 3); + fullScreenCell = quad; + } else { + // Return to 4 quadrants view + fullScreenCell = -1; + } + initScreen(); + } else { + // Tap on Tri-Temp screen: immediate sensor refresh + updateTriTempSensors(true); + } } - initScreen(); waitForTouchRelease(); // Clear mechanical bounce on release } } diff --git a/sketch/obd2_gauge/tri_temp.h b/sketch/obd2_gauge/tri_temp.h new file mode 100644 index 0000000..2cbb7d8 --- /dev/null +++ b/sketch/obd2_gauge/tri_temp.h @@ -0,0 +1,249 @@ +#pragma once + +#include +#include +#include +#include +#include "DSEG7_Modern_BoldItalic_34.h" + +// Hardware 1-Wire Pin: Conector CN1 Pin 3 (GPIO 22) +#define ONE_WIRE_BUS_PIN 22 + +extern TFT_eSPI tft; +extern uint16_t getThemePrimaryColor(); +extern uint16_t getThemeBgColor(); +extern void touchBeep(); + +// Global states for multi-page system +extern int8_t fullScreenCell; +extern int8_t currentScreenPage; // 0 = 4 cuadrantes, 1 = Tri-Temperatura +extern bool ignitionOn; + +// Global temperature values +extern float tempEngineCoolant; +extern float tempInterior; +extern float tempExterior; +extern uint8_t dsSensorCount; + +// OneWire and DallasTemperature instances +static OneWire oneWireObj(ONE_WIRE_BUS_PIN); +static DallasTemperature dsSensors(&oneWireObj); + +// Previous values for flicker-free redrawing +static float oldTriCoolant = -999.0f; +static float oldTriInterior = -999.0f; +static float oldTriExterior = -999.0f; + +// Initialize 1-Wire sensors +inline void initTriTemp() { + dsSensors.begin(); + dsSensors.setWaitForConversion(false); // Non-blocking conversion (0ms wait) + dsSensorCount = dsSensors.getDeviceCount(); + if (dsSensorCount > 0) { + dsSensors.requestTemperatures(); + } +#ifdef SERIAL_DEBUG + Serial.printf("[1-WIRE] Sensores DS18B20 detectados en GPIO 22: %d\n", dsSensorCount); +#endif +} + +// Draw a single temperature card with card index (0: Motor, 1: Interior, 2: Exterior) +inline void drawTriTempCard(uint8_t cardIdx, float val, int x_offset = 0) { + int cardY = 6 + cardIdx * 77; // y = 6, 83, 160 + int cardH = 73; + int cardX = 4 + x_offset; + int cardW = 312; + + if (cardX + cardW <= 0 || cardX >= 320) return; + + uint16_t cardBg = 0x10A2; // Dark graphite navy + uint16_t cardBorder = 0x2945; // Subtle edge border + + // 1. Draw Card Background and Border + tft.fillRoundRect(cardX, cardY, cardW, cardH, 6, cardBg); + tft.drawRoundRect(cardX, cardY, cardW, cardH, 6, cardBorder); + + // 2. Card Metadata & Accent Color + uint16_t accentCol = getThemePrimaryColor(); + const char* titleStr = ""; + const char* subStr = ""; + float minRange = 0.0f, maxRange = 100.0f; + bool isInt = false; + + if (cardIdx == 0) { + titleStr = "MOTOR / REFRIGERANTE"; + subStr = ignitionOn ? "ECU OBD-II (PID 0105)" : "ECU OBD-II (STANDBY)"; + minRange = 60.0f; + maxRange = 120.0f; + isInt = true; + if (val < 100.0f) accentCol = getThemePrimaryColor(); + else if (val < 110.0f) accentCol = TFT_YELLOW; + else accentCol = TFT_RED; + } else if (cardIdx == 1) { + titleStr = "HABITACULO / INTERIOR"; + subStr = (dsSensorCount > 0) ? "DS18B20 SENSOR 1 (GPIO 22)" : "DS18B20 SENSOR 1 (DEMO)"; + minRange = 10.0f; + maxRange = 40.0f; + accentCol = 0x07F3; // Mint Green + } else { + titleStr = "AMBIENTE / EXTERIOR"; + subStr = (dsSensorCount > 1) ? "DS18B20 SENSOR 2 (GPIO 22)" : (dsSensorCount == 1 ? "ESTIMADA (-4.5`C)" : "DS18B20 SENSOR 2 (DEMO)"); + minRange = -10.0f; + maxRange = 50.0f; + accentCol = 0x3D7F; // Sky Blue + } + + // Left vertical accent bar + tft.fillRoundRect(cardX + 6, cardY + 8, 4, cardH - 16, 2, accentCol); + + // Titles + tft.setTextColor(TFT_WHITE, cardBg); + tft.drawString(titleStr, cardX + 16, cardY + 8, 2); + + tft.setTextColor(0x8410, cardBg); // Soft gray + tft.drawString(subStr, cardX + 16, cardY + 28, 1); + + // 3. Horizontal Progress Gauge Bar + int barX = cardX + 16; + int barY = cardY + 50; + int barW = 120; + int barH = 8; + tft.fillRoundRect(barX, barY, barW, barH, 3, 0x18C3); // Trough background + + float ratio = (val - minRange) / (maxRange - minRange); + if (ratio < 0.0f) ratio = 0.0f; + if (ratio > 1.0f) ratio = 1.0f; + int fillW = (int)(barW * ratio); + if (fillW < 4) fillW = 4; + tft.fillRoundRect(barX, barY, fillW, barH, 3, accentCol); + + // 4. Digital Number using DSEG7 Modern Bold Italic 34pt + String numStr; + if (val <= -900.0f) { + numStr = "---"; + } else if (isInt) { + numStr = String((int)val); + } else { + numStr = String(val, 1); + } + + tft.loadFont(DSEG7_Modern_BoldItalic_34); + tft.setTextColor(accentCol, cardBg); + tft.setTextPadding(100); + tft.drawRightString(numStr.c_str(), cardX + 262, cardY + 22, 1); + tft.setTextPadding(0); + tft.unloadFont(); + + // Unit "°C" + tft.setTextColor(0xC618, cardBg); + tft.drawString("`C", cardX + 268, cardY + 32, 2); +} + +// Draw full Tri-Temp screen +inline void drawTriTempScreen(int x_offset = 0) { + if (x_offset == 0) { + tft.fillScreen(TFT_BLACK); + } else { + tft.fillRect(0, 0, x_offset, 240, TFT_BLACK); + } + + drawTriTempCard(0, tempEngineCoolant, x_offset); + drawTriTempCard(1, tempInterior, x_offset); + drawTriTempCard(2, tempExterior, x_offset); + + oldTriCoolant = tempEngineCoolant; + oldTriInterior = tempInterior; + oldTriExterior = tempExterior; + + // Draw Page Indicator: [○ ●] (Page 2 active) + tft.fillCircle(153, 2, 2, 0x5AEB); // Page 1 inactive + tft.fillCircle(167, 2, 2, getThemePrimaryColor()); // Page 2 active +} + +// Update coolant reading dynamically without redrawing entire screen +inline void updateTriTempCoolant(float val) { + tempEngineCoolant = val; + if (currentScreenPage != 1 || fullScreenCell != -1) return; + if (val != oldTriCoolant) { + drawTriTempCard(0, val, 0); + oldTriCoolant = val; + } +} + +// Update 1-Wire sensors (Interior and Exterior) +inline void updateTriTempSensors(bool forceRedraw = false) { + // Hotplug scan if 0 sensors detected yet + if (dsSensorCount == 0) { + static uint32_t lastHotplugCheck = 0; + if (millis() - lastHotplugCheck >= 5000) { + lastHotplugCheck = millis(); + dsSensors.begin(); + dsSensorCount = dsSensors.getDeviceCount(); + if (dsSensorCount > 0) { + dsSensors.setWaitForConversion(false); + dsSensors.requestTemperatures(); + forceRedraw = true; + } + } + } + + if (dsSensorCount > 0) { + float t1 = dsSensors.getTempCByIndex(0); + if (t1 > -50.0f && t1 < 100.0f) { + tempInterior = t1; + } + if (dsSensorCount > 1) { + float t2 = dsSensors.getTempCByIndex(1); + if (t2 > -50.0f && t2 < 100.0f) { + tempExterior = t2; + } + } else { + // Demo exterior offset if only 1 physical sensor connected + tempExterior = tempInterior - 4.5f; + } + dsSensors.requestTemperatures(); // non-blocking next reading request + } else { + // Simulated demo readings if sensors not yet plugged in + tempInterior = 22.4f + 0.3f * sin(millis() / 4000.0f); + tempExterior = 16.8f + 0.2f * cos(millis() / 5000.0f); + } + + if (currentScreenPage == 1 && fullScreenCell == -1) { + if (forceRedraw || abs(tempInterior - oldTriInterior) >= 0.1f) { + drawTriTempCard(1, tempInterior, 0); + oldTriInterior = tempInterior; + } + if (forceRedraw || abs(tempExterior - oldTriExterior) >= 0.1f) { + drawTriTempCard(2, tempExterior, 0); + oldTriExterior = tempExterior; + } + } +} + +// Animated mobile-like slide transition between screens +inline void animateSlideTransition(bool swipeLeft) { + touchBeep(); + const uint8_t STEPS = 10; + const uint16_t STEP_W = 320 / STEPS; // 32 px + uint16_t primary = getThemePrimaryColor(); + + if (swipeLeft) { + // Swipe left: wipe beam sweeping from right to left + for (int s = 0; s < STEPS; s++) { + int x = 320 - (s + 1) * STEP_W; + tft.fillRect(x, 0, STEP_W, 240, TFT_BLACK); + tft.drawFastVLine(x, 0, 240, primary); + tft.drawFastVLine(x + 1, 0, 240, TFT_WHITE); + delay(10); + } + } else { + // Swipe right: wipe beam sweeping from left to right + for (int s = 0; s < STEPS; s++) { + int x = s * STEP_W; + tft.fillRect(x, 0, STEP_W, 240, TFT_BLACK); + tft.drawFastVLine(x + STEP_W - 1, 0, 240, primary); + tft.drawFastVLine(x + STEP_W - 2, 0, 240, TFT_WHITE); + delay(10); + } + } +}