Repository navigation
v4.1.0
No breaking API changes: Rust and JS code written for 4.0.0 keeps compiling and the
invoke payloads are unchanged; the API additions and the behaviour changes are below.
Behaviour changes (AT parser)
- URCs are no longer de-duplicated: two identical URCs in one read are both reported.
- V.250 numeric result codes (
ATV0mode:0,3,4, …) are recognised as final results;
result codes match case-insensitively. - Verbose
+CME ERROR:/+CMS ERROR:texts are mapped to their numeric codes
("+CME ERROR: SIM busy"givescode: 14);codeisnullonly for text the parser
does not know. - Trailing lines after the final result code no longer hide it and are reported as URCs; a final
OKglued to a following+CUSD:is split; quoted multi-line+CUSDis one URC. - Two-line
+CMT/+CDS/+CBMURCs keep their PDU line;+CMGS:in the PDU phase
ofsend_sms_pduis treated as solicited; text-mode SMS bodies stay data. ATV0replies whose result code ends with CR only (V.250 §5.7.1) complete instead of
timing out; echo and result are no longer glued into one line.- Basic commands in front of an extended one (
ATE1+CSQ,AT&F+CSQ) no longer turn the
reply line into a URC; withATE0the lines of a known command (AT+CLAC) are body. CONNECTorOKfollowed by data in the same read (PPP, CMUX) completes the exchange.- A reply without a final result keeps its last line in
solicitedBody. - The non-standard
COMMAND NOT SUPPORTline is no longer a final result (only V.250,
27.007 and 27.005 codes are;SEND OK/SEND FAILstay for compatibility).
Features
- New
serialplugin-corecrate (crates/serialplugin-core): the plugin's whole serial core
(RX hub, watch with parsers, exchange / AT / SMS PDU, CMUX, TCP, BLE, Android USB)
without Tauri.SerialManagertakes closure sinks (EventSink,PortListSink)
instead of IPC channels, so headless Rust apps can drive many ports directly. The
plugin is now a thin layer over it:api::SerialPort<R>keeps its 4.0.0 methods
(delegating toSerialManager, which it also derefs to) and the moved modules are
re-exported at their old paths (state,events,error,at,hub, …). - New
serial-codeccrate (crates/serial-codec, MPL-2.0), usable without Tauri:
framing(Node serialport parser parity: delimiter, readline, byteLength,
interByteTimeout, packetLength, ready, regex, startEnd, SLIP, ccTalk),at(AT
response parsing),args(V.250 / 27.007 argument splitting),urc(typed 27.007 /
27.005 information lines and URCs, e.g.+CSQ,+CREG),sms(SMS PDU decode/encode,
GSM 7-bit with national shift tables, UCS-2, concatenated SMS reassembly),ussd
(+CUSDparsing, USSD 7-bit packing, CBS DCS) andcmux(27.010 basic-mode frames). watch({ parser })(RustSerialPort::watch_framed/SerialManager::watch_framed;
the parser is a top-levelparserargument of thewatchcommand): native framing, each complete
frame is onedataevent with no batching; time-based parsers fire without further
bytes; the buffered remainder is emitted on unwatch, close or disconnect. Works on
every transport and on CMUX channels, and survives auto-reconnect. See the
parsers guide.- JS: new exported types
BlePortInfo(scanBlePorts()results withrssi) and
ExchangeCompletionMode. - Documentation site: https://s00d.github.io/tauri-plugin-serialplugin/ (guides,
platform notes, and an API reference generated from the sources). - Android BLE:
ble://<MAC or name>ports andscanBlePorts()now work on Android
(featureble) through a KotlinBluetoothGattbridge: Nordic UART Service, MTU 247,
serialized chunked writes, notifications as read data, link loss asonDisconnect,
one retry on GATT 133/62/129. Runtime permission is requested on first use
(Nearby devices on Android 12+, location on 11 and older). GATT callbacks complete only
the step that waits for them (a peripheral-initiated MTU exchange or a late callback no
longer finishes the wrong step); a MAC not seen before is found by a short address scan
(random static addresses); names are cached and Android's 5-scans-per-30 s limit is a
clear error; Bluetooth switched off ends open ports withonDisconnect; Location
services off (Android 11 and older) is a clear error; pairing-protected devices are
paired and retried once; one 20 s budget covers the wholeopen(); partial writes
return the bytes sent; the RX queue is capped at 1 MiB and counted bybytesToRead().
The Bluetooth permissions are not in the library manifest (its merge would have
changed apps' own location /BLUETOOTH_SCANdeclarations): withble,build.rs
writes them into the app's generatedAndroidManifest.xml, leaving out any the app
declares itself. Apps withoutbleget no new permissions (see
BLE).
Bug Fixes
- watch/URC: URCs found while draining RX before an exchange were judged per read
chunk, so a+CMT:/+CDS:header could go out without its PDU line; they are now
emitted when the drain completes, over the whole drained buffer. A URC that arrives
during the pre-exchange drain is therefore delayed by up to the drain window
(drainIdleMsafter the last byte, at mostdrainMaxMs, default 200 ms), and still
reaches the watch when the drain ends with an error (read error, close, USB error). - watch:
unwatch/closeracing the RX thread could drop frames the parser had
already decoded and deliver the later remainder; events now keep their sink from the
moment they are decoded, and the remainder always comes after them (also on CMUX
channels, where it could arrive first). - core: the watch sink is never called under a manager, hub, drain or CMUX lock, so
a sink may call back intoSerialManager(unwatch, the parser remainder on
unwatch/close/re-watch and drain URCs used to run under them and could deadlock). The
sink runs on the RX thread, except the last events of a watch (remainder on
unwatch / close), which may run on the caller's thread; one call at a time, in order. - CMUX: the receive limit is the N1 from the
AT+CMUX=command (4th parameter),
at least 1509 bytes; frames up to N1 bytes are no longer dropped as corrupt when N1 is
above 1509 (the default command keeps N1 = 31). - CMUX: a physical disconnect now flushes channel watches too: their parser
remainder, thendisconnectwith the channel's path. - watch: time-based parsers (
interByteTimeout, ccTalk) and the batch timer kept
running only outside a pre-exchange drain; they now fire during it. - core:
watchids handed out bySerialManager::watch/watch_portsskip ids
already taken by plugin channels, and a duplicate id is rejected instead of silently
replacing the other watch.force_closejoins the RX thread outside the port map lock. - watch: unknown keys inside
watch({ parser })failwatch(a typo would fall back
to a default); the parsers are linear on input without delimiters (startEnd and regex
rescanned their whole buffer per read), andpacketLengthgainedmaxBufferSizeand
rejects options that could overflow. Exchanges judge each reply line once (a long
reply re-ran completion over the whole buffer per read). - BLE: chunks are capped at 512 bytes (Android 14+ and some desktops negotiate
MTU 517+);ble://<name>lookups scan without the service filter (boards that do not
advertise the NUS UUID); every 8th chunk is written with response when the device
allows it. Desktop / iOS:open()times out after 20 s instead of waiting forever on
macOS, one Bluetooth adapter is shared and waited for until powered on, notifications
sent right after subscribing are kept, Linux ports areble://<MAC>and MACs match in
any case, the RX queue is capped at 1 MiB like on Android. Android: bonded devices get
1.6 s before service discovery, a stale GATT cache is refreshed once, Service Changed
ends the port, high connection priority, indicate-only TX characteristics work.
New crates: serial-codec 0.1.0 (MPL-2.0), serialplugin-core 0.1.0. Docs: https://s00d.github.io/tauri-plugin-serialplugin/