Raspberry Pi control arduino atmega2560



  • Hello, I thought I would share a project I have been working on for work. I'm using a raspberry pi to send commands to an arduino atmega 2560 which either turns on/off relays or takes an analog reading.

    The raspberry pi software is written in C++ using QT Creator. It starts through user input then sends single to turn on X relay and sends command to read an analog channel at set increments. When designated time is finished it send command to turn on x relay then kills the timer.

    The arduino software is very basic. simple case statement and either reads or turns on/off relay based on command.

    DISCLAIMER: Both pieces of code are poorly written. I needed this working immediately so I hacked it to heck and back. I will be cleaning it up soon, but thought this may help somebody wanting to communicate with an arduino using c++ on the raspberry pi.

    GITHUB: https://github.com/aelmendorf/Reliability-Stress-System/

    .cpp

    #include <QtGui>
    #include <QCoreApplication>
    #include "SlotTimer.h"
    #include "ui_mainwindow.h"
    #include <vector>
    #include <algorithm>
    #include <QDebug>
    #include <QFile>
    #include <QTextStream>
    #include <QDateTime>
    
    /**
    *Author: Andrew Elmendorf
    *Date: 3/20/17
    *To-Do:
    *   1:Add Qtimer object to each slot
    *   2:convert BoardSlots to a class
    *   3:Cleanup your damn code!  There is a lot to fix here.
    *
    */
    
    SlotTimer::SlotTimer(QWidget *parent) :QMainWindow(parent),ui(new Ui::SlotTimer)
    {
        ui->setupUi(this);
        this->arduino=new QSerialPort();
        this->connected=false;
    
        //Setup the table
        this->model = new QStandardItemModel(10,7,this);
        this->model->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
        this->model->setHorizontalHeaderItem(1, new QStandardItem(QString("BoardSlot")));
        this->model->setHorizontalHeaderItem(2, new QStandardItem(QString("Start Time")));
        this->model->setHorizontalHeaderItem(3, new QStandardItem(QString("Last Measure")));
        this->model->setHorizontalHeaderItem(4, new QStandardItem(QString("Duration")));
        this->model->setHorizontalHeaderItem(5, new QStandardItem(QString("Elapsed")));
        this->model->setHorizontalHeaderItem(6, new QStandardItem(QString("Reading")));
        this->model->setHorizontalHeaderItem(7, new QStandardItem(QString("Running?")));
    
        //build slot list
        for(int i=0;i<8;i++)
            this->boardSlots.push_back(BoardSlot(0,i+1));
    
        //set status table status to not loaded
        for(int i=0;i<8;i++){
            QStandardItem *item = new QStandardItem(QString("Not Loaded"));
            this->model->setItem(i,0,item);
            //this->boardSlots.push_back(BoardSlot(0));
        }//End for loop
    
        ui->timerList->model();
        ui->timerList->setModel(this->model);
    }//End constructor, init program
    
    void SlotTimer::on_Start_t_clicked()
    {
        bool found=false;
        int index=0;
    
        //Find index of requested slot
        for(int i=0;i<this->boardSlots.size();i++){
            if(this->boardSlots[i].channel==(long)ui->channel_in->value()){
                found=true;
                index=i;
            }//end if == channel
        }//end loop through
    
        if(found){
            if(!this->boardSlots[index].running && !this->boardSlots[index].loaded){
    
                //set the table values
                this->boardSlots[index].duration_t=(long)ui->duration->value()*this->hrsTomilli;
                this->boardSlots[index].channel=(long)ui->channel_in->value();
                this->boardSlots[index].start=QTime::currentTime();
                this->boardSlots[index].current=this->boardSlots[index].start;
                this->boardSlots[index].inc=(long)ui->increment->value()*this->minsToMilli;
                this->boardSlots[index].elapsed_t=this->boardSlots[index].inc;
                this->boardSlots[index].running=true;
                this->boardSlots[index].loaded=true;
                this->boardSlots[index].slotName=ui->testName_in->text();
                this->boardSlots[index].filename+=this->boardSlots[index].slotName+"_data.txt";
    
                ui->current->setText(this->boardSlots[index].start.toString("hh:mm:ss"));
    
                this->boardSlots[index].ID=startTimer((int)this->boardSlots[index].inc);
    
                ui->ID->setText(QString::number(this->boardSlots[index].ID));
    
                this->fillTable(this->boardSlots[index]);
    
    
                //Write out to the arduino
                // "PX Y"  P: switching relay designator X:channel 1-8  Y:  on or off
                QByteArray output;
                QString txt;
                QTextStream(&txt)<<"P"<<this->boardSlots[index].channel<<" 0\n";
                output =txt.toLocal8Bit();
                this->arduino->write(output);
                this->arduino->flush();
    
            }else{
                this->sendUserMessage("Warning","Board Loaded");
            }//End check for slot loaded
        }else{
            this->sendUserMessage("Warning","Slot Not Found");
        }//End for slot found
    }//End start timer procedure
    
    void SlotTimer::timerEvent(QTimerEvent *e)
    {
    
        std::vector<BoardSlot>::iterator itr;
        itr = std::find (this->boardSlots.begin(),this->boardSlots.end(),BoardSlot(e->timerId()));
    
        if (itr != this->boardSlots.end()){
              itr->elapsed_t=itr->elapsed_t+itr->inc;
              if(!itr->check()){
                  itr->current=QTime::currentTime();
                  this->fillTable(*itr);
    
                  //Request Current Reading
                  QByteArray output;
                  QString txt;
                  QTextStream(&txt)<<"R"<<itr->channel<<"\n";
                  output =txt.toLocal8Bit();
                  this->arduino->write(output);
                  this->arduino->flush();
              }else{
                 itr->running=false;
    
                 this->model->setItem(itr->channel-1,7,new QStandardItem(QString("Done")));
                 killTimer(itr->ID);
    
                 //sending a message to arduino
                 QByteArray output;
                 QString txt;
                 // "PX Y"  P: switching relay designator X:channel 1-8  Y:  on or off
                 QTextStream(&txt)<<"P"<<itr->channel<<" 1\n";
                 output =txt.toLocal8Bit();
                 this->arduino->write(output);
                 this->arduino->flush();
              }//if not finished do continue on, if done stop it all
        }else{
            this->sendUserMessage("Error","No Such slot");
        }// if slot not found.  should never happen
    }//End timer event
    
    void SlotTimer::readData()
    {
        if (!this->arduino){
            qDebug()<<"Why am I here!?!";
            //Need a message
            return;
        }
            QByteArray in;
    
            //get buffer size and resize
            int buffSize=this->arduino->bytesAvailable();
            in.resize(buffSize);
    
            //read and append the data
            this->arduino->read(in.data(),in.size());
            this->recieved.append(in);
    
            //if it is a newline stop and finalize read.  there is no guarantee that this
            //function will be called once for one command.  have to make a buffer in struct/class and append until finished
            if(in.contains('\n')){
                QString str = QString::fromUtf8(recieved.split('\n').first());
                ui->current->setText(str);
                int index=str.split(':').first().toInt()-1;
                this->boardSlots[index].lastRead=str.split(':').last().toLong();
                this->writeDataOut(this->boardSlots[index]);
                recieved.clear();
            }//End terminator check
    }//End serial read data
    
    void SlotTimer::setControlsEnabled(bool enable)
    {
        ui->Start_t->setEnabled(enable);
    }//End button enabled
    
    void SlotTimer::on_connectSerial_clicked()
    {
        QSerialPortInfo port;
    
        //Find the arduino port using vendor and manufacturer IDs
        foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
            ui->serialPortList->addItem(info.portName());
            if(info.vendorIdentifier()==this->arduinoVendorID && info.productIdentifier()==this->megaProductID){
                qDebug()<<info.manufacturer();
                qDebug()<<info.vendorIdentifier();
                qDebug()<<info.productIdentifier();
                port=info;
                //found=true;
            }//end check for arduino
        }//end iteration of open ports
    
        //Set the port up!
        this->arduino->setPortName(port.portName());
        this->arduino->setBaudRate(QSerialPort::Baud9600);
        this->arduino->setDataBits(QSerialPort::Data8);
        this->arduino->setParity(QSerialPort::NoParity);
        this->arduino->setStopBits(QSerialPort::OneStop);
        this->arduino->setFlowControl(QSerialPort::NoFlowControl);
        this->arduino->open(QIODevice::ReadWrite);
        this->arduino->setDataTerminalReady(true);
    
        //Connect the slot for readData()
        connect(arduino, SIGNAL(readyRead()),this,SLOT(readData()));
    
        //making sure setup worked.
        this->connected=this->arduino->isWritable() && this->arduino->isReadable();
    
        if(this->connected==true){
            ui->serialStatus->setText("Connected");
            qDebug()<<"Success!";
        }// if success!!
    }//connect serial
    
    void SlotTimer::fillTable(const BoardSlot &s)
    {
        this->model->setItem(s.channel-1,0,new QStandardItem(s.slotName));
        this->model->setItem(s.channel-1,1,new QStandardItem(QString::number(s.channel)));
        this->model->setItem(s.channel-1,2,new QStandardItem(QString(s.start.toString("hh:mm:ss"))));
        this->model->setItem(s.channel-1,3,new QStandardItem(QString(s.current.toString("hh:mm:ss"))));
        this->model->setItem(s.channel-1,4,new QStandardItem(QString::number((double)s.duration_t/SlotTimer::hrsTomilli)));
        this->model->setItem(s.channel-1,5,new QStandardItem(QString::number((double)s.elapsed_t/SlotTimer::hrsTomilli)));
        if(s.running){
            this->model->setItem(s.channel-1,7,new QStandardItem(QString("Running")));
        }else{
            this->model->setItem(s.channel-1,7,new QStandardItem(QString("Not Running")));
        }//End check for running
            this->model->setItem(s.channel-1,6,new QStandardItem(QString::number(s.lastRead)));
    }//End fill table
    
    void SlotTimer::clearTable()
    {
        for(int i=0;i<8;i++){
            this->model->setItem(i,0,new QStandardItem("Not Loaded"));
            this->model->setItem(i,1,new QStandardItem(" "));
            this->model->setItem(i,2,new QStandardItem(" "));
            this->model->setItem(i,3,new QStandardItem(" "));
            this->model->setItem(i,4,new QStandardItem(" "));
            this->model->setItem(i,5,new QStandardItem(" "));
            this->model->setItem(i,6,new QStandardItem(" "));
            this->model->setItem(i,7,new QStandardItem(" "));
        }//End for loop
    }//End fill table
    
    
    void SlotTimer::clearSlot(int s)
    {
        this->model->setItem(s,0,new QStandardItem("Not Loaded"));
        this->model->setItem(s,1,new QStandardItem(" "));
        this->model->setItem(s,2,new QStandardItem(" "));
        this->model->setItem(s,3,new QStandardItem(" "));
        this->model->setItem(s,4,new QStandardItem(" "));
        this->model->setItem(s,5,new QStandardItem(" "));
        this->model->setItem(s,6,new QStandardItem(" "));
        this->model->setItem(s,7,new QStandardItem(" "));
    }//End fill table
    
    void SlotTimer::on_timerList_clicked(const QModelIndex &index)
    {
        qDebug()<<index.row();
    }
    
    
    void SlotTimer::on_Unload_Chan_clicked()
    {
        if(!this->boardSlots[ui->channel_in->value()-1].running){
            int channel=ui->channel_in->value();
            QString msg="Unload channel "+QString::number(channel)+"?";
            if(this->getUserResponce("Unload?",msg))
            {
             this->boardSlots[channel-1].reset();
             this->clearSlot(channel-1);
            }//end check user response
        }else{
            this->sendUserMessage("Warning","Board Loaded");
        }//End check if slot as running board
    }//End unload
    
    void SlotTimer::on_Stop_Chan_clicked()
    {
        if(this->boardSlots[ui->channel_in->value()-1].running){
            int channel=ui->channel_in->value();
            QString msg="Stop channel "+QString::number(channel)+"?";
            if(this->getUserResponce("Unload?",msg))
            {
    
                this->boardSlots[ui->channel_in->value()-1].running=false;
                killTimer(this->boardSlots[ui->channel_in->value()-1].ID);
                QByteArray output;
                QString txt;
                QTextStream(&txt)<<"P"<<ui->channel_in->value()<<" 1\n";
                output =txt.toLocal8Bit();
                this->arduino->write(output);
                this->arduino->flush();
            }//End check with user
        }else{
            this->sendUserMessage("Error","Not Running");
        }//End check if running
    }//End stop channel
    
    void SlotTimer::writeDataOut(const BoardSlot &s)
    {
        QFile out(s.filename);
        if (!out.open(QIODevice::ReadOnly | QIODevice::Text | QIODevice::ReadWrite |QIODevice::Append)){
            qDebug() << "FAIL TO CREATE FILE / FILE NOT EXIT***";
        }else{
            QTextStream stream(&out);
            QDateTime temp=QDateTime::currentDateTime();
            stream <<s.lastRead<<"\t"<<temp.toString("dd.MM.yyyy")<<"\t"<<s.current.toString("hh:mm:ss")<<endl;
            out.close();
        }//End check if file
    }//End write data out
    
    bool SlotTimer::getUserResponce(const QString &t,const QString &msg)
    {
        bool ret=false;
        QMessageBox::StandardButton reply;
        reply = QMessageBox::question(0,t,msg,QMessageBox::Yes|QMessageBox::No);
        if (reply == QMessageBox::Yes) {
          ret=true;
        } else {
            ret=false;
        }//End set ret answer
        return ret;
    }//end request feedback
    
    void SlotTimer::sendUserMessage(const QString &t,const QString &msg)
    {
        QMessageBox messageBox;
        messageBox.critical(0,t,msg);
        messageBox.setFixedSize(500,200);
    }//End send message
    
    SlotTimer::~SlotTimer()
    {
        if(this->arduino->isOpen()){
            this->arduino->close();
        }//end serial open check
        delete ui;
    }//
    

    .h

    #ifndef SLOTTIMER_H
    #define SLOTTIMER_H
    
    #include <QMainWindow>
    #include <QtGui>
    #include <QSerialPort>
    #include <QSerialPortInfo>
    #include <string>
    
    
    namespace Ui
    {
        class SlotTimer;
    }//UI namespace
    
    class SlotTimer : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit SlotTimer(QWidget *parent = 0);
        ~SlotTimer();
    
        struct BoardSlot
        {
            QTime current,start;
            long elapsed_t;
            long duration_t;
            long inc;
            long ID;
            int channel;
            bool running;
            bool loaded;
            long lastRead;
            QString slotName;
            QString filename;
    
            //Constructor overload for timerID
            BoardSlot(int id)
            {
                ID=id;
                loaded=false;
                running=false;
                lastRead=0;
                filename="/home/pi/Monitor/";
                slotName="";
                elapsed_t=0;
                duration_t=0;
            }//end timerID overload
    
            //constructor overload for initial channel and timerID
            BoardSlot(int id,int c)
            {
                ID=id;
                channel=c;
                loaded=false;
                running=false;
                lastRead=0;
                filename="/home/pi/Monitor/";
                slotName="";
                elapsed_t=0;
                duration_t=0;
            }//end channel/timerID overload
    
            //reset this timer
            void reset()
            {
                ID=0;
                loaded=false;
                running=false;
                lastRead=0;
                filename="/home/pi/Monitor/";
                slotName="";
                elapsed_t=0;
                duration_t=0;
            }//End timer reset
    
            //checking if done
            bool check()
            {
                return elapsed_t>duration_t;
            }//end check if done
    
            //equals overload for vector search
            bool operator==(const BoardSlot& b) const
            {
              return ID == b.ID;
            }//end equals overload
        };//End BoardSlot struct
    
        //Constants for timer conversion
        static const long hrsTomilli=3600000;
        static const long minsToMilli=60000;
    
    
        QSerialPort *arduino;  //serial port of arduino
        QStandardItemModel *model; //model for building display table
        std::vector<BoardSlot> boardSlots;//  using only in timerEvent.  really don't need a vector, left because ran out of time
        bool connected; //are we connected/
        QByteArray recieved; //from arduino buffer
        void setControlsEnabled(bool enable);//enable/disable controls.  NOT EVEN USING
        void fillTable(const BoardSlot &s);//fille the table
        void writeDataOut(const BoardSlot &s);//write out to a txt file
        void clearTable();//clear the table
        void sendUserMessage(const QString &t,const QString &msg);//send a message a user
        bool getUserResponce(const QString &t,const QString &msg);//send a message a user requesting feedback
        void clearSlot(int s);//clear that slot!
    
    private slots:
        void timerEvent(QTimerEvent *e);//timer events!
        void readData();//read in serial
        void on_Start_t_clicked();//start a timer
        void on_connectSerial_clicked();//connect to the arduino
        void on_timerList_clicked(const QModelIndex &index);//not even used, was going to have the table as the slot selector.  still might
        void on_Unload_Chan_clicked();//unload a channel
        void on_Stop_Chan_clicked();//stop a channel
    
    
    private:
        Ui::SlotTimer *ui;
        static const quint16 arduinoVendorID=9025;//arduino vendor ID for finding the port
        static const quint16 megaProductID=66;//arduino atmega product ID ID for finding the port
    
        void processError(const QString &error);//not even using, will implement later
        void initSerial();//initialize the arduino com!
    
    };
    
    #endif // SLOTTIMER_h
    
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.