meshtastic 0.0.176 → 0.0.177
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +0 -21
- data/Gemfile +1 -0
- data/README.md +79 -82
- data/lib/meshtastic/bluetooth/bluez.rb +187 -0
- data/lib/meshtastic/bluetooth.rb +299 -0
- data/lib/meshtastic/lorawan_bridge_pb.rb +20 -0
- data/lib/meshtastic/mesh_interface.rb +3 -1
- data/lib/meshtastic/mqtt.rb +7 -34
- data/lib/meshtastic/{serial_interface.rb → serial.rb} +144 -106
- data/lib/meshtastic/stream_interface.rb +1 -1
- data/lib/meshtastic/version.rb +1 -1
- data/lib/meshtastic.rb +3 -1
- data/spec/lib/meshtastic/bluetooth/bluez_spec.rb +190 -0
- data/spec/lib/meshtastic/bluetooth_spec.rb +146 -0
- data/spec/lib/meshtastic/lorawan_bridge_pb_spec.rb +6 -0
- data/spec/lib/meshtastic/mqtt_spec.rb +160 -1
- data/spec/lib/meshtastic/serial_spec.rb +295 -0
- metadata +23 -3
- data/spec/lib/meshtastic/serial_interface_spec.rb +0 -78
|
@@ -13,12 +13,8 @@ require 'uart'
|
|
|
13
13
|
# Wire protocol matches the official Python client:
|
|
14
14
|
# [START1=0x94][START2=0xC3][len_hi][len_lo] + protobuf(ToRadio|FromRadio)
|
|
15
15
|
module Meshtastic
|
|
16
|
-
module
|
|
17
|
-
@
|
|
18
|
-
@proto_data = []
|
|
19
|
-
@from_radio_queue = nil
|
|
20
|
-
@rx_mutex = Mutex.new
|
|
21
|
-
@want_exit = false
|
|
16
|
+
module Serial # rubocop:disable Metrics/ModuleLength
|
|
17
|
+
@last_serial_obj = nil
|
|
22
18
|
|
|
23
19
|
module_function
|
|
24
20
|
|
|
@@ -50,23 +46,25 @@ module Meshtastic
|
|
|
50
46
|
serial_obj = opts[:serial_obj]
|
|
51
47
|
debug_out = opts[:debug_out]
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
serial_obj[:from_radio_queue] = Queue.new
|
|
50
|
+
serial_obj[:config_queue] = Queue.new
|
|
51
|
+
serial_obj[:console_data] = []
|
|
52
|
+
serial_obj[:proto_data] = []
|
|
53
|
+
serial_obj[:rx_mutex] = Mutex.new
|
|
57
54
|
|
|
58
55
|
Thread.new do
|
|
59
56
|
Thread.current.abort_on_exception = false
|
|
60
57
|
rx_buf = +''.b
|
|
61
58
|
empty = +''.b
|
|
62
59
|
|
|
63
|
-
until
|
|
60
|
+
until serial_obj[:closing]
|
|
64
61
|
begin
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
next unless serial_conn.wait_readable(0.1)
|
|
63
|
+
|
|
64
|
+
chunk = serial_conn.read_nonblock(1, exception: false)
|
|
65
|
+
next if chunk == :wait_readable
|
|
66
|
+
|
|
67
|
+
raise EOFError, 'serial device disconnected' if chunk.nil? || chunk.empty?
|
|
70
68
|
|
|
71
69
|
c = chunk.getbyte(0)
|
|
72
70
|
rx_buf << chunk
|
|
@@ -76,11 +74,13 @@ module Meshtastic
|
|
|
76
74
|
# looking for START1
|
|
77
75
|
unless c == Meshtastic::START1
|
|
78
76
|
rx_buf = empty.dup
|
|
79
|
-
append_console_byte(chunk, debug_out)
|
|
77
|
+
append_console_byte(chunk, debug_out, serial_obj)
|
|
80
78
|
end
|
|
81
79
|
elsif ptr == 1
|
|
82
80
|
# looking for START2
|
|
83
|
-
|
|
81
|
+
unless c == Meshtastic::START2
|
|
82
|
+
rx_buf = c == Meshtastic::START1 ? chunk.dup : empty.dup
|
|
83
|
+
end
|
|
84
84
|
elsif ptr >= (Meshtastic::HEADER_LEN - 1)
|
|
85
85
|
packet_len = (rx_buf.getbyte(2) << 8) + rx_buf.getbyte(3)
|
|
86
86
|
|
|
@@ -95,20 +95,22 @@ module Meshtastic
|
|
|
95
95
|
handle_from_radio_bytes(payload: payload, serial_obj: serial_obj)
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
|
-
rescue IOError,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
sleep 0.05
|
|
98
|
+
rescue IOError, SystemCallError => e
|
|
99
|
+
serial_obj[:rx_error] = IOError.new("Serial receive failed: #{e.message}") unless serial_obj[:closing]
|
|
100
|
+
break
|
|
102
101
|
rescue StandardError => e
|
|
103
|
-
warn "Meshtastic::
|
|
102
|
+
warn "Meshtastic::Serial RX error: #{e.class}: #{e.message}" unless serial_obj[:closing]
|
|
104
103
|
sleep 0.05
|
|
105
104
|
end
|
|
106
105
|
end
|
|
106
|
+
ensure
|
|
107
|
+
serial_obj[:from_radio_queue].close
|
|
108
|
+
serial_obj[:config_queue].close
|
|
107
109
|
end
|
|
108
110
|
end
|
|
109
111
|
|
|
110
112
|
|
|
111
|
-
private_class_method def self.append_console_byte(chunk, debug_out)
|
|
113
|
+
private_class_method def self.append_console_byte(chunk, debug_out, serial_obj)
|
|
112
114
|
if debug_out
|
|
113
115
|
begin
|
|
114
116
|
debug_out.write(chunk.force_encoding('UTF-8'))
|
|
@@ -116,7 +118,7 @@ module Meshtastic
|
|
|
116
118
|
debug_out.write('?')
|
|
117
119
|
end
|
|
118
120
|
else
|
|
119
|
-
|
|
121
|
+
serial_obj[:rx_mutex].synchronize { serial_obj[:console_data] << chunk.force_encoding('UTF-8') }
|
|
120
122
|
end
|
|
121
123
|
end
|
|
122
124
|
|
|
@@ -128,8 +130,7 @@ module Meshtastic
|
|
|
128
130
|
from_radio = Meshtastic::FromRadio.decode(payload)
|
|
129
131
|
hash = from_radio.to_h
|
|
130
132
|
|
|
131
|
-
|
|
132
|
-
@from_radio_queue << from_radio if @from_radio_queue
|
|
133
|
+
serial_obj[:rx_mutex].synchronize { serial_obj[:proto_data] << hash }
|
|
133
134
|
|
|
134
135
|
# Cache useful device identity on the serial_obj handle.
|
|
135
136
|
if serial_obj && from_radio.my_info
|
|
@@ -137,22 +138,27 @@ module Meshtastic
|
|
|
137
138
|
serial_obj[:my_node_num] = from_radio.my_info.my_node_num
|
|
138
139
|
end
|
|
139
140
|
serial_obj[:metadata] = from_radio.metadata.to_h if serial_obj && from_radio.metadata
|
|
141
|
+
if from_radio.payload_variant == :config_complete_id && from_radio.config_complete_id == serial_obj[:config_id]
|
|
142
|
+
serial_obj[:config_complete] = true
|
|
143
|
+
serial_obj[:config_queue].close
|
|
144
|
+
end
|
|
140
145
|
|
|
141
146
|
if from_radio.log_record
|
|
142
147
|
msg = from_radio.log_record.message.to_s
|
|
143
|
-
|
|
148
|
+
serial_obj[:rx_mutex].synchronize { serial_obj[:console_data] << "#{msg}\n" } unless msg.empty?
|
|
144
149
|
end
|
|
145
150
|
|
|
151
|
+
serial_obj[:from_radio_queue] << from_radio
|
|
146
152
|
from_radio
|
|
147
153
|
rescue Google::Protobuf::ParseError => e
|
|
148
|
-
warn "Meshtastic::
|
|
154
|
+
warn "Meshtastic::Serial: failed to decode FromRadio (#{e.message})"
|
|
149
155
|
nil
|
|
150
156
|
end
|
|
151
157
|
|
|
152
158
|
# ---- public API ----------------------------------------------------------
|
|
153
159
|
|
|
154
160
|
# Supported Method Parameters::
|
|
155
|
-
# Meshtastic::
|
|
161
|
+
# Meshtastic::Serial.request(
|
|
156
162
|
# serial_obj: 'required serial_obj returned from #connect method',
|
|
157
163
|
# payload: 'required - array of bytes OR string to write to serial device'
|
|
158
164
|
# )
|
|
@@ -169,8 +175,17 @@ module Meshtastic
|
|
|
169
175
|
raise "ERROR: Invalid payload type: #{payload.class}"
|
|
170
176
|
end
|
|
171
177
|
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
serial_obj[:tx_mutex] ||= Mutex.new
|
|
179
|
+
serial_obj[:tx_mutex].synchronize do
|
|
180
|
+
offset = 0
|
|
181
|
+
while offset < bytes.bytesize
|
|
182
|
+
count = serial_conn.write(bytes.byteslice(offset, bytes.bytesize - offset))
|
|
183
|
+
raise IOError, 'serial write made no progress' unless count && count.positive?
|
|
184
|
+
|
|
185
|
+
offset += count
|
|
186
|
+
end
|
|
187
|
+
serial_conn.flush
|
|
188
|
+
end
|
|
174
189
|
sleep 0.05
|
|
175
190
|
bytes.bytesize
|
|
176
191
|
rescue StandardError => e
|
|
@@ -179,7 +194,7 @@ module Meshtastic
|
|
|
179
194
|
end
|
|
180
195
|
|
|
181
196
|
# Supported Method Parameters::
|
|
182
|
-
# Meshtastic::
|
|
197
|
+
# Meshtastic::Serial.send_to_radio(
|
|
183
198
|
# serial_obj: 'required - serial_obj returned from #connect method',
|
|
184
199
|
# to_radio: 'required - Meshtastic::ToRadio OR already-serialized String'
|
|
185
200
|
# )
|
|
@@ -213,7 +228,7 @@ module Meshtastic
|
|
|
213
228
|
end
|
|
214
229
|
|
|
215
230
|
# Supported Method Parameters::
|
|
216
|
-
# serial_obj = Meshtastic::
|
|
231
|
+
# serial_obj = Meshtastic::Serial.connect(
|
|
217
232
|
# block_dev: 'optional - serial block device path (defaults to /dev/ttyUSB0)',
|
|
218
233
|
# baud: 'optional - (defaults to 115200)',
|
|
219
234
|
# data_bits: 'optional - (defaults to 8)',
|
|
@@ -252,6 +267,7 @@ module Meshtastic
|
|
|
252
267
|
serial_conn: serial_conn,
|
|
253
268
|
block_dev: block_dev,
|
|
254
269
|
baud: baud,
|
|
270
|
+
tx_mutex: Mutex.new,
|
|
255
271
|
my_info: nil,
|
|
256
272
|
my_node_num: nil,
|
|
257
273
|
metadata: nil
|
|
@@ -262,6 +278,7 @@ module Meshtastic
|
|
|
262
278
|
serial_obj: serial_obj,
|
|
263
279
|
debug_out: debug_out
|
|
264
280
|
)
|
|
281
|
+
@last_serial_obj = serial_obj
|
|
265
282
|
|
|
266
283
|
# Wake / resync the device's framing state-machine.
|
|
267
284
|
wake_up_device(serial_obj: serial_obj)
|
|
@@ -269,6 +286,7 @@ module Meshtastic
|
|
|
269
286
|
if want_config
|
|
270
287
|
mui = Meshtastic::MeshInterface.new
|
|
271
288
|
to_radio_bytes = mui.start_config
|
|
289
|
+
serial_obj[:config_id] = mui.config_id
|
|
272
290
|
send_to_radio(serial_obj: serial_obj, to_radio: to_radio_bytes)
|
|
273
291
|
end
|
|
274
292
|
|
|
@@ -278,6 +296,20 @@ module Meshtastic
|
|
|
278
296
|
raise e
|
|
279
297
|
end
|
|
280
298
|
|
|
299
|
+
# Wait for the requested configuration without consuming application messages.
|
|
300
|
+
# Raises Timeout::Error if the firmware does not complete the handshake.
|
|
301
|
+
public_class_method def self.wait_for_config(opts = {})
|
|
302
|
+
serial_obj = opts[:serial_obj]
|
|
303
|
+
raise ArgumentError, 'serial_obj with want_config enabled is required' unless serial_obj && serial_obj[:config_id]
|
|
304
|
+
|
|
305
|
+
serial_obj[:config_queue].pop(timeout: opts.fetch(:timeout, 10))
|
|
306
|
+
raise serial_obj[:rx_error] if serial_obj[:rx_error]
|
|
307
|
+
raise IOError, 'serial connection closed' if serial_obj[:closing]
|
|
308
|
+
raise Timeout::Error, "No configuration response from #{serial_obj[:block_dev]}" unless serial_obj[:config_complete]
|
|
309
|
+
|
|
310
|
+
serial_obj
|
|
311
|
+
end
|
|
312
|
+
|
|
281
313
|
# Supported Method Parameters::
|
|
282
314
|
# wake_up_device(
|
|
283
315
|
# serial_obj: 'required - serial_obj returned from #connect method'
|
|
@@ -294,30 +326,28 @@ module Meshtastic
|
|
|
294
326
|
end
|
|
295
327
|
|
|
296
328
|
# Supported Method Parameters::
|
|
297
|
-
# stdout_data = Meshtastic::
|
|
329
|
+
# stdout_data = Meshtastic::Serial.dump_stdout_data(
|
|
298
330
|
# type: 'required - :proto or :console'
|
|
299
331
|
# )
|
|
300
|
-
public_class_method def self.dump_stdout_data(opts = {})
|
|
332
|
+
public_class_method def self.dump_stdout_data(opts = {}, &)
|
|
301
333
|
type = opts[:type]
|
|
302
334
|
valid_types = %i[proto console]
|
|
303
335
|
raise "ERROR: Invalid type: #{type}. Supported types are :proto or :console" unless valid_types.include?(type)
|
|
304
336
|
|
|
305
|
-
@
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
@console_data.join.split("\n").each { |line| yield line.force_encoding('UTF-8') }
|
|
311
|
-
end
|
|
312
|
-
nil
|
|
313
|
-
else
|
|
314
|
-
type == :proto ? @proto_data.dup : @console_data.join
|
|
315
|
-
end
|
|
337
|
+
serial_obj = opts[:serial_obj] || @last_serial_obj
|
|
338
|
+
raise 'ERROR: call connect first' unless serial_obj
|
|
339
|
+
|
|
340
|
+
data = serial_obj[:rx_mutex].synchronize do
|
|
341
|
+
type == :proto ? serial_obj[:proto_data].dup : serial_obj[:console_data].join
|
|
316
342
|
end
|
|
343
|
+
return data unless block_given?
|
|
344
|
+
|
|
345
|
+
(type == :proto ? data : data.split("\n")).each(&)
|
|
346
|
+
nil
|
|
317
347
|
end
|
|
318
348
|
|
|
319
349
|
# Supported Method Parameters::
|
|
320
|
-
# Meshtastic::
|
|
350
|
+
# Meshtastic::Serial.flush_data(
|
|
321
351
|
# type: 'required - :proto or :console'
|
|
322
352
|
# )
|
|
323
353
|
public_class_method def self.flush_data(opts = {}) # rubocop:disable Naming/PredicateMethod
|
|
@@ -325,9 +355,12 @@ module Meshtastic
|
|
|
325
355
|
valid_types = %i[proto console]
|
|
326
356
|
raise "ERROR: Invalid type: #{type}. Supported types are :proto or :console" unless valid_types.include?(type)
|
|
327
357
|
|
|
328
|
-
@
|
|
329
|
-
|
|
330
|
-
|
|
358
|
+
serial_obj = opts[:serial_obj] || @last_serial_obj
|
|
359
|
+
raise 'ERROR: call connect first' unless serial_obj
|
|
360
|
+
|
|
361
|
+
serial_obj[:rx_mutex].synchronize do
|
|
362
|
+
serial_obj[:console_data].clear if type == :console
|
|
363
|
+
serial_obj[:proto_data].clear if type == :proto
|
|
331
364
|
end
|
|
332
365
|
true
|
|
333
366
|
end
|
|
@@ -336,34 +369,40 @@ module Meshtastic
|
|
|
336
369
|
public_class_method def self.drain_from_radio(opts = {})
|
|
337
370
|
max = opts[:max] ||= 256
|
|
338
371
|
msgs = []
|
|
339
|
-
|
|
372
|
+
serial_obj = opts[:serial_obj] || @last_serial_obj
|
|
373
|
+
queue = serial_obj && serial_obj[:from_radio_queue]
|
|
374
|
+
return msgs unless queue
|
|
340
375
|
|
|
341
376
|
max.times do
|
|
342
|
-
|
|
377
|
+
message = queue.pop(true)
|
|
378
|
+
break unless message
|
|
379
|
+
|
|
380
|
+
msgs << message
|
|
343
381
|
rescue ThreadError
|
|
344
|
-
|
|
382
|
+
break
|
|
345
383
|
end
|
|
346
384
|
msgs
|
|
347
385
|
end
|
|
348
386
|
|
|
349
387
|
# Block until a FromRadio arrives or timeout (seconds). Returns FromRadio or nil.
|
|
350
388
|
public_class_method def self.recv_from_radio(opts = {})
|
|
351
|
-
timeout = opts
|
|
352
|
-
|
|
389
|
+
timeout = opts.fetch(:timeout, 5)
|
|
390
|
+
serial_obj = opts[:serial_obj] || @last_serial_obj
|
|
391
|
+
queue = serial_obj && serial_obj[:from_radio_queue]
|
|
392
|
+
raise 'ERROR: RX queue not initialised — call connect first' unless queue
|
|
393
|
+
|
|
394
|
+
message = if timeout.nil? || timeout.negative?
|
|
395
|
+
queue.pop
|
|
396
|
+
else
|
|
397
|
+
queue.pop(timeout: timeout)
|
|
398
|
+
end
|
|
399
|
+
raise serial_obj[:rx_error] if message.nil? && serial_obj[:rx_error]
|
|
353
400
|
|
|
354
|
-
|
|
355
|
-
@from_radio_queue.pop
|
|
356
|
-
else
|
|
357
|
-
begin
|
|
358
|
-
Timeout.timeout(timeout) { @from_radio_queue.pop }
|
|
359
|
-
rescue Timeout::Error
|
|
360
|
-
nil
|
|
361
|
-
end
|
|
362
|
-
end
|
|
401
|
+
message
|
|
363
402
|
end
|
|
364
403
|
|
|
365
404
|
# Supported Method Parameters::
|
|
366
|
-
# Meshtastic::
|
|
405
|
+
# Meshtastic::Serial.monitor_stdout(
|
|
367
406
|
# serial_obj: 'required - serial_obj returned from #connect method',
|
|
368
407
|
# type: 'required - :proto or :console',
|
|
369
408
|
# refresh: 'optional - refresh interval (default: 3)',
|
|
@@ -384,15 +423,15 @@ module Meshtastic
|
|
|
384
423
|
exclude_arr = exclude.to_s.split(',').map(&:strip)
|
|
385
424
|
include_arr = include.to_s.split(',').map(&:strip)
|
|
386
425
|
|
|
387
|
-
dump_stdout_data(type: type) do |data|
|
|
426
|
+
dump_stdout_data(serial_obj: serial_obj, type: type) do |data|
|
|
388
427
|
data_s = data.is_a?(Hash) ? data.inspect : data.to_s
|
|
389
|
-
disp =
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
428
|
+
disp = exclude_arr.none? { |exc| data_s.include?(exc) } && (
|
|
429
|
+
include_arr.empty? ||
|
|
430
|
+
include_arr.all? { |inc| data_s.include?(inc) }
|
|
431
|
+
)
|
|
393
432
|
puts data_s if disp
|
|
394
433
|
end
|
|
395
|
-
flush_data(type: type)
|
|
434
|
+
flush_data(serial_obj: serial_obj, type: type)
|
|
396
435
|
sleep refresh
|
|
397
436
|
end
|
|
398
437
|
rescue Interrupt
|
|
@@ -457,7 +496,7 @@ module Meshtastic
|
|
|
457
496
|
end
|
|
458
497
|
|
|
459
498
|
# Supported Method Parameters::
|
|
460
|
-
# Meshtastic::
|
|
499
|
+
# Meshtastic::Serial.subscribe(
|
|
461
500
|
# serial_obj: 'required - serial_obj returned from #connect method',
|
|
462
501
|
# psks: 'optional - hash of :channel_id => psk (default: { LongFast: "AQ==" })',
|
|
463
502
|
# exclude: 'optional - comma-delimited substrings to hide',
|
|
@@ -491,12 +530,9 @@ module Meshtastic
|
|
|
491
530
|
puts 'Subscribing to serial FromRadio stream...'
|
|
492
531
|
|
|
493
532
|
loop do
|
|
494
|
-
from_radio =
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
else
|
|
498
|
-
@from_radio_queue.pop
|
|
499
|
-
end
|
|
533
|
+
from_radio = recv_from_radio(serial_obj: serial_obj, timeout: timeout)
|
|
534
|
+
break if from_radio.nil? && serial_obj[:from_radio_queue].closed?
|
|
535
|
+
|
|
500
536
|
next if from_radio.nil?
|
|
501
537
|
|
|
502
538
|
begin
|
|
@@ -536,7 +572,7 @@ module Meshtastic
|
|
|
536
572
|
flat_message = flat_source.values.join(' ')
|
|
537
573
|
flat_message = "#{flat_message} #{message.values.join(' ')}" if message.is_a?(Hash)
|
|
538
574
|
|
|
539
|
-
disp =
|
|
575
|
+
disp = exclude_arr.none? { |exc| flat_message.include?(exc) } &&
|
|
540
576
|
include_arr.all? { |inc| flat_message.include?(inc) }
|
|
541
577
|
|
|
542
578
|
if disp
|
|
@@ -562,9 +598,9 @@ module Meshtastic
|
|
|
562
598
|
end
|
|
563
599
|
|
|
564
600
|
# Supported Method Parameters::
|
|
565
|
-
# Meshtastic::
|
|
601
|
+
# Meshtastic::Serial.send_text(
|
|
566
602
|
# serial_obj: 'required - serial_obj returned from #connect method',
|
|
567
|
-
# from: 'optional - From ID (Default: local my_node_num or
|
|
603
|
+
# from: 'optional - From ID (Default: local my_node_num or 0 for firmware-assigned)',
|
|
568
604
|
# to: 'optional - Destination ID (Default: "!ffffffff")',
|
|
569
605
|
# channel: 'optional - channel index (Default: 0)',
|
|
570
606
|
# text: 'optional - Text Message (Default: SYN)',
|
|
@@ -581,29 +617,22 @@ module Meshtastic
|
|
|
581
617
|
opts[:via] = :radio
|
|
582
618
|
opts[:channel] ||= 0
|
|
583
619
|
|
|
584
|
-
if opts[:from].nil?
|
|
585
|
-
opts[:from] =
|
|
586
|
-
if serial_obj[:my_node_num]
|
|
587
|
-
"!#{serial_obj[:my_node_num].to_s(16)}"
|
|
588
|
-
else
|
|
589
|
-
'!00000b0b'
|
|
590
|
-
end
|
|
591
|
-
end
|
|
620
|
+
opts[:from] = serial_obj[:my_node_num] || 0 if opts[:from].nil?
|
|
592
621
|
|
|
593
622
|
# Device performs channel encryption for serial ToRadio packets.
|
|
594
|
-
# Pass
|
|
623
|
+
# Pass nil psks so MeshInterface leaves the payload in :decoded form.
|
|
595
624
|
opts[:psks] = nil
|
|
625
|
+
opts[:text] = opts.fetch(:text, 'SYN').to_s
|
|
626
|
+
max_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
|
|
627
|
+
raise ArgumentError, "ERROR: Text Length > #{max_len} Bytes" if opts[:text].bytesize > max_len
|
|
596
628
|
|
|
597
629
|
mui = Meshtastic::MeshInterface.new
|
|
598
630
|
protobuf = mui.send_text(opts)
|
|
599
631
|
send_to_radio(serial_obj: serial_obj, to_radio: protobuf)
|
|
600
|
-
rescue StandardError => e
|
|
601
|
-
disconnect(serial_obj: serial_obj) unless serial_obj.nil?
|
|
602
|
-
raise e
|
|
603
632
|
end
|
|
604
633
|
|
|
605
634
|
# Supported Method Parameters::
|
|
606
|
-
# Meshtastic::
|
|
635
|
+
# Meshtastic::Serial.send_data(
|
|
607
636
|
# serial_obj: 'required - serial_obj returned from #connect method',
|
|
608
637
|
# ...same kwargs as MeshInterface#send_data (via forced to :radio)
|
|
609
638
|
# )
|
|
@@ -615,25 +644,24 @@ module Meshtastic
|
|
|
615
644
|
opts[:via] = :radio
|
|
616
645
|
opts[:channel] ||= 0
|
|
617
646
|
opts[:psks] = nil
|
|
618
|
-
opts[:from] =
|
|
647
|
+
opts[:from] = serial_obj[:my_node_num] || 0 if opts[:from].nil?
|
|
619
648
|
|
|
620
649
|
mui = Meshtastic::MeshInterface.new
|
|
621
650
|
protobuf = mui.send_data(opts)
|
|
622
651
|
send_to_radio(serial_obj: serial_obj, to_radio: protobuf)
|
|
623
|
-
rescue StandardError => e
|
|
624
|
-
disconnect(serial_obj: serial_obj) unless serial_obj.nil?
|
|
625
|
-
raise e
|
|
626
652
|
end
|
|
627
653
|
|
|
628
654
|
# Supported Method Parameters::
|
|
629
|
-
# serial_obj = Meshtastic::
|
|
655
|
+
# serial_obj = Meshtastic::Serial.disconnect(
|
|
630
656
|
# serial_obj: 'required - serial_obj returned from #connect method'
|
|
631
657
|
# )
|
|
632
658
|
public_class_method def self.disconnect(opts = {})
|
|
633
659
|
serial_obj = opts[:serial_obj]
|
|
634
|
-
return nil
|
|
660
|
+
return nil if serial_obj.nil? || serial_obj[:closing]
|
|
635
661
|
|
|
636
|
-
|
|
662
|
+
serial_obj[:closing] = true
|
|
663
|
+
serial_obj[:from_radio_queue]&.close
|
|
664
|
+
serial_obj[:config_queue]&.close
|
|
637
665
|
|
|
638
666
|
# Ask device to release the link (best-effort).
|
|
639
667
|
begin
|
|
@@ -677,7 +705,9 @@ module Meshtastic
|
|
|
677
705
|
# Display Usage for this Module
|
|
678
706
|
|
|
679
707
|
public_class_method def self.help
|
|
680
|
-
puts "
|
|
708
|
+
puts "Send and receive Meshtastic messages over a framed serial (USB/UART) connection.
|
|
709
|
+
|
|
710
|
+
USAGE:
|
|
681
711
|
serial_obj = #{self}.connect(
|
|
682
712
|
block_dev: 'optional - serial block device path (defaults to /dev/ttyUSB0)',
|
|
683
713
|
baud: 'optional - (defaults to 115200)',
|
|
@@ -688,6 +718,11 @@ module Meshtastic
|
|
|
688
718
|
want_config: 'optional - request full node DB after connect (default: true)'
|
|
689
719
|
)
|
|
690
720
|
|
|
721
|
+
#{self}.wait_for_config(
|
|
722
|
+
serial_obj: 'required - serial_obj connected with want_config: true',
|
|
723
|
+
timeout: 'optional - seconds to await configuration (default: 10; raises Timeout::Error)'
|
|
724
|
+
)
|
|
725
|
+
|
|
691
726
|
#{self}.wake_up_device(
|
|
692
727
|
serial_obj: 'required - serial_obj returned from #connect method'
|
|
693
728
|
)
|
|
@@ -703,16 +738,19 @@ module Meshtastic
|
|
|
703
738
|
)
|
|
704
739
|
|
|
705
740
|
from_radio = #{self}.recv_from_radio(
|
|
706
|
-
|
|
741
|
+
serial_obj: 'optional - serial_obj (default: most recently opened connection)',
|
|
742
|
+
timeout: 'optional - seconds (default: 5; 0 = poll; nil = block forever)'
|
|
707
743
|
)
|
|
708
744
|
|
|
709
|
-
msgs = #{self}.drain_from_radio(max: 256)
|
|
745
|
+
msgs = #{self}.drain_from_radio(serial_obj: serial_obj, max: 256)
|
|
710
746
|
|
|
711
747
|
stdout_data = #{self}.dump_stdout_data(
|
|
748
|
+
serial_obj: 'optional - serial_obj (default: most recently opened connection)',
|
|
712
749
|
type: 'required - :proto or :console'
|
|
713
750
|
)
|
|
714
751
|
|
|
715
752
|
#{self}.flush_data(
|
|
753
|
+
serial_obj: 'optional - serial_obj (default: most recently opened connection)',
|
|
716
754
|
type: 'required - :console or :proto'
|
|
717
755
|
)
|
|
718
756
|
|
|
@@ -736,7 +774,7 @@ module Meshtastic
|
|
|
736
774
|
|
|
737
775
|
#{self}.send_text(
|
|
738
776
|
serial_obj: 'required - serial_obj returned from #connect method',
|
|
739
|
-
from: 'optional - From ID (Default: local my_node_num or
|
|
777
|
+
from: 'optional - From ID (Default: local my_node_num or 0 for firmware-assigned)',
|
|
740
778
|
to: 'optional - Destination ID (Default: \"!ffffffff\")',
|
|
741
779
|
channel: 'optional - channel index (Default: 0)',
|
|
742
780
|
text: 'optional - Text Message (Default: SYN)',
|
|
@@ -22,7 +22,7 @@ module Meshtastic
|
|
|
22
22
|
no_nodes = false if opts[:no_nodes].nil?
|
|
23
23
|
no_nodes = true if opts[:no_nodes]
|
|
24
24
|
# Note: In Ruby, we don't need to explicitly define a type hint for self.
|
|
25
|
-
raise Exception("StreamInterface is now abstract (to update existing code
|
|
25
|
+
raise Exception("StreamInterface is now abstract (to update existing code use Meshtastic::Serial instead)") if !defined?(@stream) && !no_proto
|
|
26
26
|
|
|
27
27
|
@stream = nil
|
|
28
28
|
@rx_buf = []
|
data/lib/meshtastic/version.rb
CHANGED
data/lib/meshtastic.rb
CHANGED
|
@@ -16,6 +16,7 @@ module Meshtastic
|
|
|
16
16
|
require 'meshtastic/device_ui_pb'
|
|
17
17
|
require 'meshtastic/interdevice_pb'
|
|
18
18
|
require 'meshtastic/localonly_pb'
|
|
19
|
+
require 'meshtastic/lorawan_bridge_pb'
|
|
19
20
|
require 'meshtastic/mesh_pb'
|
|
20
21
|
require 'meshtastic/module_config_pb'
|
|
21
22
|
require 'meshtastic/mqtt_pb'
|
|
@@ -35,6 +36,7 @@ module Meshtastic
|
|
|
35
36
|
autoload :Admin, 'meshtastic/admin'
|
|
36
37
|
autoload :Apponly, 'meshtastic/apponly'
|
|
37
38
|
autoload :ATAK, 'meshtastic/atak'
|
|
39
|
+
autoload :Bluetooth, 'meshtastic/bluetooth'
|
|
38
40
|
autoload :Cannedmessages, 'meshtastic/cannedmessages'
|
|
39
41
|
autoload :Channel, 'meshtastic/channel'
|
|
40
42
|
autoload :Clientonly, 'meshtastic/clientonly'
|
|
@@ -49,7 +51,7 @@ module Meshtastic
|
|
|
49
51
|
autoload :Portnums, 'meshtastic/portnums'
|
|
50
52
|
autoload :RemoteHardware, 'meshtastic/remote_hardware'
|
|
51
53
|
autoload :RTTTL, 'meshtastic/rtttl'
|
|
52
|
-
autoload :
|
|
54
|
+
autoload :Serial, 'meshtastic/serial'
|
|
53
55
|
autoload :Storeforward, 'meshtastic/storeforward'
|
|
54
56
|
autoload :StreamInterface, 'meshtastic/stream_interface'
|
|
55
57
|
autoload :Telemetry, 'meshtastic/telemetry'
|