#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), fs_serial(new QSerialPort()), co2_serial(new QSerialPort()), fs_open_timer(new QTimer()), co2_open_timer(new QTimer()), co2_cmd_timer(new QTimer()) { ui->setupUi(this); // Für den SDS011 UART wird nur usbserial und das ch341-uart-Kernelmodul // benötigt, normalerweise beides im Kernel, nur auf dem 355x-Board leider nicht! // Kernel-Update? fs_serial->setPortName("/dev/ttyUSB0"); co2_serial->setPortName("/dev/ttyUSB1"); // Gute Gelegenheit, um auch über UARTs mit und ohne Bitbanging // (https://de.wikipedia.org/wiki/Bit-Banging) // zu sprechen -> könnte das auch das Problem mit dem Touch-Display sein? // Alle 1000 ms versuchen, den UART zu öffnen, bis es klappt. connect(fs_open_timer, &QTimer::timeout, this, &MainWindow::openSerialFS); connect(co2_open_timer, &QTimer::timeout, this, &MainWindow::openSerialCO2); connect(co2_cmd_timer, &QTimer::timeout, this, &MainWindow::writeCmdCO2); fs_open_timer->start(1000); co2_open_timer->start(1000); } // Achtung: Die Schnittstelle muss für den User lesbar sein, also nach Einstecken: // sudo chmod +rw /dev/ttyUSB0 void MainWindow::openSerialFS(){ if(fs_serial->isOpen()) { fs_open_timer->stop(); qDebug() << "Reopening serial device..."; disconnect(fs_serial, &QSerialPort::readyRead, 0, 0); disconnect(fs_serial, &QSerialPort::errorOccurred, 0, 0); fs_serial->clearError(); fs_serial->close(); // Alle 1000 ms versuchen, den UART zu öffnen, bis es klappt. ui->label_1->setText("Warte auf Daten..."); ui->label_2->setText("Warte auf Daten..."); fs_open_timer->start(1000); } if(fs_serial->open(QIODevice::ReadOnly)){ fs_open_timer->stop(); if( !fs_serial->setBaudRate(QSerialPort::Baud9600) || !fs_serial->setDataBits(QSerialPort::Data8) || !fs_serial->setParity(QSerialPort::NoParity) || !fs_serial->setStopBits(QSerialPort::OneStop) || !fs_serial->setFlowControl(QSerialPort::NoFlowControl)) qDebug()<errorString(); connect(fs_serial, &QSerialPort::readyRead, this, &MainWindow::readDataFS); // Try to reopen port on error connect(fs_serial, &QSerialPort::errorOccurred, this, &MainWindow::openSerialFS); } else { qDebug() << "Serial Error: " << fs_serial->errorString(); } } void MainWindow::openSerialCO2(){ if(co2_serial->isOpen()) { qDebug() << "Reopening serial device..."; co2_cmd_timer->stop(); co2_open_timer->stop(); disconnect(co2_serial, &QSerialPort::readyRead, 0, 0); disconnect(co2_serial, &QSerialPort::errorOccurred, 0, 0); co2_serial->clearError(); co2_serial->close(); // Alle 1000 ms versuchen, den UART zu öffnen, bis es klappt. connect(co2_open_timer, &QTimer::timeout, this, &MainWindow::openSerialCO2); ui->label_0->setText("Warte auf Daten..."); co2_open_timer->start(1000); } if(co2_serial->open(QIODevice::ReadWrite)){ /* * 1000 ppm range: 0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x03, 0xE8, 0x7B * 2000 ppm range: 0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x8F * 3000 ppm range: 0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x0B, 0xB8, 0xA3 * 5000 ppm range: 0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB */ unsigned char calibration[] = { 0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x8F }; co2_cmd_timer->stop(); co2_open_timer->stop(); if( !co2_serial->setBaudRate(QSerialPort::Baud9600) || !co2_serial->setDataBits(QSerialPort::Data8) || !co2_serial->setParity(QSerialPort::NoParity) || !co2_serial->setStopBits(QSerialPort::OneStop) || !co2_serial->setFlowControl(QSerialPort::NoFlowControl)) qDebug()<errorString(); co2_serial->write((char *)calibration,sizeof(calibration)); connect(co2_serial, &QSerialPort::readyRead, this, &MainWindow::readDataCO2); // Try to reopen port on error connect(co2_serial, &QSerialPort::errorOccurred, this, &MainWindow::openSerialCO2); co2_cmd_timer->start(10000); } else { qDebug() << "Serial Error: " << co2_serial->errorString(); } } // Wäre eine sinnvolle Erweiterung: Signal/Slot setzen für QSerialPort::error, und in diesem Fall // serial-> close() aufrufen und wieder den Timer starten zu neu öffnen der Schnittstelle. // Auch möglich: Statt einen festen Port zu öffnen, ALLE Serial Ports durchgehen und nach einem suchen, // der das gewünschte Datenformat liedern. Dafür gibts in QSerialPort einen Iterator. void MainWindow::readDataFS(){ // Read Frames of 10 bytes, see https://cdn-reichelt.de/documents/datenblatt/X200/SDS011-DATASHEET.pdf const int size = 10; unsigned char data[size]; int bytes = fs_serial->bytesAvailable(); if( bytes < size) { // qDebug() << "Possibly invalid data frame of " << bytes << " bytes."; return; } fs_serial->read((char *)data,size); // Decode Frame if(data[0] == 0xaa && data[1] == 0xc0 && data[9] == 0xab){ double p1 = data[3]*256.0+data[2]/10.0; double p2 = data[5]*256.0+data[4]/10.0; ui->label_1->setText(QString::number(p1,'f',2).append(" µg/m³ PM2.5")); ui->label_2->setText(QString::number(p2,'f',2).append(" µg/m³ PM10 ")); } else { qDebug() << "Skipping invalid data."; } // fs_serial->flush(); } void MainWindow::writeCmdCO2(){ const unsigned char cmd[] = { 0xff, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79 }; co2_serial->write((char *)cmd,sizeof(cmd)); } void MainWindow::readDataCO2(){ const int size = 9; unsigned char data[size]; int bytes = co2_serial->bytesAvailable(); if( bytes < 9) { // qDebug() << "Possibly invalid data frame of " << bytes << " bytes."; return; } co2_serial->read((char *)data, size); if(data[0] != 0xff && data[1] != 0x86) { qDebug() << "data frame of " << bytes << " bytes doesn't start with 0xff 0x86."; co2_serial->flush(); return; } // 0xFF CM HH LL TT SS Uh Ul CS unsigned int co2 = ((unsigned int)data[2] << 8) + data[3]; int temp = data[4] - 40; QPalette pal; pal.setColor(QPalette::Background, (co2 > 1000) ? Qt::red : Qt::green); ui->label_0->setPalette(pal); ui->label_0->setText(QString::number(co2,'f',2).append(" PPM").append(", Temp = ").append(QString::number(temp,'f',2))); ui->label_4->setPalette(pal); } MainWindow::~MainWindow() { delete ui; fs_serial->close(); delete fs_serial; co2_serial->close(); delete co2_serial; fs_open_timer->stop(); delete fs_open_timer; co2_open_timer->stop(); delete co2_open_timer; co2_cmd_timer->stop(); delete co2_cmd_timer; }