From 50e168cf73bff76d0e3eb7b6eb7223a0e815cd19 Mon Sep 17 00:00:00 2001 From: Mat Booth Date: Fri, 22 May 2026 17:51:43 +0100 Subject: [PATCH] GPS Firmware API/Events Major changes: * Remove the graphical app portion, the firmware now runs in the background only * Provide 3 API methods for getting the current position as a (lat, lon) tuple, ground speed in knots, and course in degrees from true north * Add a GPSEvent type and emit events every time a location fix message is received from the GPS * In both the API and the GPSEvent, when the fix is lost, the position value will be None instead of a tuple Other fixes: * Firmware version bumped to 2 * Add a character timeout to the UART configuration to improve the ability of uart.readline() to read a full line, and reduce parsing errors caused by reading partial lines * Override background_task to manage sleep for longer between UART message bursts, to reduce the impact of the blocking UART timeout on other apps --- EEPROM/gps.mpy | Bin 1470 -> 1352 bytes EEPROM/gps.py | 163 ++++++++++++++++++++++++++++++------------------- 2 files changed, 100 insertions(+), 63 deletions(-) diff --git a/EEPROM/gps.mpy b/EEPROM/gps.mpy index 5f3d9dda6cac3bddbaecc36a57f3a97ebac38838..ca6ece88edb4192a16a6ff31759fadf44405ba65 100644 GIT binary patch literal 1352 zcmYjO-*3}a6h3y+Gz}qfY~vZ)fg7hZ$x44@!N4FaZ5#?{K-|`YOxmKO5BiltS z(=^+(Tg9}uz3g>Qd$bey1NikcX%bJP(zHEH`vW7h+R}|{GwWS*y1Na@NVD68MAO{vD4GsYOKYnTtKL&P7R1#6Odtlhx@p33bKA7k z_Vn9ARGwd2fk;j3K%(7LZfhMC;)}KAc_bPM0)dhPH9oHBoi%M8$VOK;HA~YwKr~j> zrlED#A>J^%s@j6Er310r+-w<5OGVihM7l^kY-~Xy@TPBC5Z!DV7T^X%wT_0;w%$@9 zazkDLw~F5f;;nq*je6`r{P=29*;qI9%}%RfHO&nmEKS)k8*LNl1J=^k)*$`AW1HPp zV1zL5s%kel=zJM(sY>voJLmHF6vQ)&wWVv70Ivs#&jY>;_)UnaZRGB}lN)O^(2yJI zR#!KyMgwL}olbmQU0S|!<2o{H=v~#Yw&9RVxu@Ii;aE$(Yc(OZc2{p&z{(vhc&%y} zx&ivU6b;Qz&kkkLSh+QAi~IbGDQ?glV%$bYx6CQ-b=Ro!)Pvm>mc&k;yfigsd~4a# zD9+$0hL{kFVJ2)7LwRg8GB(5v)oo%J@#xqvGmQ8M5zFJFhsH*jk+B#PtFIJ*h^KJs za52QhQ*lO+6l}N1@$tP_x$@1(!_nL2;M+++>I`$1h%@J0=7Zo;A~l4rB&0mH?2Tg> zlT4j2;6;oXM;JnwMi@rO7lxQjA;L_0%CPeh#1ebSR(YV6V9phQNft1sC@Hv8`!P%= zOXGanCNde{W5;_Q%lADtEh)}yE?O+vM|g}r?NQ?%#d}m*2q}AK|G=`z3njv4?@aKm z%!FFboKUhe8hLR7{3A&L>OJWQ>bT%SJxEVD0#>(umXnlQBeC(4UF19} z74US)K8HA|IB+z^m+XriS@tN(E#k^f?Ma!M$BgT z*|}V)l;yMhT>cI>*=n7g=F{Fyiu3>cghiH+YXQV1kK)p{@=ClisMx3vBL zama=K16gvXak+>MxGN2K`ocedJx7IIS$gnhb60-w-{y<{j!ew$iRS9!Ktw#@9$2c|>e)z(_P>=NC~7jHAVo!}R+eoj|Rj4fH#N5Nh-) Zd6TS=B3UIb<-@;=mHJie(dr96mX+XQOR6Q) zzy(<$P&&gu(dmJsmB94g!l^TzVR|UcFzu=T0GA%=lM~u%p5DIwzVGes+gU*`00mqHIhN-mT{{qQ^@^wcs%*$F^*=%bawVv-7 zw=(W+m^$E!rlv3H`aT#xh+xRKlzOMBwD)5fKuJr568Q2B6#}iQe5;`<;J=bD;+x=e zx3FK<)Q!d__;joxO(3^`Yy(*XvI(eG?bO>QEMIzTnlh6z+lt1IAJmr7t~ z$a{crQ>w;YRR+=ke_iQ|?2Fwwe|jmG&a6Ur7_~6iY8VPoN(+bJYgPI7{_`pwz3!&z z-p5yEH?n=w4wJLsk6g(Y*3z!cxt3f4vIyjLAXT7nY2N_7p{ZuYXxziqv^Q%&w-wo} zs+&!mW~0$;;&*Fzl!~bVWhz@H`0(q2TCJLkc-PcL;N+?*E6x3p0=0~bf>5R6PFC5{ zwYFKQ!2H~)z_m=FxU!yuA)B#JwY(MFlu8{2H=0`21bf~d<|MV@)}Cp%wKix=-ii#A zXCa;#@E4V8+im`4JRXNfAudSVoSIF{CJIk-mN-VTq=h^&V$2)zu%5Do29PgCj(rpw zU!Hla3qSq$l3XsI{o=1IHPn5%DdtpLYiJxb{&q{WrCLEaHH6E zv1PHfu{}%=I7!0cIMLyb(8FDHl(&8XH0Cg)DC96~@*yUVIn3eUaWqZ`n2D4PW!F8? zO%dh$4#(3}Fia0QOvFif%A#yXzrTbc*)?loVtU45$le=4?0wMpm}9|7T$m_7`Xa_p zWpmclL_B(VdUa)HW@R$gW4Pcn?L$#1MF=_9o5A8td7w<) 99: self.r.value(0) - if self.l: + # Clear fix data if we haven't had a fix for a while + if self._position: if self.z > 9999: - self.l = None + self._position = None + self._speed = 0 - def background_update(self, _d): - """ Update the app state in the background - read GPS data """ - l = self.u.readline() + l = self.uart.readline() if l: - #print(l) try: p = l.decode().strip().split(',') - if (p[0] != "$GPRMC" and p[0] != "$GNRMC") or p[2] != "A" or not p[3] or not p[5]: - return None - t = float(p[3][:2]) + float(p[3][2:]) / 60 - n = float(p[5][:3]) + float(p[5][3:]) / 60 - if p[4] == "S": - t = -t - if p[6] == "W": - n = -n - self.l = str(round(t, 5)) - self.n = str(round(n, 5)) - self.z = 0 - except (UnicodeError, ValueError, AttributeError): + if p[0] == "$GPRMC" or p[0] == "$GNRMC": + if p[2] == "A": + lat = float(p[3][:2]) + float(p[3][2:]) / 60 + lon = float(p[5][:3]) + float(p[5][3:]) / 60 + if p[4] == "S": + lat = -lat + if p[6] == "W": + lon = -lon + self._position = (round(lat, 5), round(lon, 5)) + self._speed = float(p[7]) + self._bearing = float(p[8]) + + # Eliminate satellite jitter when stationary by rounding + # very small velocities to zero + if self._speed < 1: + self._speed = 0 + + # Reset the time since last fix if we successfully got a valid fix message + self.z = 0 + + # Send event to subscribers + eventbus.emit(self.GPSEvent(self._position, self._speed, self._bearing)) + except (UnicodeError, ValueError, AttributeError, IndexError): pass + return True + return False + - def draw(self, _c): - _c.font_size = 40 # not using defined sizes to save bytes in the mpy file - _c.rgb(0, 0.2, 0).rectangle(-120, -120, 240, 240).fill() - _c.rgb(0, 1, 0).move_to(-35, -50).text("GPS") - if self.l: - _c.move_to(-110, 0).text("Lat:" + self.l) - _c.move_to(-110, 40).text("Lon:" + self.n) - else: - _c.move_to(-110, 0).text("Searching...") - button_labels(_c, cancel_label="Back") - -__app_export__ = GPSApp #pylint: disable=invalid-name +__app_export__ = GPSApp # pylint: disable=invalid-name