diff --git a/owlet/integrationpluginowlet.cpp b/owlet/integrationpluginowlet.cpp index db30ac4c..a8c54695 100644 --- a/owlet/integrationpluginowlet.cpp +++ b/owlet/integrationpluginowlet.cpp @@ -37,70 +37,104 @@ #include "network/zeroconf/zeroconfservicebrowser.h" #include "network/zeroconf/zeroconfserviceentry.h" +#include "owlettcptransport.h" +#include "owletserialtransport.h" + #include #include -static QHash idParamTypeMap = { - { digitalOutputThingClassId, digitalOutputThingOwletIdParamTypeId }, - { digitalInputThingClassId, digitalInputThingOwletIdParamTypeId }, - { ws2812ThingClassId, ws2812ThingOwletIdParamTypeId } -}; - IntegrationPluginOwlet::IntegrationPluginOwlet() { } void IntegrationPluginOwlet::init() { + m_owletIdParamTypeMap.insert(digitalOutputThingClassId, digitalOutputThingOwletIdParamTypeId); + m_owletIdParamTypeMap.insert(digitalInputThingClassId, digitalInputThingOwletIdParamTypeId); + m_owletIdParamTypeMap.insert(ws2812ThingClassId, ws2812ThingOwletIdParamTypeId); + + m_owletSerialPortParamTypeMap.insert(arduinoMiniProThingClassId, arduinoMiniProThingSerialPortParamTypeId); + m_zeroConfBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_nymea-owlet._tcp"); } void IntegrationPluginOwlet::discoverThings(ThingDiscoveryInfo *info) { - foreach (const ZeroConfServiceEntry &entry, m_zeroConfBrowser->serviceEntries()) { - qCDebug(dcOwlet()) << "Found owlet:" << entry; - ThingDescriptor descriptor(info->thingClassId(), entry.name(), entry.txt("platform")); - descriptor.setParams(ParamList() << Param(idParamTypeMap.value(info->thingClassId()), entry.txt("id"))); - foreach (Thing *existingThing, myThings().filterByParam(idParamTypeMap.value(info->thingClassId()), entry.txt("id"))) { - descriptor.setThingId(existingThing->id()); - break; + if (info->thingClassId() == arduinoMiniProThingClassId) { + // Discover serial ports for arduino bords + foreach(QSerialPortInfo port, QSerialPortInfo::availablePorts()) { + qCDebug(dcOwlet()) << "Found serial port" << port.systemLocation(); + QString description = port.systemLocation() + " " + port.manufacturer() + " " + port.description(); + ThingDescriptor thingDescriptor(info->thingClassId(), "Owlet Arduino Pro Mini", description); + ParamList parameters; + + foreach (Thing *existingThing, myThings()) { + if (existingThing->paramValue(m_owletSerialPortParamTypeMap.value(info->thingClassId())).toString() == port.systemLocation()) { + thingDescriptor.setThingId(existingThing->id()); + break; + } + } + + parameters.append(Param(m_owletSerialPortParamTypeMap.value(info->thingClassId()), port.systemLocation())); + thingDescriptor.setParams(parameters); + info->addThingDescriptor(thingDescriptor); } - info->addThingDescriptor(descriptor); + info->finish(Thing::ThingErrorNoError); + } else { + foreach (const ZeroConfServiceEntry &entry, m_zeroConfBrowser->serviceEntries()) { + qCDebug(dcOwlet()) << "Found owlet:" << entry; + ThingDescriptor descriptor(info->thingClassId(), entry.name(), entry.txt("platform")); + descriptor.setParams(ParamList() << Param(m_owletIdParamTypeMap.value(info->thingClassId()), entry.txt("id"))); + foreach (Thing *existingThing, myThings().filterByParam(m_owletIdParamTypeMap.value(info->thingClassId()), entry.txt("id"))) { + descriptor.setThingId(existingThing->id()); + break; + } + info->addThingDescriptor(descriptor); + } + info->finish(Thing::ThingErrorNoError); } - info->finish(Thing::ThingErrorNoError); } void IntegrationPluginOwlet::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); - + OwletTransport *transport = nullptr; QHostAddress ip; int port = 5555; - foreach (const ZeroConfServiceEntry &entry, m_zeroConfBrowser->serviceEntries()) { - if (entry.txt("id") == info->thing()->paramValue(idParamTypeMap.value(info->thing()->thingClassId()))) { - ip = entry.hostAddress(); - port = entry.port(); - break; + + if (thing->thingClassId() != arduinoMiniProThingClassId) { + foreach (const ZeroConfServiceEntry &entry, m_zeroConfBrowser->serviceEntries()) { + if (entry.txt("id") == info->thing()->paramValue(m_owletIdParamTypeMap.value(info->thing()->thingClassId()))) { + ip = entry.hostAddress(); + port = entry.port(); + break; + } } - } - // Try cached ip - if (ip.isNull()) { - pluginStorage()->beginGroup(thing->id().toString()); - ip = QHostAddress(pluginStorage()->value("cachedIP").toString()); - pluginStorage()->endGroup(); + // Try cached ip + if (ip.isNull()) { + pluginStorage()->beginGroup(thing->id().toString()); + ip = QHostAddress(pluginStorage()->value("cachedIP").toString()); + pluginStorage()->endGroup(); + } + + if (ip.isNull()) { + qCWarning(dcOwlet()) << "Can't find owlet in the local network."; + info->finish(Thing::ThingErrorHardwareNotAvailable); + return; + } + + transport = new OwletTcpTransport(ip, port, this); + + } else if (thing->thingClassId() == arduinoMiniProThingClassId) { + QString serialPort = thing->paramValue(arduinoMiniProThingSerialPortParamTypeId).toString(); + qCDebug(dcOwlet()) << "Setup arduino mini pro owlet on" << serialPort; + transport = new OwletSerialTransport(serialPort, 115200, this); } - if (ip.isNull()) { - qCWarning(dcOwlet()) << "Can't find owlet in the local network."; - info->finish(Thing::ThingErrorHardwareNotAvailable); - return; - } - - OwletClient *client = new OwletClient(this); - + OwletClient *client = new OwletClient(transport, this); connect(client, &OwletClient::connected, info, [=](){ - qCDebug(dcOwlet()) << "Connected to owleet"; + qCDebug(dcOwlet()) << "Connected to owlet"; m_clients.insert(thing, client); if (thing->thingClassId() == digitalOutputThingClassId) { @@ -127,15 +161,31 @@ void IntegrationPluginOwlet::setupThing(ThingSetupInfo *info) info->finish(Thing::ThingErrorNoError); }); + connect(client, &OwletClient::error, info, [=](){ info->finish(Thing::ThingErrorHardwareFailure); }); + connect(client, &OwletClient::connected, thing, [=](){ thing->setStateValue("connected", true); - pluginStorage()->beginGroup(thing->id().toString()); - pluginStorage()->setValue("cachedIP", ip.toString()); - pluginStorage()->endGroup(); + + // FIXME: find a better way + if (thing->thingClassId() != arduinoMiniProThingClassId) { + pluginStorage()->beginGroup(thing->id().toString()); + pluginStorage()->setValue("cachedIP", ip.toString()); + pluginStorage()->endGroup(); + } + + qCDebug(dcOwlet()) << "Sending get platform information request..."; + int id = client->sendCommand("Platform.GetInformation"); + connect(client, &OwletClient::replyReceived, thing, [=](int commandId, const QVariantMap ¶ms){ + if (id != commandId) + return; + + qCDebug(dcOwlet()) << "Reply from owlet platform information:" << params; + }); }); + connect(client, &OwletClient::disconnected, thing, [=](){ thing->setStateValue("connected", false); }); @@ -170,7 +220,7 @@ void IntegrationPluginOwlet::setupThing(ThingSetupInfo *info) } }); - client->connectToHost(ip, port); + client->transport()->connectTransport(); } diff --git a/owlet/integrationpluginowlet.h b/owlet/integrationpluginowlet.h index 9d6337ab..902323bd 100644 --- a/owlet/integrationpluginowlet.h +++ b/owlet/integrationpluginowlet.h @@ -56,8 +56,9 @@ public: private: ZeroConfServiceBrowser *m_zeroConfBrowser = nullptr; - QHash m_clients; - + QHash m_clients; + QHash m_owletIdParamTypeMap; + QHash m_owletSerialPortParamTypeMap; }; #endif // INTEGRATIONPLUGINOWLET_H diff --git a/owlet/integrationpluginowlet.json b/owlet/integrationpluginowlet.json index 7fef3d74..95b73968 100644 --- a/owlet/integrationpluginowlet.json +++ b/owlet/integrationpluginowlet.json @@ -219,6 +219,286 @@ "writable": true } ] + }, + { + "id": "22f216f4-9715-48f9-ab76-f7d7436d49c9", + "name": "arduinoMiniPro", + "displayName": "Arduino Pro Mini Owlet", + "createMethods": ["user", "discovery"], + "interfaces": ["gateway", "connectable"], + "paramTypes": [ + { + "id": "589715a6-aaf7-4ffb-9c34-015ac22d527e", + "name": "serialPort", + "displayName": "Serial port", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "/dev/ttyS0" + }, + { + "id": "d9643551-883a-4e33-89a8-6e2e1211eb22", + "name": "resetGpio", + "displayName": "Reset GPIO", + "type": "uint", + "defaultValue": 18 + } + ], + "settingsTypes": [ + { + "id": "82a8d0f5-3b42-4138-ae65-8554e19b73e5", + "name": "pin2", + "displayName": "Pin 2", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "Interrupt" + ], + "defaultValue": "None" + }, + { + "id": "3953ad67-69a6-4b5f-8a91-f913d75d7d2f", + "name": "pin3", + "displayName": "Pin 3", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "PWM", + "Interrupt" + ], + "defaultValue": "None" + }, + { + "id": "cee570df-1669-49df-9eb6-6c28c9cb7aec", + "name": "pin4", + "displayName": "Pin 4", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output" + ], + "defaultValue": "None" + }, + { + "id": "ec366d87-cfdb-4c7e-a23e-20cb8e99b1f5", + "name": "pin5", + "displayName": "Pin 5", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "PWM" + ], + "defaultValue": "None" + }, + { + "id": "40354097-c5e5-4d13-b94c-72cb7a97cf71", + "name": "pin6", + "displayName": "Pin 6", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "PWM" + ], + "defaultValue": "None" + }, + { + "id": "22e5320f-2f34-4fd5-8699-1e4ddf96a8c8", + "name": "pin7", + "displayName": "Pin 7", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output" + ], + "defaultValue": "None" + }, + { + "id": "9b7b159c-9da1-4811-80e1-835d7743bebc", + "name": "pin8", + "displayName": "Pin 8", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output" + ], + "defaultValue": "None" + }, + { + "id": "0d4aa38d-7607-4361-b8c7-cc5c28e17bb8", + "name": "pin9", + "displayName": "Pin 9", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "PWM" + ], + "defaultValue": "None" + }, + { + "id": "d18e6ac5-3bf7-47ac-8b5b-70a9ef8394d5", + "name": "pin10", + "displayName": "Pin 10", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "PWM" + ], + "defaultValue": "None" + }, + { + "id": "f0afb81c-dcd4-4d50-84a7-0ed6739c4011", + "name": "pin11", + "displayName": "Pin 11", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "PWM" + ], + "defaultValue": "None" + }, + { + "id": "c62ab191-40ab-4d20-8130-a6b37801b984", + "name": "pin12", + "displayName": "Pin 12", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output" + ], + "defaultValue": "None" + }, + { + "id": "4522ce36-ba6f-46b0-a54b-410a93065b34", + "name": "pin13", + "displayName": "Pin 13 (LED)", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output" + ], + "defaultValue": "None" + }, + { + "id": "d6cf8433-acef-4312-836b-b34be6592e22", + "name": "pinA1", + "displayName": "Pin A1", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "Analog Input" + ], + "defaultValue": "None" + }, + { + "id": "e22fd058-8eab-4450-a2ef-2f85c347602c", + "name": "pinA2", + "displayName": "Pin A2", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "Analog Input" + ], + "defaultValue": "None" + }, + { + "id": "1702602e-6c05-4dea-b0ff-89af8ec63a88", + "name": "pinA3", + "displayName": "Pin A3", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "Analog Input" + ], + "defaultValue": "None" + }, + { + "id": "02b5022f-8892-4e9d-b11e-6fd8a9d4616f", + "name": "pinA4", + "displayName": "Pin A4", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "Analog Input" + ], + "defaultValue": "None" + }, + { + "id": "704e2a48-b1f4-400e-a378-d7e546f2083d", + "name": "pinA5", + "displayName": "Pin A5", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "Analog Input" + ], + "defaultValue": "None" + }, + { + "id": "69c85170-9447-4a01-acd2-926fbe03489f", + "name": "pinA6", + "displayName": "Pin A6", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "Analog Input" + ], + "defaultValue": "None" + }, + { + "id": "3ad3b667-b51f-4deb-b5d3-8cad1e0fdbba", + "name": "pinA7", + "displayName": "Pin A7", + "type": "QString", + "allowedValues": [ + "None", + "Input", + "Output", + "Analog Input" + ], + "defaultValue": "None" + } + ], + "stateTypes": [ + { + "id": "9a7d9a6a-f131-4f18-bfc0-956c2dba9329", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + } + ] } ] } diff --git a/owlet/owlet.pro b/owlet/owlet.pro index 572785b2..b940baf5 100644 --- a/owlet/owlet.pro +++ b/owlet/owlet.pro @@ -1,13 +1,19 @@ include(../plugins.pri) -QT += network +QT += network serialport SOURCES += \ integrationpluginowlet.cpp \ - owletclient.cpp + owletclient.cpp \ + owletserialtransport.cpp \ + owlettcptransport.cpp \ + owlettransport.cpp HEADERS += \ integrationpluginowlet.h \ - owletclient.h + owletclient.h \ + owletserialtransport.h \ + owlettcptransport.h \ + owlettransport.h diff --git a/owlet/owletclient.cpp b/owlet/owletclient.cpp index c9ed5e7c..cd1c5f03 100644 --- a/owlet/owletclient.cpp +++ b/owlet/owletclient.cpp @@ -1,49 +1,33 @@ #include "owletclient.h" - +#include "owlettransport.h" #include "extern-plugininfo.h" #include #include -OwletClient::OwletClient(QObject *parent) : QObject(parent) +OwletClient::OwletClient(OwletTransport *transport, QObject *parent) : + QObject(parent), + m_transport(transport) { - + connect(m_transport, &OwletTransport::dataReceived, this, &OwletClient::dataReceived); + connect(m_transport, &OwletTransport::error, this, &OwletClient::error); + connect(m_transport, &OwletTransport::connectedChanged, this, [=](bool isConnected){ + if (isConnected) { + emit connected(); + } else { + emit disconnected(); + } + }); } -void OwletClient::connectToHost(const QHostAddress &address, int port) +OwletTransport *OwletClient::transport() const { - if (m_socket) { - m_socket->abort(); - m_socket->deleteLater(); - } - - m_socket = new QTcpSocket(this); - connect(m_socket, &QTcpSocket::connected, this, [this](){ - emit connected(); - }); - connect(m_socket, &QTcpSocket::disconnected, this, [this, address, port](){ - qCDebug(dcOwlet()) << "Disconnected from owleet"; - emit disconnected(); - QTimer::singleShot(1000, this, [=]{ - connectToHost(address, port); - }); - - }); - typedef void (QTcpSocket:: *errorSignal)(QAbstractSocket::SocketError); - connect(m_socket, static_cast(&QTcpSocket::error), this, [this](){ - qCDebug(dcOwlet()) << "Error in owlet communication"; - emit error(); - }); - - connect(m_socket, &QTcpSocket::readyRead, this, [this](){ - dataReceived(m_socket->readAll()); - }); - m_socket->connectToHost(address, port); + return m_transport; } int OwletClient::sendCommand(const QString &method, const QVariantMap ¶ms) { - if (!m_socket) { + if (!m_transport->connected()) { qCWarning(dcOwlet()) << "Not connected to owlet. Not sending command."; return -1; } @@ -54,7 +38,7 @@ int OwletClient::sendCommand(const QString &method, const QVariantMap ¶ms) packet.insert("id", id); packet.insert("method", method); packet.insert("params", params); - m_socket->write(QJsonDocument::fromVariant(packet).toJson(QJsonDocument::Compact)); + m_transport->sendData(QJsonDocument::fromVariant(packet).toJson(QJsonDocument::Compact)); return id; } diff --git a/owlet/owletclient.h b/owlet/owletclient.h index 289374de..4f9b6e18 100644 --- a/owlet/owletclient.h +++ b/owlet/owletclient.h @@ -5,15 +5,17 @@ #include #include +class OwletTransport; + class OwletClient : public QObject { Q_OBJECT public: - explicit OwletClient(QObject *parent = nullptr); + explicit OwletClient(OwletTransport *transport, QObject *parent = nullptr); - void connectToHost(const QHostAddress &address, int port); + OwletTransport *transport() const; - int sendCommand(const QString &method, const QVariantMap ¶ms); + int sendCommand(const QString &method, const QVariantMap ¶ms = QVariantMap()); signals: void connected(); @@ -27,7 +29,7 @@ private slots: void dataReceived(const QByteArray &data); private: - QTcpSocket *m_socket = nullptr; + OwletTransport *m_transport = nullptr; int m_commandId = 0; QByteArray m_receiveBuffer; diff --git a/owlet/owletserialtransport.cpp b/owlet/owletserialtransport.cpp new file mode 100644 index 00000000..510211c6 --- /dev/null +++ b/owlet/owletserialtransport.cpp @@ -0,0 +1,168 @@ +#include "owletserialtransport.h" +#include "owlettransport.h" +#include "extern-plugininfo.h" + +#include + +OwletSerialTransport::OwletSerialTransport(const QString &serialPortName, uint baudrate, QObject *parent) : + OwletTransport(parent), + m_serialPortName(serialPortName), + m_baudrate(baudrate) +{ + qRegisterMetaType(); + + m_serialPort = new QSerialPort(this); + m_serialPort->setPortName(serialPortName); + m_serialPort->setBaudRate(115200); + m_serialPort->setDataBits(QSerialPort::DataBits::Data8); + m_serialPort->setParity(QSerialPort::Parity::NoParity); + m_serialPort->setStopBits(QSerialPort::StopBits::OneStop); + m_serialPort->setFlowControl(QSerialPort::FlowControl::NoFlowControl); + + connect(m_serialPort, &QSerialPort::readyRead, this, &OwletSerialTransport::onReadyRead); + connect(m_serialPort, &QSerialPort::errorOccurred, this, [=](QSerialPort::SerialPortError serialPortError){ + if (serialPortError != QSerialPort::NoError) { + qCWarning(dcOwlet()) << "Serial port error occured" << serialPortError << m_serialPort->errorString(); + emit error(); + m_reconnectTimer->start(); + if (m_serialPort->isOpen()) { + m_serialPort->close(); + } + emit connectedChanged(false); + } + }); + + m_reconnectTimer = new QTimer(this); + m_reconnectTimer->setInterval(5000); + m_reconnectTimer->setSingleShot(false); + connect(m_reconnectTimer, &QTimer::timeout, this, [=](){ + if (m_serialPort->isOpen()) { + m_reconnectTimer->stop(); + return; + } else { + connectTransport(); + } + }); +} + +bool OwletSerialTransport::connected() const +{ + return m_serialPort->isOpen(); +} + +void OwletSerialTransport::sendData(const QByteArray &data) +{ + // Stream bytes using SLIP + QByteArray message; + QDataStream stream(&message, QIODevice::WriteOnly); + stream << static_cast(SlipProtocolEnd); + + for (int i = 0; i < data.length(); i++) { + quint8 dataByte = data.at(i); + switch (dataByte) { + case SlipProtocolEnd: + stream << static_cast(SlipProtocolEsc); + stream << static_cast(SlipProtocolTransposedEnd); + break; + case SlipProtocolEsc: + stream << static_cast(SlipProtocolEsc); + stream << static_cast(SlipProtocolTransposedEsc); + break; + default: + stream << dataByte; + break; + } + } + stream << static_cast(SlipProtocolEnd); + + qCDebug(dcOwlet()) << "UART -->" << qUtf8Printable(data) << message.toHex(); + m_serialPort->write(message); + m_serialPort->flush(); +} + +void OwletSerialTransport::connectTransport() +{ + if (m_serialPort->isOpen()) + return; + + qCDebug(dcOwlet()) << "Connecting to" << m_serialPortName; + bool serialPortFound = false; + foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) { + if (serialPortInfo.systemLocation() == m_serialPortName) { + serialPortFound = true; + break; + } + } + + // Prevent repeating warnings... + if (!serialPortFound) { + if (!m_reconnectTimer->isActive()) { + m_reconnectTimer->start(); + } + return; + } + + + if (!m_serialPort->open(QIODevice::ReadWrite)) { + qCWarning(dcOwlet()) << "Could not open serial port on" << m_serialPortName << m_serialPort->errorString(); + m_reconnectTimer->start(); + return; + } + + m_reconnectTimer->stop(); + emit connectedChanged(true); +} + +void OwletSerialTransport::disconnectTransport() +{ + if (m_serialPort->isOpen()) { + m_serialPort->close(); + emit connectedChanged(false); + } +} + +void OwletSerialTransport::onReadyRead() +{ + QByteArray data = m_serialPort->readAll(); + qCDebug(dcOwlet()) << "UART <-- raw:" << data.toHex() << qUtf8Printable(data); + + for (int i = 0; i < data.length(); i++) { + quint8 receivedByte = data.at(i); + + if (m_protocolEscaping) { + switch (receivedByte) { + case SlipProtocolTransposedEnd: + m_buffer.append(static_cast(SlipProtocolEnd)); + m_protocolEscaping = false; + break; + case SlipProtocolTransposedEsc: + m_buffer.append(static_cast(SlipProtocolEsc)); + m_protocolEscaping = false; + break; + default: + // SLIP protocol violation...received escape, but it is not an escaped byte + break; + } + } + + switch (receivedByte) { + case SlipProtocolEnd: + // We are done with this package, process it and reset the buffer + if (!m_buffer.isEmpty() && m_buffer.length() >= 3) { + qCDebug(dcOwlet()) << "UART <--" << m_buffer.toHex() << qUtf8Printable(m_buffer); + emit dataReceived(m_buffer); + } + m_buffer.clear(); + m_protocolEscaping = false; + break; + case SlipProtocolEsc: + // The next byte will be escaped, lets wait for it + m_protocolEscaping = true; + break; + default: + // Nothing special, just add to buffer + m_buffer.append(receivedByte); + break; + } + } +} diff --git a/owlet/owletserialtransport.h b/owlet/owletserialtransport.h new file mode 100644 index 00000000..48eed4c5 --- /dev/null +++ b/owlet/owletserialtransport.h @@ -0,0 +1,45 @@ +#ifndef OWLETSERIALTRANSPORT_H +#define OWLETSERIALTRANSPORT_H + +#include +#include +#include +#include + +#include "owlettransport.h" + +class OwletSerialTransport : public OwletTransport +{ + Q_OBJECT +public: + explicit OwletSerialTransport(const QString &serialPortName, uint baudrate, QObject *parent = nullptr); + + bool connected() const override; + void sendData(const QByteArray &data) override; + +public slots: + void connectTransport() override; + void disconnectTransport() override; + +private slots: + void onReadyRead(); + +private: + + enum SlipProtocol { + SlipProtocolEnd = 0xC0, + SlipProtocolEsc = 0xDB, + SlipProtocolTransposedEnd = 0xDC, + SlipProtocolTransposedEsc = 0xDD + }; + + QSerialPort *m_serialPort = nullptr; + QTimer *m_reconnectTimer = nullptr; + QString m_serialPortName; + uint m_baudrate; + + QByteArray m_buffer; + bool m_protocolEscaping = false; +}; + +#endif // OWLETSERIALTRANSPORT_H diff --git a/owlet/owlettcptransport.cpp b/owlet/owlettcptransport.cpp new file mode 100644 index 00000000..a2f0a1bd --- /dev/null +++ b/owlet/owlettcptransport.cpp @@ -0,0 +1,51 @@ +#include "owlettcptransport.h" +#include "extern-plugininfo.h" + +#include + +OwletTcpTransport::OwletTcpTransport(const QHostAddress &hostAddress, quint16 port, QObject *parent) : + OwletTransport(parent), + m_socket(new QTcpSocket(this)), + m_hostAddress(hostAddress), + m_port(port) +{ + connect(m_socket, &QTcpSocket::connected, this, [=](){ + emit connectedChanged(true); + }); + connect(m_socket, &QTcpSocket::disconnected, this, [=](){ + qCDebug(dcOwlet()) << "TCP transport: Disconnected from owlet" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port); + emit connectedChanged(false); + QTimer::singleShot(1000, this, &OwletTcpTransport::connectTransport); + }); + + typedef void (QTcpSocket:: *errorSignal)(QAbstractSocket::SocketError); + connect(m_socket, static_cast(&QTcpSocket::error), this, [this](){ + qCDebug(dcOwlet()) << "TCP transport: Error in owlet communication" << m_socket->errorString(); + emit error(); + }); + + connect(m_socket, &QTcpSocket::readyRead, this, [this](){ + emit dataReceived(m_socket->readAll()); + }); +} + +bool OwletTcpTransport::connected() const +{ + return m_socket->state() == QAbstractSocket::ConnectedState; +} + +void OwletTcpTransport::sendData(const QByteArray &data) +{ + m_socket->write(data); +} + +void OwletTcpTransport::connectTransport() +{ + qCDebug(dcOwlet()) << "Connecting to" << m_hostAddress << m_port; + m_socket->connectToHost(m_hostAddress, m_port); +} + +void OwletTcpTransport::disconnectTransport() +{ + m_socket->close(); +} diff --git a/owlet/owlettcptransport.h b/owlet/owlettcptransport.h new file mode 100644 index 00000000..15a23790 --- /dev/null +++ b/owlet/owlettcptransport.h @@ -0,0 +1,30 @@ +#ifndef OWLETTCPTRANSPORT_H +#define OWLETTCPTRANSPORT_H + +#include +#include +#include + +#include "owlettransport.h" + +class OwletTcpTransport : public OwletTransport +{ + Q_OBJECT +public: + explicit OwletTcpTransport(const QHostAddress &hostAddress, quint16 port, QObject *parent = nullptr); + + bool connected() const override; + void sendData(const QByteArray &data) override; + +public slots: + void connectTransport() override; + void disconnectTransport() override; + +private: + QTcpSocket *m_socket = nullptr; + QHostAddress m_hostAddress; + quint16 m_port; + +}; + +#endif // OWLETTCPTRANSPORT_H diff --git a/owlet/owlettransport.cpp b/owlet/owlettransport.cpp new file mode 100644 index 00000000..55f481d0 --- /dev/null +++ b/owlet/owlettransport.cpp @@ -0,0 +1,11 @@ +#include "owlettransport.h" + +OwletTransport::OwletTransport(QObject *parent) : QObject(parent) +{ + +} + +bool OwletTransport::connected() const +{ + return m_connected; +} diff --git a/owlet/owlettransport.h b/owlet/owlettransport.h new file mode 100644 index 00000000..48e646a3 --- /dev/null +++ b/owlet/owlettransport.h @@ -0,0 +1,29 @@ +#ifndef OWLETTRANSPORT_H +#define OWLETTRANSPORT_H + +#include + +class OwletTransport : public QObject +{ + Q_OBJECT +public: + explicit OwletTransport(QObject *parent = nullptr); + + virtual bool connected() const; + virtual void sendData(const QByteArray &data) = 0; + +public slots: + virtual void connectTransport() = 0; + virtual void disconnectTransport() = 0; + +signals: + void connectedChanged(bool connected); + void dataReceived(const QByteArray &data); + void error(); + +protected: + bool m_connected; + +}; + +#endif // OWLETTRANSPORT_H