AlLoRa 1.2.0__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. allora-2.0.0/AlLoRa/Connectors/Connector.py +323 -0
  2. allora-2.0.0/AlLoRa/Connectors/E5_connector.py +339 -0
  3. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/Connectors/LoPy4_connector.py +1 -0
  4. allora-2.0.0/AlLoRa/Connectors/SX1262_connector.py +119 -0
  5. allora-2.0.0/AlLoRa/Connectors/SX127x_connector.py +125 -0
  6. allora-2.0.0/AlLoRa/Connectors/Serial_connector.py +262 -0
  7. allora-2.0.0/AlLoRa/Connectors/Wifi_connector.py +124 -0
  8. allora-2.0.0/AlLoRa/Digital_Endpoint.py +189 -0
  9. allora-2.0.0/AlLoRa/File.py +147 -0
  10. allora-2.0.0/AlLoRa/Interfaces/Serial_interface.py +250 -0
  11. allora-2.0.0/AlLoRa/Interfaces/WiFi_interface.py +268 -0
  12. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/Nodes/Adapter.py +16 -5
  13. allora-2.0.0/AlLoRa/Nodes/Gateway.py +103 -0
  14. allora-2.0.0/AlLoRa/Nodes/Node.py +167 -0
  15. allora-2.0.0/AlLoRa/Nodes/Requester.py +490 -0
  16. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/Nodes/Source.py +117 -35
  17. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/Packet.py +164 -50
  18. allora-2.0.0/AlLoRa/utils/debug_utils.py +6 -0
  19. allora-2.0.0/AlLoRa/utils/json_utils.py +4 -0
  20. allora-2.0.0/AlLoRa/utils/os_utils.py +6 -0
  21. allora-2.0.0/AlLoRa/utils/time_utils.py +90 -0
  22. allora-2.0.0/AlLoRa.egg-info/PKG-INFO +377 -0
  23. allora-2.0.0/AlLoRa.egg-info/SOURCES.txt +77 -0
  24. allora-2.0.0/PKG-INFO +377 -0
  25. allora-2.0.0/README.md +351 -0
  26. allora-2.0.0/examples/Adapters/WiFi_adapters/WiFi-Client/boot.py +4 -0
  27. allora-2.0.0/examples/Adapters/WiFi_adapters/WiFi-Client/main_ESP32.py +7 -0
  28. allora-2.0.0/examples/Adapters/WiFi_adapters/WiFi-Client/main_LoPy4.py +7 -0
  29. allora-2.0.0/examples/Adapters/WiFi_adapters/WiFi-Hotspot/boot.py +4 -0
  30. allora-2.0.0/examples/Adapters/WiFi_adapters/WiFi-Hotspot/main_ESP32.py +9 -0
  31. allora-2.0.0/examples/Adapters/WiFi_adapters/WiFi-Hotspot/main_LoPy4.py +7 -0
  32. allora-2.0.0/examples/Gateways/Serial_Gateway/main.py +31 -0
  33. allora-2.0.0/examples/Gateways/Serial_Gateway/rf_changer.py +57 -0
  34. {AlLoRa-1.2.0/examples/RaspberryGateway → allora-2.0.0/examples/Gateways}/Wifi_Gateway/main.py +5 -5
  35. allora-2.0.0/examples/Requesters/LopyRequester/__init__.py +0 -0
  36. {AlLoRa-1.2.0/examples/LoPyReceiver → allora-2.0.0/examples/Requesters/LopyRequester}/main.py +1 -1
  37. allora-2.0.0/examples/Requesters/T3S3/__init__.py +0 -0
  38. allora-2.0.0/examples/Requesters/T3S3/main.py +34 -0
  39. allora-2.0.0/examples/Requesters/T3S3/main_pro.py +52 -0
  40. allora-2.0.0/examples/Requesters/__init__.py +0 -0
  41. allora-2.0.0/examples/Sources/ESP32/__init__.py +0 -0
  42. allora-2.0.0/examples/Sources/ESP32/boot.py +3 -0
  43. allora-2.0.0/examples/Sources/ESP32/main_Heltec.py +62 -0
  44. allora-2.0.0/examples/Sources/ESP32/main_TTGO.py +80 -0
  45. allora-2.0.0/examples/Sources/LopySource/__init__.py +0 -0
  46. {AlLoRa-1.2.0/examples/LoPySender → allora-2.0.0/examples/Sources/LopySource}/main.py +1 -1
  47. allora-2.0.0/examples/Sources/T3S3/__init__.py +0 -0
  48. allora-2.0.0/examples/Sources/T3S3/main.py +57 -0
  49. allora-2.0.0/examples/Sources/T3S3/main_pro.py +125 -0
  50. allora-2.0.0/examples/Sources/T3S3-Tester/__init__.py +0 -0
  51. allora-2.0.0/examples/Sources/T3S3-Tester/main.py +75 -0
  52. allora-2.0.0/examples/Sources/XIAO-ESP32S3/__init__.py +0 -0
  53. allora-2.0.0/examples/Sources/XIAO-ESP32S3/main.py +57 -0
  54. allora-2.0.0/examples/Sources/XIAO-ESP32S3/main_pro.py +121 -0
  55. allora-2.0.0/examples/Sources/__init__.py +0 -0
  56. allora-2.0.0/examples/__init__.py +0 -0
  57. {AlLoRa-1.2.0 → allora-2.0.0}/setup.py +4 -4
  58. AlLoRa-1.2.0/AlLoRa/Connectors/Connector.py +0 -120
  59. AlLoRa-1.2.0/AlLoRa/Connectors/SX127x_connector.py +0 -63
  60. AlLoRa-1.2.0/AlLoRa/Connectors/Serial_connector.py +0 -125
  61. AlLoRa-1.2.0/AlLoRa/Connectors/Wifi_connector.py +0 -88
  62. AlLoRa-1.2.0/AlLoRa/Digital_Endpoint.py +0 -117
  63. AlLoRa-1.2.0/AlLoRa/File.py +0 -113
  64. AlLoRa-1.2.0/AlLoRa/Interfaces/Serial_interface.py +0 -150
  65. AlLoRa-1.2.0/AlLoRa/Interfaces/Wifi_client.py +0 -109
  66. AlLoRa-1.2.0/AlLoRa/Interfaces/Wifi_hotspot.py +0 -73
  67. AlLoRa-1.2.0/AlLoRa/Nodes/Gateway.py +0 -73
  68. AlLoRa-1.2.0/AlLoRa/Nodes/Node.py +0 -102
  69. AlLoRa-1.2.0/AlLoRa/Nodes/Requester.py +0 -170
  70. AlLoRa-1.2.0/AlLoRa.egg-info/PKG-INFO +0 -638
  71. AlLoRa-1.2.0/AlLoRa.egg-info/SOURCES.txt +0 -45
  72. AlLoRa-1.2.0/PKG-INFO +0 -638
  73. AlLoRa-1.2.0/README.md +0 -619
  74. AlLoRa-1.2.0/examples/RaspberryGateway/Serial_Gateway/main.py +0 -16
  75. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/Connectors/__init__.py +0 -0
  76. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/DataSource.py +0 -0
  77. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/Interfaces/Interface.py +0 -0
  78. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/Interfaces/__init__.py +0 -0
  79. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/Nodes/__init__.py +0 -0
  80. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa/__init__.py +0 -0
  81. {AlLoRa-1.2.0/examples/Adapters/Serial_adapter → allora-2.0.0/AlLoRa/utils}/__init__.py +0 -0
  82. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa.egg-info/dependency_links.txt +0 -0
  83. {AlLoRa-1.2.0 → allora-2.0.0}/AlLoRa.egg-info/top_level.txt +0 -0
  84. {AlLoRa-1.2.0 → allora-2.0.0}/LICENSE +0 -0
  85. {AlLoRa-1.2.0/examples/Adapters/WiFi_adapters → allora-2.0.0/examples/Adapters/Serial_adapter}/__init__.py +0 -0
  86. {AlLoRa-1.2.0 → allora-2.0.0}/examples/Adapters/Serial_adapter/main.py +0 -0
  87. {AlLoRa-1.2.0/examples/Adapters → allora-2.0.0/examples/Adapters/WiFi_adapters/WiFi-Client}/__init__.py +0 -0
  88. {AlLoRa-1.2.0/examples/LoPyReceiver → allora-2.0.0/examples/Adapters/WiFi_adapters/WiFi-Hotspot}/__init__.py +0 -0
  89. {AlLoRa-1.2.0/examples/LoPySender → allora-2.0.0/examples/Adapters/WiFi_adapters}/__init__.py +0 -0
  90. {AlLoRa-1.2.0/examples/RaspberryGateway/Serial_Gateway → allora-2.0.0/examples/Adapters}/__init__.py +0 -0
  91. {AlLoRa-1.2.0/examples/RaspberryGateway/Wifi_Gateway → allora-2.0.0/examples/Gateways/Serial_Gateway}/__init__.py +0 -0
  92. {AlLoRa-1.2.0/examples/RaspberryGateway → allora-2.0.0/examples/Gateways/Wifi_Gateway}/__init__.py +0 -0
  93. {AlLoRa-1.2.0/examples → allora-2.0.0/examples/Gateways}/__init__.py +0 -0
  94. {AlLoRa-1.2.0/examples/LoPyReceiver → allora-2.0.0/examples/Requesters/LopyRequester}/boot.py +0 -0
  95. {AlLoRa-1.2.0/examples/LoPySender → allora-2.0.0/examples/Sources/LopySource}/boot.py +0 -0
  96. {AlLoRa-1.2.0 → allora-2.0.0}/setup.cfg +0 -0
@@ -0,0 +1,323 @@
1
+ from AlLoRa.Packet import Packet
2
+ import gc
3
+ from math import ceil
4
+ from AlLoRa.utils.time_utils import get_time, current_time_ms as time, sleep, sleep_ms
5
+ from AlLoRa.utils.debug_utils import print
6
+ from AlLoRa.utils.os_utils import os
7
+ from os import urandom
8
+
9
+ class Connector:
10
+ MAX_LENGTH_MESSAGE = 255
11
+
12
+ def __init__(self):
13
+ self.MAC = "00000000"
14
+ self.observed_min_timeout = float('inf')
15
+ self.debug = False
16
+
17
+ def config(self, config_json):
18
+ # JSON Example:
19
+ # {
20
+ # "name": "N",
21
+ # "frequency": 868,
22
+ # "sf": 7,
23
+ # "mesh_mode": false,
24
+ # "debug": false,
25
+ # "min_timeout": 0.5,
26
+ # "max_timeout": 6
27
+ # }
28
+ self.config_parameters = config_json
29
+ if self.config_parameters:
30
+ self.name = self.config_parameters.get('name', "N")
31
+ self.debug = self.config_parameters.get('debug', False)
32
+
33
+ self.frequency = self.config_parameters.get('freq', 868) # 868 MHz
34
+ self.sf = self.config_parameters.get('sf', 7) # SF7
35
+ self.bw = self.config_parameters.get("bandwidth", 125) # 125 kHz
36
+ self.cr = self.config_parameters.get("coding_rate", 1) # 4/5
37
+ self.tx_power = self.config_parameters.get("tx_power", 14) # 14 dBm
38
+
39
+ self.mesh_mode = self.config_parameters.get('mesh_mode', False)
40
+ self.short_mac = self.config_parameters.get('short_mac', False)
41
+
42
+ self.min_timeout = self.config_parameters.get('min_timeout', 0.5)
43
+ self.max_timeout = self.config_parameters.get('max_timeout', 6)
44
+ self.timeout_delta = self.config_parameters.get('timeout_delta', 1) # Delta for processing times
45
+
46
+ # Calculate initial adaptive timeouts
47
+ self.update_timeouts()
48
+
49
+ self.adaptive_timeout = self.max_timeout
50
+ self.backup_timeout = self.adaptive_timeout
51
+ self.backup_rf_config()
52
+ else:
53
+ if self.debug:
54
+ print("Error: No config parameters")
55
+
56
+ def get_max_payload_size(self):
57
+ if self.sf < 11:
58
+ return 255
59
+ elif self.sf == 11:
60
+ return 111
61
+ elif self.sf == 12:
62
+ return 30 #51
63
+
64
+ def update_timeouts(self):
65
+ # Calculate the min and max timeouts based on the ToA for the current RF settings
66
+ self.max_payload_size = self.get_max_payload_size()
67
+ min_toa = self.calculate_toa(self.sf, self.bw, self.cr, self.max_payload_size) # Max payload
68
+ max_toa = min_toa * 2
69
+ self.min_timeout = min_toa + self.timeout_delta # Convert ms to seconds
70
+ self.max_timeout = max_toa + self.timeout_delta # Convert ms to seconds and add delta for processing times
71
+ if self.debug:
72
+ print("Updated timeouts: Min: {} s, Max: {} s".format(self.min_timeout, self.max_timeout))
73
+
74
+ def calculate_toa(self, sf, bw, cr, payload_size):
75
+ if self.debug:
76
+ print("TOA with SF:", sf, "BW:", bw, "CR:", cr, "Payload:", payload_size)
77
+ crc = 1 # CRC enabled
78
+ bw_hz = bw * 1000 # Convert bandwidth to Hz
79
+ t_symbol = (2 ** sf) / bw_hz # Symbol duration
80
+ t_preamble = t_symbol * (8 + 4.25) # Preamble duration
81
+ h = 0 # Implicit header disabled
82
+ de = 1 if (sf >= 11 and bw == 125) else 0 # Low data rate optimization enabled for SF11 and SF12 with 125kHz BW
83
+ cr_rate = cr / 4.0 # Coding rate
84
+ # Payload Symbol Calculation
85
+ payload_bits = 8 * payload_size - 4 * sf + 28 + 16 * (1 if crc else 0) - 20 * h
86
+ bits_per_symbol = 4 * (sf - 2 * de)
87
+ n_payload = 8 + max(0, int(ceil(payload_bits / bits_per_symbol) * (cr_rate + 4)))
88
+
89
+ # Payload Duration
90
+ t_payload = t_symbol * n_payload
91
+
92
+ # Total Time on Air
93
+ t_air = t_preamble + t_payload
94
+ if self.debug:
95
+ print("TOA:", t_air)
96
+
97
+ return t_air
98
+
99
+ def backup_config(self):
100
+ return self.config_parameters
101
+
102
+ def get_mac(self):
103
+ return self.MAC
104
+
105
+ def set_mesh_mode(self, mesh_mode=False):
106
+ self.mesh_mode = mesh_mode
107
+
108
+ def send(self, packet: Packet):
109
+ return None
110
+
111
+ def recv(self, focus_time=12):
112
+ return None
113
+
114
+ def increase_adaptive_timeout(self):
115
+ random_factor = int.from_bytes(urandom(2), "little") / 2**16
116
+ self.adaptive_timeout = min(self.adaptive_timeout * (1 + random_factor), self.max_timeout)
117
+
118
+ def decrease_adaptive_timeout(self, td):
119
+ smoothing_factor = 0.2
120
+ new_timeout = self.adaptive_timeout * (1 - smoothing_factor) + td * smoothing_factor
121
+ self.observed_min_timeout = min(self.observed_min_timeout, td)
122
+ self.adaptive_timeout = max(new_timeout, max(self.min_timeout, self.observed_min_timeout))
123
+
124
+ def send_and_wait_response(self, packet):
125
+ focus_time = self.adaptive_timeout
126
+ packet_size_sent = len(packet.get_content())
127
+ try:
128
+ send_success = self.send(packet)
129
+ if not send_success:
130
+ error_info = {
131
+ "type": "SEND_ERROR",
132
+ "message": "Error sending packet",
133
+ "focus_time": focus_time,
134
+ "adaptive_timeout": self.adaptive_timeout,
135
+ }
136
+ if self.debug:
137
+ print(error_info["message"])
138
+ return error_info, packet_size_sent, 0, 0
139
+ except Exception as e:
140
+ error_info = {
141
+ "type": "EXCEPTION",
142
+ "message": "Exception during send: {}".format(e),
143
+ "focus_time": focus_time,
144
+ "adaptive_timeout": self.adaptive_timeout,
145
+ }
146
+ if self.debug:
147
+ print(error_info["message"])
148
+ self.increase_adaptive_timeout()
149
+ return error_info, packet_size_sent, 0, 0
150
+
151
+ while focus_time > 0:
152
+ t0 = time()
153
+ try:
154
+ received_data = self.recv(focus_time)
155
+ except Exception as e:
156
+ error_info = {
157
+ "type": "EXCEPTION",
158
+ "message": "Exception during recv: {}".format(e),
159
+ "focus_time": focus_time,
160
+ "adaptive_timeout": self.adaptive_timeout,
161
+ }
162
+ if self.debug:
163
+ print(error_info["message"])
164
+ return error_info, packet_size_sent, 0, 0
165
+
166
+ td = (time() - t0) / 1000 # Calculate the time difference in seconds
167
+ packet_size_received = len(received_data) if received_data else 0
168
+
169
+ if not received_data:
170
+ error_info = {
171
+ "type": "TIMEOUT",
172
+ "message": "No response received",
173
+ "focus_time": focus_time,
174
+ "time_difference": td,
175
+ "adaptive_timeout": self.adaptive_timeout,
176
+ }
177
+ if self.debug:
178
+ print(error_info["message"])
179
+ self.increase_adaptive_timeout()
180
+ return error_info, packet_size_sent, packet_size_received, td
181
+
182
+ response_packet = Packet(self.mesh_mode, self.short_mac)
183
+ if self.debug:
184
+ print("WAIT_RESPONSE({}) at: {}|| source_reply: {}".format(td, self.adaptive_timeout, received_data))
185
+ try:
186
+ if response_packet.load(received_data):
187
+ if response_packet.get_source() == packet.get_destination() and response_packet.get_destination() == self.get_mac():
188
+ if len(received_data) > response_packet.HEADER_SIZE + 60: # Hardcoded for only chunks
189
+ self.decrease_adaptive_timeout(td)
190
+ if response_packet.get_debug_hops():
191
+ response_packet.add_hop(self.name, self.get_rssi(), 0)
192
+ return response_packet, packet_size_sent, packet_size_received, td
193
+ else:
194
+ if len(received_data) > 0:
195
+ error_info = {
196
+ "type": "CORRUPTED_PACKET",
197
+ "message": "{}".format(received_data),
198
+ }
199
+ else:
200
+ error_info = {
201
+ "type": "NO_PACKET",
202
+ "message": "No packet received",
203
+ }
204
+ if self.debug:
205
+ print(error_info["message"])
206
+ return error_info, packet_size_sent, packet_size_received, td
207
+
208
+ except Exception as e:
209
+ error_info = {
210
+ "type": "EXCEPTION",
211
+ "message": "Exception during packet load: {}, data: {}".format(e, received_data),
212
+ }
213
+ if self.debug:
214
+ print(error_info["message"])
215
+ return error_info, packet_size_sent, packet_size_received, td
216
+
217
+ focus_time = self.adaptive_timeout - td
218
+ if focus_time < self.min_timeout:
219
+ focus_time = self.min_timeout
220
+ error_info = {
221
+ "type": "MIN_TIMEOUT_REACHED",
222
+ "message": "Minimum timeout reached, can't wait more",
223
+ "focus_time": focus_time,
224
+ }
225
+ if self.debug:
226
+ print(error_info["message"])
227
+ return error_info, packet_size_sent, packet_size_received, td
228
+
229
+ # This function returns the RSSI of the last received packet
230
+ def get_rssi(self):
231
+ return 0
232
+
233
+ # This function returns the SNR of the last received packet
234
+ def get_snr(self):
235
+ return 0
236
+
237
+ def signal_estimation(self):
238
+ percentage = 0
239
+ rssi = self.get_rssi()
240
+ if (rssi >= -50):
241
+ percentage = 100
242
+ elif (rssi <= -50) and (rssi >= -100):
243
+ percentage = 2 * (rssi + 100)
244
+ elif (rssi < 100):
245
+ percentage = 0
246
+ if self.debug:
247
+ print('SIGNAL STRENGTH', percentage, '%')
248
+ return percentage
249
+
250
+ def get_rf_config(self):
251
+ return [self.frequency, self.sf, self.bw, self.cr, self.tx_power]
252
+
253
+ def change_rf_config(self, frequency=None, sf=None, bw=None, cr=None, tx_power=None, backup=True):
254
+ if backup:
255
+ self.backup_rf_config()
256
+ try:
257
+ if frequency is not None:
258
+ self.set_frequency(frequency)
259
+ if sf is not None:
260
+ self.set_sf(sf)
261
+ if bw is not None:
262
+ self.set_bw(bw)
263
+ if cr is not None:
264
+ self.set_cr(cr)
265
+ if tx_power is not None:
266
+ self.set_transmission_power(tx_power)
267
+ self.update_timeouts()
268
+ self.adaptive_timeout = self.max_timeout
269
+ return True
270
+ except Exception as e:
271
+ if self.debug:
272
+ print("Error changing RF config: ", e)
273
+ self.restore_rf_config()
274
+ return False
275
+
276
+ def backup_rf_config(self):
277
+ self.last_rf_config = [self.frequency,
278
+ self.sf,
279
+ self.bw,
280
+ self.cr,
281
+ self.tx_power]
282
+
283
+ def update_rf_params(self, params):
284
+ """
285
+ Updates the connector's RF parameters.
286
+ Override in derived classes if needed.
287
+ """
288
+ self.frequency = params.get("frequency", self.frequency)
289
+ self.sf = params.get("sf", self.sf)
290
+ self.bw = params.get("bw", self.bw)
291
+ self.cr = params.get("cr", self.cr)
292
+ self.tx_power = params.get("tx_power", self.tx_power)
293
+ if self.debug:
294
+ print("Updated RF parameters:", self.get_rf_config())
295
+
296
+ def restore_rf_config(self):
297
+ frequency = self.last_rf_config[0]
298
+ sf = self.last_rf_config[1]
299
+ bw = self.last_rf_config[2]
300
+ cr = self.last_rf_config[3]
301
+ tx_power = self.last_rf_config[4]
302
+ self.change_rf_config(frequency=frequency,
303
+ sf=sf,
304
+ bw=bw,
305
+ cr=cr,
306
+ tx_power=tx_power,
307
+ backup=False)
308
+
309
+
310
+ def set_frequency(self, frequency):
311
+ pass
312
+
313
+ def set_sf(self, sf):
314
+ pass
315
+
316
+ def set_bw(self, bw):
317
+ pass
318
+
319
+ def set_cr(self, cr):
320
+ pass
321
+
322
+ def set_transmission_power(self, tx_power):
323
+ pass
@@ -0,0 +1,339 @@
1
+ import utime
2
+ from machine import UART
3
+ import ubinascii
4
+ # import network
5
+ import re
6
+ from math import ceil
7
+
8
+ from AlLoRa.Packet import Packet
9
+ from AlLoRa.Connectors.Connector import Connector
10
+ from AlLoRa.utils.debug_utils import print
11
+
12
+ class E5_connector(Connector):
13
+ def __init__(self):
14
+ super().__init__()
15
+
16
+ def config(self, config_json):
17
+ super().config(config_json)
18
+ if self.config_parameters:
19
+ connector_config = self.config_parameters
20
+
21
+ self.debug = connector_config.get("debug", True)
22
+
23
+ self.uart_id = connector_config.get("uart_id", 1)
24
+ self.baudrate = connector_config.get("baudrate", 115200)
25
+ self.tx_pin = connector_config.get("tx", 43)
26
+ self.rx_pin = connector_config.get("rx", 44)
27
+ self.bits = connector_config.get("bits", 8)
28
+ self.parity = connector_config.get("parity", None)
29
+ self.stop = connector_config.get("stop", 1)
30
+ self.timeout = connector_config.get("timeout", 300) # Set the UART timeout to 300 ms
31
+ self.txbuf = connector_config.get("txbuf", 150) # TX buffer size
32
+ self.rxbuf = connector_config.get("rxbuf", 150) # RX buffer size
33
+
34
+ self.tx_preamble = connector_config.get("tx_preamble", 8)
35
+ self.rx_preamble = connector_config.get("rx_preamble", 8)
36
+
37
+ self.crc = connector_config.get("crc", "ON")
38
+ self.iq = connector_config.get("iq", "OFF")
39
+ self.net = connector_config.get("net", "ON")
40
+
41
+
42
+ # Initialize UART with the default baud rate
43
+ self.uart = UART(self.uart_id, 9600)
44
+ self.uart.init(baudrate=9600, tx=self.tx_pin, rx=self.rx_pin, bits=self.bits,
45
+ parity=self.parity, stop=self.stop, timeout=self.timeout,
46
+ txbuf=self.txbuf, rxbuf=self.rxbuf)
47
+
48
+ # Set the baud rate to the desired value and restart the module
49
+ self.set_uart_baudrate(self.baudrate)
50
+ self.restart_module()
51
+ # Reinitialize UART with the new baud rate
52
+ self.uart.init(baudrate=self.baudrate, tx=self.tx_pin, rx=self.rx_pin, bits=self.bits, parity=self.parity, stop=self.stop, timeout=self.timeout)
53
+
54
+ # Set the UART timeout
55
+ self.set_uart_timeout(0)
56
+
57
+ # Enter test mode and configure RF settings
58
+ self.enter_test_mode()
59
+ self.set_rf_config(self.frequency, self.sf, self.bw, self.tx_preamble, self.rx_preamble, self.tx_power, self.crc, self.iq, self.net)
60
+ if self.debug:
61
+ print("Configuration: uart_id={}, baudrate={}, tx={}, rx={}, bits={}, parity={}, stop={}, timeout={}".format(
62
+ self.uart_id, self.baudrate, self.tx_pin, self.rx_pin, self.bits, self.parity, self.stop, self.timeout))
63
+ print("RF Configuration: frequency={}, sf={}, bandwidth={}, tx_preamble={}, rx_preamble={}, tx_power={}, crc={}, iq={}, net={}".format(
64
+ self.frequency, self.sf, self.bw, self.tx_preamble, self.rx_preamble, self.tx_power, self.crc, self.iq, self.net))
65
+ self.get_mac_from_module()
66
+
67
+ def get_mac_from_module(self):
68
+ cmd = "AT+ID\r\n"
69
+ expected_response = "+ID: DevEui,"
70
+ default_APP_EUI = "80:00:00:00:00:00:00:06"
71
+ success, response = self.send_command(cmd, expected_response, 500)
72
+ if success:
73
+ if self.debug:
74
+ print("Response:", response.decode())
75
+ mac = response.decode()#.split('+ID: ')[-1].strip()
76
+ obtained_APP_EUI = mac.split('DevEui, ')[1][:23]
77
+ self.MAC = obtained_APP_EUI.replace(':', '').strip()
78
+ if self.debug:
79
+ print("Obtained APP_EUI:", obtained_APP_EUI)
80
+ print("MAC:", self.MAC)
81
+
82
+ def set_dynamic_uart_timeout(self, timeout):
83
+ self.uart.init(baudrate=self.baudrate, tx=self.tx_pin, rx=self.rx_pin, bits=self.bits, parity=self.parity, stop=self.stop, timeout=timeout)
84
+ if self.debug:
85
+ print("UART timeout set to:", timeout)
86
+
87
+ # AT Commands
88
+ def send_command(self, cmd, expected_response, timeout_ms):
89
+ start_time_total = utime.ticks_ms()
90
+
91
+ self.uart.write(cmd)
92
+ utime.sleep_ms(10) # Short sleep to ensure command is processed
93
+
94
+ start_time = utime.ticks_ms()
95
+ response = b''
96
+ while utime.ticks_diff(utime.ticks_ms(), start_time) < timeout_ms:
97
+ if self.uart.any():
98
+ response += self.uart.read()
99
+ if expected_response in response:
100
+ end_time_total = utime.ticks_ms()
101
+ if self.debug:
102
+ print("send_command Total Time:", utime.ticks_diff(end_time_total, start_time_total), "ms")
103
+ return True, response
104
+ end_time_total = utime.ticks_ms()
105
+ if self.debug:
106
+ print("send_command Total Time:", utime.ticks_diff(end_time_total, start_time_total), "ms")
107
+ return False, response
108
+
109
+ def set_uart_baudrate(self, baudrate):
110
+ cmd = "AT+UART=BR,{}\r\n".format(baudrate)
111
+ print("Set Baud Rate Command:", cmd)
112
+ expected_response = "+UART=BR"
113
+ success, response = self.send_command(cmd, expected_response, 500)
114
+ if self.debug:
115
+ print("Set Baud Rate Response:", response.decode())
116
+ return success
117
+
118
+ def set_uart_timeout(self, timeout):
119
+ cmd = "AT+UART=TIMEOUT,{}\r\n".format(timeout)
120
+ if self.debug:
121
+ print("Set Timeout Command:", cmd)
122
+ expected_response = "+UART: TIMEOUT, {}".format(timeout)
123
+ success, response = self.send_command(cmd, expected_response, 500)
124
+ if self.debug:
125
+ print("Set Timeout Response:", response.decode(), "Success:", success)
126
+ return success
127
+
128
+ def restart_module(self):
129
+ # Send a command to reset the module (if available) or reinitialize the UART
130
+ cmd = "AT+RESET\r\n"
131
+ if self.debug:
132
+ print("Restart Module Command:", cmd)
133
+ expected_response = "+RESET"
134
+ success, response = self.send_command(cmd, expected_response, 1000)
135
+ if self.debug:
136
+ print("Restart Module Response:", response.decode())
137
+ utime.sleep(1) # Wait for the module to reset
138
+ return success
139
+
140
+ def enter_test_mode(self):
141
+ cmd = "AT+MODE=TEST\r\n"
142
+ expected_response = "+MODE: TEST"
143
+ success, response = self.send_command(cmd, expected_response, 2000)
144
+ if self.debug:
145
+ print("Enter Test Mode Response:", response.decode())
146
+ return success
147
+
148
+ def receive_packet(self, timeout=10000, chunk_size=124):
149
+ cmd = "AT+TEST=RXLRPKT\r\n"
150
+ success = self.uart.write(cmd)
151
+
152
+ t0_ack = utime.ticks_ms()
153
+ ack = self.uart.read(32)
154
+ start_time_total = utime.ticks_ms()
155
+ response = b''
156
+
157
+ while utime.ticks_diff(utime.ticks_ms(), start_time_total) < timeout:
158
+ if self.uart.any():
159
+ response += self.uart.read(min(chunk_size, self.uart.any()))
160
+ if response.endswith(b'"\r\n'):
161
+ break
162
+ else:
163
+ utime.sleep_ms(1)
164
+
165
+ end_time_total = utime.ticks_ms()
166
+ if self.debug:
167
+ print("receive_packet Total Time:", utime.ticks_diff(end_time_total, start_time_total), "ms")
168
+
169
+ return response if response else None
170
+
171
+ def send_packet(self, data):
172
+ cmd = 'AT+TEST=TXLRPKT,"{}"\r\n'.format(data)
173
+ start_time_total = utime.ticks_ms()
174
+ expected_response = "+TEST: TXLRPKT" #expected_response = "+TEST: TX DONE\r\n" #
175
+ sfs = {8: 2, 9: 4, 10: 8, 11: 16, 12: 32}
176
+ sf_changed = False
177
+ if self.sf > 7:
178
+ self.set_dynamic_uart_timeout(ceil((self.timeout * sfs[self.sf])*1.1))
179
+ sf_changed = True
180
+
181
+ wt0 = utime.ticks_ms()
182
+ success = self.uart.write(cmd)
183
+ wt1 = utime.ticks_ms()
184
+ print("Time to write:", utime.ticks_diff(wt1, wt0), "ms")
185
+ start_time = utime.ticks_ms()
186
+ #response = b''
187
+ response = self.uart.read()
188
+ if sf_changed:
189
+ self.set_dynamic_uart_timeout(self.timeout) # Set the UART timeout back to the default value
190
+ #expected_response = "+TEST: TXLRPKT"
191
+
192
+ end_time_total = utime.ticks_ms()
193
+ if self.debug:
194
+ print("send_packet Total Time:", utime.ticks_diff(end_time_total, start_time_total), "ms")
195
+ try:
196
+ if expected_response in response:
197
+ return True
198
+ else:
199
+ return False
200
+ except:
201
+ return False
202
+
203
+ # Packet Handling
204
+ def hex_to_bytes(self, hex_str):
205
+ hex_str = hex_str.replace('"', '').replace('\n', '').replace(' ', '').strip()
206
+ if len(hex_str) % 2 != 0:
207
+ raise ValueError("odd-length string")
208
+ return bytes.fromhex(hex_str)
209
+
210
+ def bytes_to_hex(self, byte_str):
211
+ return byte_str.hex()
212
+
213
+ def extract_packet_info(self, response):
214
+ # time_0 = utime.ticks_ms()
215
+ rssi_match = re.search(r'RSSI:(-?\d+)', response)
216
+ snr_match = re.search(r'SNR:(-?\d+)', response)
217
+
218
+ if rssi_match:
219
+ try:
220
+ self.rssi = int(rssi_match.group(1))
221
+ except ValueError:
222
+ self.rssi = None
223
+ else:
224
+ self.rssi = None
225
+
226
+ if snr_match:
227
+ try:
228
+ self.snr = int(snr_match.group(1))
229
+ except ValueError:
230
+ self.snr = None
231
+
232
+ else:
233
+ self.snr = None
234
+
235
+ # Connector Methods
236
+ def send(self, packet):
237
+ data = packet.get_content()
238
+ hex_data = self.bytes_to_hex(data)
239
+ hex_data = hex_data.strip().strip('"')
240
+
241
+ success = self.send_packet(hex_data)
242
+
243
+ return success
244
+
245
+ def recv(self, focus_time=12):
246
+ rt_0 = utime.ticks_ms()
247
+ packet_data = self.receive_packet(focus_time * 1000)
248
+ if packet_data:
249
+ try:
250
+ self.extract_packet_info(packet_data)
251
+ hex_data = packet_data.decode().split('RX ')[-1].strip().strip('"')
252
+ bytes_data = self.hex_to_bytes(hex_data)
253
+ return bytes_data
254
+ except (ValueError, IndexError) as e:
255
+ if self.debug:
256
+ print("Error decoding packet:", e)
257
+ return None
258
+
259
+ def get_rssi(self):
260
+ return self.rssi
261
+
262
+ def get_snr(self):
263
+ return self.snr
264
+
265
+ def change_rf_config(self, frequency=None, sf=None, bw=None, cr=None, tx_power=None, backup=True):
266
+ if self.debug:
267
+ print("Changing RF Config to: ", frequency, sf, bw, cr, tx_power)
268
+ if backup:
269
+ self.backup_rf_config()
270
+ freq = frequency if frequency is not None else self.frequency
271
+ sf = sf if sf is not None else self.sf
272
+ bandwidth = bw if bw is not None else self.bw
273
+ cr = cr if cr is not None else self.cr
274
+ tx_power = tx_power if tx_power is not None else self.tx_power
275
+ success = self.set_rf_config(freq, sf, bandwidth, self.tx_preamble, self.rx_preamble, tx_power, self.crc, self.iq, self.net)
276
+ if success:
277
+ if self.debug:
278
+ print("RF Config Changed to: ", freq, sf, bandwidth, cr, tx_power)
279
+ self.update_timeouts()
280
+ self.adaptive_timeout = self.max_timeout
281
+ return True
282
+ else:
283
+ self.restore_rf_config()
284
+ return False
285
+
286
+ def set_rf_config(self, frequency, sf, bandwidth, tx_preamble, rx_preamble, tx_power, crc, iq, net):
287
+ cmd = "AT+TEST=RFCFG,{},SF{},{},{},{},{},{},{},{}\r\n".format(frequency, sf, bandwidth, tx_preamble, rx_preamble, tx_power, crc, iq, net)
288
+ print(cmd)
289
+ expected_response = "+TEST: RFCFG"
290
+ success, response = self.send_command(cmd, expected_response, 2000)
291
+ if self.debug:
292
+ print("Set RF Config Response:", response.decode())
293
+ if success:
294
+ if self.debug:
295
+ print("RF Config Set to:", frequency, sf, bandwidth, tx_preamble, rx_preamble, tx_power, crc, iq, net)
296
+ self.frequency = frequency
297
+ self.sf = sf
298
+ self.bw = bandwidth
299
+ self.tx_preamble = tx_preamble
300
+ self.rx_preamble = rx_preamble
301
+ self.tx_power = tx_power
302
+ self.crc = crc
303
+ self.iq = iq
304
+ self.net = net
305
+ return success
306
+
307
+ def set_frequency(self, frequency):
308
+ success = self.set_rf_config(frequency, "SF" + str(self.sf), self.bw, self.tx_preamble, self.rx_preamble, self.tx_power, self.crc, self.iq, self.net)
309
+ if success:
310
+ if self.debug:
311
+ print("Frequency Set to:", frequency)
312
+ return success
313
+
314
+ def set_sf(self, sf):
315
+ success = self.set_rf_config(self.frequency, "SF" + str(self.sf), self.bw, self.tx_preamble, self.rx_preamble, self.tx_power, self.crc, self.iq, self.net)
316
+ if success:
317
+ if self.debug:
318
+ print("SF Changed to:", sf)
319
+ return success
320
+
321
+ def set_bw(self, bw):
322
+ success = self.set_rf_config(self.frequency, "SF" + str(self.sf), bw, self.tx_preamble, self.rx_preamble, self.tx_power, self.crc, self.iq, self.net)
323
+ if success:
324
+ if self.debug:
325
+ print("Bandwidth Set to:", bw)
326
+ return success
327
+
328
+ def set_cr(self, cr):
329
+ # CR is not configurable in E5 module
330
+ if self.debug:
331
+ print("CR is not configurable in E5 module")
332
+ return False
333
+
334
+ def set_transmission_power(self, desired_power):
335
+ success = self.set_rf_config(self.frequency, "SF" + str(self.sf), self.bw, self.tx_preamble, self.rx_preamble, desired_power, self.crc, self.iq, self.net)
336
+ if success:
337
+ if self.debug:
338
+ print("Transmission Power Set to:", desired_power)
339
+ return success
@@ -2,6 +2,7 @@ from time import sleep
2
2
 
3
3
  from AlLoRa.Packet import Packet
4
4
  from AlLoRa.Connectors.Connector import Connector
5
+ from AlLoRa.utils.debug_utils import print
5
6
  from network import LoRa
6
7
  import socket
7
8
  import binascii