PDA

View Full Version : sequential execution of commands using serial communication between PC and device



anita.niharika@gmail.com
27th January 2021, 17:50
Hi All,

Using serial communication between PC and microcontroller device to send/receive set of commands.
I send a first request command to a device, and based on the response received need to send a next command. Till then I do not want to send any further commands.

I tried using a private member variable between send/receive functions by unset/set. Looks like send function is not able to see change in a variable from response function.

Can someone provide me better approach to resolve this? How about state machine usage?

Thanks,
Anita

d_stranz
27th January 2021, 19:55
I send a first request command to a device, and based on the response received need to send a next command.

If your microcontroller is sending you a response to your last command that shows it is ready for another command, then this sounds like a good opportunity to implement a signal / slot approach to the communication.

Depending on how complex the commands and responses are, you could use a state machine approach if it is complex, otherwise a simple signal . slot implementation might be good enough.

Without more details it is hard to make a recommendation.

anita.niharika@gmail.com
28th January 2021, 12:32
Giving brief details,


MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);

connect(serial, &QSerialPort::bytesWritten,
this, &MainWindow::handleBytesWritten);

connect(serial, &QSerialPort::readyRead,
this, &MainWindow::serialReadyRead);
}

void MainWindow::on_PushButton_clicked()
{
configure serial port
serial port open for readWrite
runCommands()
}

void MainWindow::writeData (const QByteArray &tx_cmd)
{
serial->write(writeData);
}

void MainWindow::parseReceivedData(QByteArray receivedData)
{
Based on the command id, data is been processed here.
switch(cmd)
{
case 1:
if (particular_bit == 1)
response = true;
else
response = false;
break;
}
. ....
.....


}

void MainWindow::serialReadyRead()
{
m_readData += serial->readAll();
....
...
QByteArray data = m_readData.mid(startOfFrame + 1, enfOfFrame - startOfFrame - 1);
parseReceivedData(data);
}

void MainWindow::runCommands()
{
1. send command to check device is connected or not
writeData(tx_cmd);

2. If received response is success, then only next command has to be sent
If device is not connected, debug print with failure message and do nothing.
}

So what should be the solution here to know, received response is successful in runCommands() function. So that I can just decide to send next command or not.

d_stranz
28th January 2021, 15:43
For the last time, use CODE tags when posting source code. See my signature if you do not know how to do that. I am tired of editing your posts to make them readable.


runCommands()

You cannot make this a single function. You need to break it up into multiple functions:

1. run first command
2. wait for response
3. run next command
4. wait for response
5. etc.

The way you implement this is to either have a queue of commands and you pop each command off the queue when it is time to run it, or you keep some kind of status variable that keeps track of which command is currently running and which one to run next. (Like your "command id").

You use signals and slots. In your parsedReceivedData method, if the previous command executed successfully, you emit a signal to "runNextCommand". In the slot that is connected to this signal, you assemble the command string and then send it to writeData. The serialReadyRead slot will be called when the command has finished, you parse the results, then go on to the next command or stop if there is an error.

anita.niharika@gmail.com
29th January 2021, 06:21
Thank you so much for timely response. I used signals and slots to make it works.

anita.niharika@gmail.com
10th February 2021, 07:32
Hi All,

Referring to the above message content, I want to go with state machine approach. Using state machine, Based on device connected/disconnected in the response data, I need to process next commands.
So how the state machine information can be accessed between send and receiver?

Best Regards,
Anita

d_stranz
10th February 2021, 17:40
So how the state machine information can be accessed between send and receiver?

Qt's state machine framework (https://doc.qt.io/qt-5/statemachine-api.html) operates using signals and slots to cause transitions as well as to signal when a transition has occurred. You should read about it and study the examples.

anita.niharika@gmail.com
10th February 2021, 18:36
Hi d_stranz,

I gone through the link which you mentioned above. In examples, state transition or connect method uses graphical signal mechanism (I mean by clicking a button).
In my case, through serial communication based on response success/failure result.

For example, The request to device connect request is from sender and response is updated in serial handler by reading a particular bit to decide device is connected/disconnected. So based on this, the now sender side has to take a call for further command processing. How does sender get this info? I don't have any graphical button to connect state transition.

Similarly, I have another device where it would require same mechanism. When both device connected, then rest of my application starts running.
Could you please give sample, which would help me to implement further.

Best Regards,
Anita

d_stranz
10th February 2021, 21:45
I don't have any graphical button to connect state transition.

Start thinking outside of the box. Any QObject-based class can emit signals, and any QObject-based class can have slots that are connected to signals.

I have implemented a very complex state machine using the Qt framework. Almost none of the state transitions are triggered by buttons (or any other UI thing). It has several hundred states and transitions and the diagram I drew is almost 1m^2. Most transitions are triggered by a signal from some other state, based on some status value.

In your case, you have at least four states: the "not started yet" state, the "execute next command" state, the "process reply" state, the "no more commands" (final) state and maybe an "error" state, and at least four transitions: the "start executing" transition that moves the machine from the "not started" into the "execute next command" state. The "execute command state" pulls the next command off the queue and starts executing. The "readyRead" signal from your serial port causes the transition into the "process reply" state, which evaluates the response and either causes a transition back to the "execute next command" state or into the "error" state. When the "execute next command" state runs out of things to do, it emits a signal that causes the transition to the "no more commands" state.

When you build the state machine in code, you have to define all of these states and transitions (by deriving from QAbstractState or QAbstractTransition if needed) and connecting the correct signals to slots.

It will probably be helpful you (like it was for me) to draw your state machine on paper, with a box for each state, and arrows that label the transitions between states and the things that trigger them. You might discover that you need more or fewer states or transitions than the ones I have described above.