From 5712bf54e30e461350b31ca36cf919702199ee84 Mon Sep 17 00:00:00 2001 From: Mobkom21 Date: Mon, 11 May 2026 17:17:02 +0200 Subject: [PATCH] update code --- src/__pycache__/gps_hat.cpython-313.pyc | Bin 0 -> 7042 bytes src/__pycache__/sens_hat.cpython-313.pyc | Bin 0 -> 3417 bytes src/__pycache__/sens_hat.cpython-314.pyc | Bin 0 -> 2779 bytes src/gps_hat.py | 83 +++++++++++++++++++++-- src/main.py | 43 ++++++++++-- src/sens_hat.py | 17 ++++- 6 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 src/__pycache__/gps_hat.cpython-313.pyc create mode 100644 src/__pycache__/sens_hat.cpython-313.pyc create mode 100644 src/__pycache__/sens_hat.cpython-314.pyc diff --git a/src/__pycache__/gps_hat.cpython-313.pyc b/src/__pycache__/gps_hat.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1e230b78804b38c4419d8fe88e3ad0c7d20eaa5 GIT binary patch literal 7042 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 literal 0 HcmV?d00001 diff --git a/src/__pycache__/sens_hat.cpython-313.pyc b/src/__pycache__/sens_hat.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46461f0a49b50e7ccb3bb81220cc4b4898166a8a GIT binary patch literal 3417 zcmcImU2NOd6}}WnnWCuLvMpJ0;)HS1WlS>5Hf$tyn*rGxEJzR=F()8c3=oXW$_gY( zxug)=MFBk(3fdyg2G<@|Ych0mB}v*_~Cc16=gUZ*GiYkJ~vUMKg{K z3)kVlx_|s{e&+Ppkm=&TDmAOeM1K-Ha$PJdV zGRcr(qOyZTc0TOP`#*c{1G7Tr++ER#%1~B~P!2i|Jqo=G zx&S=}U4$NoF3lxoPlSi_c#H&SPQI*Bxz{4JN96|zO{!6#y=oWGK2-qPuf~9;RFS3& zaYcd^Uz=97>sNoegYl;rR)yUWkcR^zJ1hzqho85Xsz%e*Xe2x5vkR57ku6v5)=CkOdE$H@0b;%Jlt!Nf)O>>f+<&Shkcx+Z2NS%C<8v4c08r%os9{iHS7yoOR zeTC%6ESel>v$)y=Ewniwg5)S~jNgG4^6tR>C~w@tffn+Pc2q~Kyt|{{|8~4h<_MqU zzHNsKd?GnUWe~@3sIiU~WD#HHUTc(tjP+%~4ssEd^JP-t8&P>*mI%qBzU-BbtU#2m zX@{egQpw?~MLWOXuuJ+whgo!(C5Ks7x+#w7bQcU;vyF1qpt@b7hQr~HBcXVqRxZvL zZNN>yx$-JCEDLRgfOChb(4t}5x?QZ8P6`Lm^Le9WP{5~cP>t#jX+O+KQ%KTC29RWs z90Q`T6xZ*>!vjhFAnvdhB1@KKis$659r0bE+PrS-P9{J#&K}a>WY+J1)JeNPvwGvn zjjjHXR{zLG|7e4IKi1Bi_>K5WaVwK;WwINYF_0w=lWnI5SMNW$zm*Dp;L-L^0J8+*I3Dz*Ibp;8$>ja zeGjd1aS%&EdMrK?w}ebf$b2RYdTS2u7}(%lc9Y$>n=e!*$5@doktrxTI@qZm2cykI zWHQqY`pC=rc|gSsBZpF@%7#h_6ucb!f*0qG5Bb#{^%Us2L-gTU`Iq&F%*uyng=`RH zGT5cxX7M5(Xm@(=Oyu}UUn3Lzn_$5|94X%?=Vb=w9Q?x_j!wI!We4N<4Mu)Zu9@@l zl3f1lpDh5my!^0e3+bi+4(;vl~x7SZUy}SP4>D@1+?dW{^o>pF3YczpaCxF7oLIZv99vIr3K!a^hno+Yu?F|J`%(8A z(-H>P#{MA;d7urKEZmC=+P?t+A3S8y1%j7i z`Fy2Rp%!BK;Avyd^&s=&X$SHNaI)rr{2!;i8zt#XLu{vxt!huS=IvJM+&Z(FI^Vd~ zmeO0&P)i#6INyBrSNH!ZDPOZBeE}SL20s;!xyPx96vF0L8VCct-;iL^qsNi#7aq3` zVfC3-2JnGo>SY5SNTQ~dEAzDy@{*=KsOcqN)2(T56{%&Hil$*!G|d~(oo;`oqe`3; zF{=*03^mbm_!>m}vU>m&AKUaS68F+#r0DBFoRp?nSW@$vZo?J4SF;UE)1JZO6iu)U zQalTnaKO0neV4T3QvJ<#cTfGgOSau#DN(=H7UT6FZws;d)pkI(lYOh1pJ&#T_3=Mk zes;N)oT%S;&N00FJo*ZM=DE!9$Dc=I{P}OvBJbsRcRGw#SudKJric!2*2>i-dKH%7 zK=OchDn2^hcdNI+E2x2ot7(OCXIDx^%cj?G3&D;Y;SDeLKcqK70hNPTw}9-j48#13 z^!}Ti{0}$941P_3>@G5dlN!q#?6J?|eeX@x-~5t`HO@ZHKFV%#gP(W!Em3ZX@`gD4 N8FywkMmTRD{{slcsLlWY literal 0 HcmV?d00001 diff --git a/src/__pycache__/sens_hat.cpython-314.pyc b/src/__pycache__/sens_hat.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71b73b00f5ba14510553df9f9aac6b6a0da9beba GIT binary patch literal 2779 zcmaJDOKj9ebnLY^&VEQX*`FYgO;bWGLN=h#q(DEkMGaI5!Jr_O63gtG#B_g*XGIbT zX>U!Xik4Pt!l^mYq?h*CLxn2!(j!p_*-ohxwI^t;0D_VF3*8W9g#Km=cStp4djOCMnnIdz77+oy;#qOjM||BVhI)|ZOCU`e5*=wG zX+kej<)>^pt_oDSXcla9)F2s0z=5zAAKOdtF~Is&u&f~>PD#WUM@86;NHArXewZyV z6_~9s12BUyLs!EST{Sy(AMi*4&Vf2o4b_BU*UNTIOdt|rzLO430@$g^0Hd0p#3m7m zYb_8(D|mlq{GvX3`VvGUXUSa2%m{UcnK>Uq%J!>(RM5vBZ>T0Ti3%P~L%nEK7*|CM zDu~K9JD630O17E1ZZoky`g%HVW-TKS{JS5XAJd0E zNZUA*X8QV!K?X{5RMB;-U=dxX5!d4Zm(&TC?I3^(dJ^0J^+yZRoxnCMQS(q9f+rO; zi6)px!a_hXYZvV{m%sCMwM)V-I*}b& z7xQ$)Nyyj!y)K=_TxfMyz!MsHyv`UOcwz(3i)>QVd=0#iXH(Rq241+1CpYl+xIE1d zncPApT+HRDT(U@JhWh4=TU3~(!WJg05bCeewRFPYdNWo%N3Eog|5XUcgi zYZ1sa599O_Hf@{9Djvqo6tQI%h(WAkfyS5tBa<<6CWgEwCf2cW3$xS39SpDx#Zd-g z48#GbKFm&q2I~!lYDTzOo8@9(DuvkzIli-sC}KTp5Q8SYI$*okahP@cdjJ(wjV2e* z-95J%9axJFtVahIq|aNc$*yk$_X3;A^jb2#o;(V)a5LIgeDC6oyEiuDgKP1@_4u)c zU^TICasKZ7W@30PF}$95V|e@k zL|y^hUu}xt@ne>}&8zhU4`i48{hw;>MywrS1=OJ-@#~i}In%(HC92bnoRe+MnOU8) zXoq*E9<7f_vrB~2R6<__J_~Fr$u%YUSlR1%?bRj7u=)p|pk4AFGleb*W$~mq0zqWK zlP)yX!g0ugE;J@~qp=9ch|OfC+qsO;UDtFqVW`$@Av&=a-jGYa2Hk(T{X?GpUXYQR z%Dn7@+)Spa55AXufSd8UB1B&s6Yl%x65xBe-U_Z@ve4 zE_&)w=uHsaa&xY0P3c;?uzYkS@>n^{Gfxd*)}5UW!~PSThQm@FCmGmjIK2O`zA&!( zp0XsxX#h`I`KSr@wib-M32$pa*Ym|}ImhsjuHP&hxf-WU*RNaHCONBM7K*y=?7;Wm z*?PBLN5w*k%Ja}1Z7P@HHb!WG9ctbn*qX(x%i|sf4l-~A0FCLo&AMwwHwXw_D-+XZ zkG~F;YS?zx$`EL`IbDbUmT(;r)L4nU!>o@IbHR@R-|(=`BidBT{-r{$m+&;5Jk-sdSn-uJ?< z$osg#09X4DiVQTK literal 0 HcmV?d00001 diff --git a/src/gps_hat.py b/src/gps_hat.py index a93bc90..e5322f3 100644 --- a/src/gps_hat.py +++ b/src/gps_hat.py @@ -1,5 +1,7 @@ import serial import time +import subprocess +import os class GPS_DATA: breitengrad = "" @@ -41,13 +43,82 @@ class GPS_DATA: else: print("Suche Satelliten...") - def get_sat_count(self): - # self.ser.write(b'AT+CPSI?\r\n') - self.ser.write(b'AT+CGPSGSV\r\n') - time.sleep(0.1) - result = self.ser.read_all().decode() - print(result) + def LTE_hat_encode_feedback(self, result_org): + result_clean = [] + result_pars = result_org.strip().split("\n") + for result in result_pars: + if result == "\r": + continue + + result_clean.append(result.strip()) + + return result_clean + + def get_sim_status(self): + ser.write(b'AT+CPIN?\r\n') + time.sleep(0.5) + result = self.ser.read_all().decode() + result_clean = self.LTE_hat_encode_feedback(result) + + # lockd: ['AT+CPIN?', '+CPIN: SIM PIN', 'OK'] + # unlock: ['AT+CPIN?', '+CPIN: READY', 'OK'] + match result_clean[1]: + case "+CPIN: SIM PIN": + return False + case "+CPIN: READY": + return True + case _: + print("Ein fehler ist aufgetreten. Sim Status konnte nicht abgefragt werden.") + + + def enter_sim_pin(self): + if self.get_sim_status(): + print("Sim bereits entsperrt.") + return + + self.ser.write(b'AT+CPIN="3082"\r\n') + time.sleep(1) + result = self.ser.read_all().decode() + result_clean = self.LTE_hat_encode_feedback(result) + # ['AT+CPIN="3082"', 'OK', '+CPIN: READY', 'SMS DONE'] + # print(result_clean) + if result_clean[2] == "+CPIN: READY": + print("Sim entsperrt.") + + def LTE_hat_disable(self): + self.ser.write(b'AT+CFUN=0\r\n') + + def LTE_hat_enabel(self): + self.ser.write(b'AT+CFUN=1\r\n') + + def LTE_hat_start(self): + print("Starte LTE Verbindung (Swisscom)...") + try: + subprocess.run(["pon", "swisscom"], check=True) + + print("Warte auf IP-Zuweisung...") + for _ in range(10): + time.sleep(2) + if os.path.exists("/sys/class/net/ppp0"): + print("LTE erfolgreich gestartet. Interface ppp0 ist aktiv.") + return True + + print("Fehler: Verbindung konnte nicht in der vorgegebenen Zeit aufgebaut werden.") + return False + except subprocess.CalledProcessError as e: + print(f"Fehler beim Starten von pon: {e}") + return False + + def LTE_hat_stop(self): + print("Beende LTE Verbindung...") + try: + subprocess.run(["poff", "swisscom"], check=True) + print("LTE Verbindung getrennt.") + return True + except subprocess.CalledProcessError as e: + print(f"Fehler beim Stoppen von poff: {e}") + return False if __name__ == "__main__": gps = GPS_DATA("/dev/ttyUSB0") diff --git a/src/main.py b/src/main.py index 20c1c75..9978cf4 100644 --- a/src/main.py +++ b/src/main.py @@ -32,8 +32,9 @@ def encode_led_set_payload(payload): x = int(data["pos"][0]) y = int(data["pos"][1]) r = int(data["rgb"][0]) - b = int(data["rgb"][1]) - g = int(data["rgb"][2]) + g = int(data["rgb"][1]) + b = int(data["rgb"][2]) + print("set Pixel: ", end="") print(data) sens.led_set_pixel(x, y, r, g, b) except json.JSONDecodeError: @@ -43,13 +44,34 @@ def encode_led_set_payload(payload): except ValueError: print("Werte müssen int sein:\n" + payload) +def encode_led_set_matrix_payload(payload): + try: + data = json.loads(payload) + matrix = data["matrix"] + print("set matrix: ") + # print(matrix) + + for y, row in enumerate(matrix): + for x, col in enumerate(row): + sens.led_set_pixel(x, y, col[0], col[1], col[2]) + except json.JSONDecodeError: + print("Ungültiges JSON formatt:\n" + payload) + except KeyError: + print("Ungültige Keys:\n" + payload) + except ValueError: + print("Werte müssen int sein:\n" + payload) + def encode_led_get_payload(payload): try: data = json.loads(payload) x = int(data["pos"][0]) y = int(data["pos"][1]) + print("get Pixel: ", end="") print(data) - # sens.led_set_pixel(x, y) + color = sens.led_get_pixel(x, y) + pixel_data = [{"pos": (x, y), "rgb": color}] + # print(pixel_data) + client.publish("MOBKOM/LED/state", json.dumps(pixel_data)) except json.JSONDecodeError: print("Ungültiges JSON formatt:\n" + payload) except KeyError: @@ -70,6 +92,11 @@ def on_message(client, userdata, message): encode_led_set_payload(payload) case "MOBKOM/LED/get_pixel": encode_led_get_payload(payload) + case "MOBKOM/LED/set_matrix": + encode_led_set_matrix_payload(payload) + case "MOBKOM/LED/get_matrix": + client.publish("MOBKOM/LED/state_matrix", json.dumps({"matrix": sens.led_matrix.tolist()})) + pass case _: print(f"Eingehend auf {topic}: {payload}") @@ -84,7 +111,8 @@ def on_connect(client, userdata, flags, rc): client.subscribe("MOBKOM/LED/set_pixel") client.subscribe("MOBKOM/LED/get_pixel") client.subscribe("MOBKOM/LED/clear") - client.on_message = on_message + client.subscribe("MOBKOM/LED/set_matrix") + client.subscribe("MOBKOM/LED/get_matrix") if __name__ == "__main__": # gps = GPS_DATA("/dev/ttyUSB0") @@ -92,11 +120,12 @@ if __name__ == "__main__": sens = SENS_HAT() client = mqtt.Client() + client.on_connect = on_connect + client.on_message = on_message client.connect("localhost", 1883) - client.on_connect = on_connect - update_timer = 0 + update_timer = 0 # for mqtt update send try: while True: @@ -105,7 +134,7 @@ if __name__ == "__main__": update_timer = time.time() client.loop() - time.sleep(0.1) + time.sleep(0.01) except KeyboardInterrupt: diff --git a/src/sens_hat.py b/src/sens_hat.py index cef0ef1..50e92af 100644 --- a/src/sens_hat.py +++ b/src/sens_hat.py @@ -14,7 +14,7 @@ class SENS_HAT: y = 0 z = 0 - led_matrix = np.zeros((7, 7, 3), dtype=np.uint8) + led_matrix = np.zeros((8, 8, 3), dtype=np.uint8) def __init__(self): self.sense = SenseHat() @@ -47,9 +47,22 @@ class SENS_HAT: return self.sense.set_pixel(x, y, r, g, b) + self.led_matrix[y][x] = (r, g, b) def led_get_pixel(self, x, y): if not (0 <= x <= 7) or not (0 <= y <= 7): print("x und y müssen zwischen 0 und 7 sein!") return - return self.led_matrix[x][y] + + # umwandlung in ein normales python array da json keine numpy array mag. + pixel_data = [] + for color in self.led_matrix[x][y]: + pixel_data.append(int(color)) + + return pixel_data + +if __name__ == "__main__": + sens = SENS_HAT() + + print(sens.led_matrix) + print(sens.led_matrix.tolist())