From 80354e321a20887d3aaa20cec784a68a13ab5cc3 Mon Sep 17 00:00:00 2001 From: "pls.153" Date: Mon, 15 Apr 2024 16:41:24 +0200 Subject: [PATCH] example 'meshtastic': review USB mode, add setting in menu --- examples/meshtastic/cpp/ble/ble.cpp | 6 +- examples/meshtastic/cpp/ble/ble.h | 3 +- examples/meshtastic/cpp/qt.cpp | 14 +++- examples/meshtastic/cpp/qt.h | 1 + .../meshtastic/cpp/usb/usb_meshtastic.cpp | 78 +++++++++---------- examples/meshtastic/cpp/usb/usb_meshtastic.h | 11 +-- examples/meshtastic/lisp/lora.lisp | 28 ++++--- examples/meshtastic/lisp/main.lisp | 4 +- examples/meshtastic/lisp/package.lisp | 2 +- examples/meshtastic/lisp/qt.lisp | 1 + examples/meshtastic/lisp/radios.lisp | 20 ++++- examples/meshtastic/qml/main.qml | 23 ++++++ 12 files changed, 119 insertions(+), 72 deletions(-) diff --git a/examples/meshtastic/cpp/ble/ble.cpp b/examples/meshtastic/cpp/ble/ble.cpp index b5dd0ca..a429ca4 100644 --- a/examples/meshtastic/cpp/ble/ble.cpp +++ b/examples/meshtastic/cpp/ble/ble.cpp @@ -35,6 +35,10 @@ void BLE::startDeviceDiscovery(const QString& name) { discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); } +void BLE::stopDeviceDiscovery() { + discoveryAgent->stop(); +} + void BLE::addDevice(const QBluetoothDeviceInfo& device) { if (deviceFilter(device)) { QString name(device.name()); @@ -176,7 +180,7 @@ void BLE::errorReceived(QLowEnergyController::Error) { Q_EMIT bleError(); } -void BLE::disconnectFromDevice() { +void BLE::disconnect() { if (controller->state() != QLowEnergyController::UnconnectedState) { controller->disconnectFromDevice(); } else { diff --git a/examples/meshtastic/cpp/ble/ble.h b/examples/meshtastic/cpp/ble/ble.h index adf7253..98ed742 100644 --- a/examples/meshtastic/cpp/ble/ble.h +++ b/examples/meshtastic/cpp/ble/ble.h @@ -40,12 +40,13 @@ Q_SIGNALS: public Q_SLOTS: void startDeviceDiscovery(const QString& = QString()); + void stopDeviceDiscovery(); + void disconnect(); /*** *********************************************************/ void scanServices(); void connectToService(const QString&); - void disconnectFromDevice(); private Q_SLOTS: // QBluetoothDeviceDiscoveryAgent related diff --git a/examples/meshtastic/cpp/qt.cpp b/examples/meshtastic/cpp/qt.cpp index 8474354..314c0da 100644 --- a/examples/meshtastic/cpp/qt.cpp +++ b/examples/meshtastic/cpp/qt.cpp @@ -146,10 +146,22 @@ QT::QT() : QObject() { // BLE QVariant QT::startDeviceDiscovery(const QVariant& vName) { - ble->startDeviceDiscovery(vName.toString()); + QByteArray connection(ecl_fun("radios:connection").toString().toLatin1()); + if (connection == "BLE") { + usb->disconnect(); + ble->startDeviceDiscovery(vName.toString()); + } else if (connection == "USB") { + ble->disconnect(); + usb->connectToRadio(); + } return vName; } +QVariant QT::stopDeviceDiscovery() { + ble->stopDeviceDiscovery(); + return QVariant(); +} + QVariant QT::setDeviceFilter(const QVariant& vName) { ble->setDeviceFilter(vName.toString()); return vName; diff --git a/examples/meshtastic/cpp/qt.h b/examples/meshtastic/cpp/qt.h index a3dc168..face7be 100644 --- a/examples/meshtastic/cpp/qt.h +++ b/examples/meshtastic/cpp/qt.h @@ -26,6 +26,7 @@ class QT : public QObject { public: // BLE Q_INVOKABLE QVariant startDeviceDiscovery(const QVariant&); + Q_INVOKABLE QVariant stopDeviceDiscovery(); Q_INVOKABLE QVariant setDeviceFilter(const QVariant&); Q_INVOKABLE QVariant readBle(); Q_INVOKABLE QVariant writeBle(const QVariant&); diff --git a/examples/meshtastic/cpp/usb/usb_meshtastic.cpp b/examples/meshtastic/cpp/usb/usb_meshtastic.cpp index ea45634..f5fafba 100644 --- a/examples/meshtastic/cpp/usb/usb_meshtastic.cpp +++ b/examples/meshtastic/cpp/usb/usb_meshtastic.cpp @@ -1,27 +1,10 @@ #include "usb_meshtastic.h" #include #include +#include #include USB_ME::USB_ME() { - const auto infos = QSerialPortInfo::availablePorts(); - const QStringList boards = { "RAK", "UART" }; // TODO - for (const QSerialPortInfo& info : infos) { - QString name(info.description() + " " + info.manufacturer()); - QString port(info.portName()); - qDebug() << "USB:" << port << name; - if (port.startsWith("tty") && - (port.contains("ACM", Qt::CaseInsensitive) || // Linux - port.contains("USB", Qt::CaseInsensitive))) { // macOS - for (auto board : boards) { - if (name.contains(board, Qt::CaseInsensitive)) { - setPortName(info.portName()); - goto done; - } - } - } - } -done: connect(this, &QSerialPort::readyRead, this, &USB_ME::read2); connect(this, &QSerialPort::errorOccurred, [](QSerialPort::SerialPortError error) { @@ -29,30 +12,50 @@ done: qDebug() << "USB error:" << error; } }); +} + +void USB_ME::connectToRadio() { + if (isOpen()) { + Q_EMIT setReady(portName()); + qDebug() << "USB already open;" << portName(); + return; + } + + const auto infos = QSerialPortInfo::availablePorts(); + const QStringList supported = { "RAK" }; // TODO: currently RAK only + for (const QSerialPortInfo& info : infos) { + QString name(info.description() + " " + info.manufacturer()); + QString port(info.portName()); + if (port.startsWith("tty") && + (port.contains("ACM", Qt::CaseInsensitive) || // Linux + port.contains("USB", Qt::CaseInsensitive))) { // macOS + for (auto s : supported) { + if (name.contains(s, Qt::CaseInsensitive)) { + setPortName(info.portName()); + qDebug() << "USB:" << port << name; + goto done; + } + } + } + } + done: if (!open(QIODevice::ReadWrite)) { qDebug() << "USB: unable to open port" << portName(); } else { + ready = true; + Q_EMIT setReady(portName()); qDebug() << "USB open"; - state = Open; - QTimer::singleShot(0, this, &USB_ME::wantConfigId); } } -void USB_ME::wantConfigId() { - if (state != Closed) { - // 4 bytes header + protobuf 'wantConfigId' - const char want_config_id[] = { '\x94', '\xc3', '\x00', '\x02', - '\x18', '\x01' }; - write(want_config_id, sizeof(want_config_id)); - qDebug() << "USB wantConfigId()"; - } else { - qDebug() << "USB not ready: wantConfigId()"; - } +void USB_ME::disconnect() { + close(); + qDebug() << "USB closed"; } void USB_ME::write2(const QByteArray& data) { - if (state == Ready) { + if (ready) { write(data.constData(), data.size()); } else { qDebug() << "USB not ready: write()"; @@ -60,17 +63,11 @@ void USB_ME::write2(const QByteArray& data) { } void USB_ME::read2() { - static QTimer* timer = nullptr; const char header[] = { '\x94', '\xc3' }; - const int MAX = 0xffff; - QByteArray data(read(MAX)); - if (data.startsWith(header)) { // skip evtl. debug log - if (state == Open) { - Q_EMIT setReady(portName()); - qDebug() << "USB ready"; - } - state = Ready; + QByteArray data(readAll()); + qGuiApp->processEvents(); // macOS needs this for some reason + if (data.startsWith(header)) { // skip evtl. debug log // split on every new header const int LEN = 4; int end = 0; @@ -81,6 +78,7 @@ void USB_ME::read2() { } Q_EMIT receivedFromRadio(data.mid(start)); + static QTimer* timer = nullptr; if (!timer) { timer = new QTimer; timer->setSingleShot(true); diff --git a/examples/meshtastic/cpp/usb/usb_meshtastic.h b/examples/meshtastic/cpp/usb/usb_meshtastic.h index ee0bf34..ed2a532 100644 --- a/examples/meshtastic/cpp/usb/usb_meshtastic.h +++ b/examples/meshtastic/cpp/usb/usb_meshtastic.h @@ -8,16 +8,11 @@ class USB_ME : public QSerialPort { public: USB_ME(); - enum State { - Closed, - Open, - Ready - }; - - State state = Closed; + bool ready = false; public Q_SLOTS: - void wantConfigId(); + void connectToRadio(); + void disconnect(); void write2(const QByteArray&); void read2(); diff --git a/examples/meshtastic/lisp/lora.lisp b/examples/meshtastic/lisp/lora.lisp index e0626e0..13ee621 100644 --- a/examples/meshtastic/lisp/lora.lisp +++ b/examples/meshtastic/lisp/lora.lisp @@ -14,9 +14,9 @@ #-mobile t) (defun ini () - (setf *receiver* (app:setting :latest-receiver) - *channel-name* (or (app:setting :channel-name) - "LongFast")) + (setf *channel-name* (or (app:setting :channel-name) + "LongFast") + *receiver* (app:setting :latest-receiver)) (q> |text| ui:*channel-name* *channel-name*)) ;;; header @@ -31,7 +31,6 @@ (defconstant +broadcast-id+ #xffffffff) (defparameter *broadcast-name* "ffff") -(defvar *mode* :ble) ; :ble :usb (defvar *config-id* 0) (defvar *config-complete* nil) (defvar *notify-id* nil) @@ -49,14 +48,15 @@ (defun start-device-discovery (&optional (name (or (app:setting :device) ""))) (when *allow-discovery* - (setf *schedule-clear* t) - (setf *ble-names* nil) + (setf *ready* nil + *ble-names* nil + *schedule-clear* t) (unless radios:*found* (radios:clear)) (qrun* (qt:start-device-discovery qt:*cpp* name)) (q> |playing| ui:*busy* t))) -(defun get-node-config (&optional (want t)) +(defun get-node-config () ;; see also Timer in 'qml/ext/group/Group.qml' (when *ready* (setf *schedule-clear* t) @@ -64,9 +64,8 @@ *channels* nil *node-infos* nil) (incf *config-id*) - (when want - (send-to-radio - (me:make-to-radio :want-config-id *config-id*))) + (send-to-radio + (me:make-to-radio :want-config-id *config-id*)) (q> |playing| ui:*busy* t))) (defun set-ready-ble (&optional ready name ble-names) ; see Qt @@ -78,10 +77,9 @@ (values)) (defun set-ready-usb (port) ; see Qt - (setf *ready* t - *mode* :usb) + (setf *ready* t) (app:toast (x:cc "USB: " port) 2) - (get-node-config nil) + (get-node-config) (values)) (defun add-line-breaks (text) @@ -150,7 +148,7 @@ (pr:print-json to-radio)) (let* ((bytes (pr:serialize-to-bytes to-radio)) (header (header (length bytes)))) - (case *mode* + (case radios:*connection* (:ble (qrun* (qt:write-ble qt:*cpp* header) (qt:write-ble qt:*cpp* bytes))) @@ -297,7 +295,7 @@ :custom-name (or (app:setting name :custom-name) "") :node-num (me:num info) :current (equal name (app:setting :latest-receiver))))) - (when (or (and (eql *mode* :usb) + (when (or (and (eql radios:*connection* :usb) current) (find name *ble-names* :test 'string=)) (setf radios:*found* t) diff --git a/examples/meshtastic/lisp/main.lisp b/examples/meshtastic/lisp/main.lisp index c46495a..f9d6d42 100644 --- a/examples/meshtastic/lisp/main.lisp +++ b/examples/meshtastic/lisp/main.lisp @@ -28,9 +28,7 @@ :bluetooth-connect)) #+(or android ios) (qlater (lambda () (qt:keep-screen-on qt:*cpp*))) - (qsingle-shot 1000 (lambda () - (unless (eql lora:*mode* :usb) - (lora:start-device-discovery))))) + (lora:start-device-discovery)) (defun in-data-path (&optional (file "") (prefix "data/")) #+mobile diff --git a/examples/meshtastic/lisp/package.lisp b/examples/meshtastic/lisp/package.lisp index edd9cbb..6979035 100644 --- a/examples/meshtastic/lisp/package.lisp +++ b/examples/meshtastic/lisp/package.lisp @@ -38,7 +38,6 @@ #:*channels* #:*config-complete* #:*config-lora* - #:*mode* #:*my-node-info* #:*node-infos* #:*print-json* @@ -121,6 +120,7 @@ (:use :cl :qml) (:export #:*found* + #:*connection* #:add-radio #:change-radio #:choose-region diff --git a/examples/meshtastic/lisp/qt.lisp b/examples/meshtastic/lisp/qt.lisp index 67e396c..aba5167 100644 --- a/examples/meshtastic/lisp/qt.lisp +++ b/examples/meshtastic/lisp/qt.lisp @@ -11,6 +11,7 @@ #:last-position #:local-ip #:start-device-discovery + #:stop-device-discovery #:read-ble #:set-background-mode ; for testing #:set-device-filter diff --git a/examples/meshtastic/lisp/radios.lisp b/examples/meshtastic/lisp/radios.lisp index b9e6e87..1acab04 100644 --- a/examples/meshtastic/lisp/radios.lisp +++ b/examples/meshtastic/lisp/radios.lisp @@ -1,14 +1,29 @@ (in-package :radios) -(defvar *found* nil) +(defvar *connection* nil) +(defvar *found* nil) (defun ini () + (setf *connection* (or (app:setting :connection) + :ble)) + (q> |checked| (symbol-name *connection*) t) (q> |model| ui:*region* (cons "-" (mapcar 'symbol-name (rest (lora:keywords :region-code))))) (set-region) (x:when-it (app:setting :device-filter) (qt:set-device-filter qt:*cpp* x:it))) +(defun connection () ; see Qt + (symbol-name *connection*)) + +(defun connection-changed (name) + (when (eql *connection* :ble) + (qt:stop-device-discovery qt:*cpp*)) + (let ((con (app:kw name))) + (setf *connection* con) + (app:change-setting :connection con)) + (lora:start-device-discovery)) + (defun saved-region () (let ((region (app:setting :region))) (unless (find region '(nil :unset)) @@ -50,5 +65,6 @@ (values)) (defun reset () - (lora:start-device-discovery)) + (when (eql *connection* :ble) + (lora:start-device-discovery))) diff --git a/examples/meshtastic/qml/main.qml b/examples/meshtastic/qml/main.qml index ed772c1..3d61d5f 100644 --- a/examples/meshtastic/qml/main.qml +++ b/examples/meshtastic/qml/main.qml @@ -61,6 +61,29 @@ Item { MenuSeparator {} + Com.Menu { + id: connection + title: qsTr("Connection") + enabled: (view.pageIndex === 2) + + function changed(name) { Lisp.call("radios:connection-changed", name) } + + Com.MenuItem { + objectName: "BLE" + text: "BLE" + autoExclusive: true + checked: true + onTriggered: connection.changed(objectName) + } + Com.MenuItem { + objectName: "USB" + text: "USB" + autoExclusive: true + checkable: true + onTriggered: connection.changed(objectName) + } + } + Com.MenuItem { text: qsTr("Reset node DB") onTriggered: Lisp.call("lora:reset-node-db")