QSerialPort ->write() or read() not as expected on clicked() signal - qt

I'm working on a Qt5 project. I have a button that issues a clicked() signal and its slot has the following code which writes to the serial port:
void Dialog::on_startStream_clicked()
{
QByteArray ba;
if(esp->isOpen()){
esp->write("1");
if(esp->canReadLine()){
ba = esp->readLine();
ba.replace("\xFE", "");
ba = ba.simplified();
QString ba_with_time = stamp->currentDateTime().toString("MM.dd.yyyy hh:mm:ss ");
ba_with_time.append(ba);
ui->output->appendPlainText(ba_with_time);
qDebug() << ba_with_time;
}
}
}
After establishing the connection to the serial port, the first time I click on the button nothing happens. Subsequent clicks work correctly.
I'm using PlatformIO to upload the Arduino code to the ESP32 and in the PlatformIO serial monitor there is output immediately after I issue the command on the first time, which makes me think the problem is my Qt code.
How can I fix it so that Qt can read the buffer on the first button click?

QByteArry ba; // member of Dialog class
// connect the QSerialPort::readyRead() signal after creation of esp
connect(esp, &QSerialPort::readyRead, this, &Dialog::onDataReady);
...
void Dialog::on_startStream_clicked()
{
if(esp->isOpen()){
esp->write("1");
}
}
// The "onDataReady()" is called every time you receive data via serial port
void Dialog::onDataReady()
{
do{
ba += esp->readAll(); // buffer received data
int i = ba.indexOf("\n"); // i assume your message is \n terminated
if(i != -1){
QByteArray ba1 = ba.mid(0, i);
// modify the data
qDebug() << ba1;
ba.remove(0, i); // remove message from receive buffer
}
} while(esp->bytesAvailable());
}

Related

Unable to store stream data in variable in Arduino programming

I'm unable to store the serial.port value in a variable. I want to send a message from Android telnet app, on and off. If on comes I want to print fan on, if off comes I want to print off. I'm able to print on and off while I'm statically fixing value. I'm unable to store the stream in a variable.
String stringOne;
void setup() {
digitalWrite(13, LOW);
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// send an intro:
Serial.println("\n\nString substring():");
Serial.println();
pinMode(13,OUTPUT);
}
void loop() {
digitalWrite(13,LOW);
// Set up a String:
stringOne ="+IPD 0,14 :ON";
int length = stringOne.length();
Serial.println(stringOne.length());
Serial.println(stringOne);
if (Serial.available() > 0) {
// substring(index) looks for the substring from the index position to the end:
if (stringOne.substring(length-2,length) == "ON") {
Serial.println("FAN ON");
digitalWrite(13,HIGH);
// delay(2000);
}
if (stringOne.substring(length-3,length) == "OFF") {
Serial.println("FAN OFF");
digitalWrite(13,LOW);
// delay(2000);
}
}
// you can also look for a substring in the middle of a string:
// do nothing while true:
while (true);
}
Well, while editing your question, I couldnt help noticing that infinite loop at the end of your code.
// do nothing while true:
while(true)
In this case, even if your code was all right, you cant expect to get next data.
void loop --> Remember it is itself a infinite loop
update 1:
your logic to use the serial port is wrong;
Remember, serial port only recieves a single character at a time.
if you send "hello" from pc, at the other end, arduino will recieve h, e, l, l, o
The trick is to collect all letters into a array. and then use our logic in it.
char commandbuffer[20]; //an array to hold our characters
int i=0;
if (Serial.available() > 0) {
while( Serial.available() >0) { //read until all data we send arrives
char c = Serial.read();
commandbuffer[i]= c; //we are actually storing it one by one
i++;
}
}
commandbuffer[i]='\n';
for(int j = 0; j<i; j++){
Serial.print(commandbuffer[j]);// and show it one by one too
}
now when you send "hello", it will print hello back. I hope this give you some idea. Happy coding.

Arduino Uno + ESP8266 reading server's response

I'm sending a GET request from Arduino Uno using ESP8266. The request is sent, but I'm unable to print the received response.
I'm using code from https://elementztechblog.wordpress.com/2015/05/13/esp8266-based-temperature-data-logger-using-arduino/
I have changed the code for connecting to my server and I can see the GET request is received on my server's log.
I tried putting
while (ser.available())
{
Serial.write(ser.read());
}
after the Serial.println("AT+CIPCLOSE"); statement.
BUT I'm not getting anything on Serial monitor after "AT+CIPCLOSE"
EDIT:
Here's my entire code:
// connect 10 to TX of Serial USB
// connect 11 to RX of serial USB
SoftwareSerial ser(10, 11); // TX, RX
// this runs once
void setup()
{
// enable debug serial
Serial.begin(9600);
// enable software serial
ser.begin(9600);
// reset ESP8266
ser.println("AT+RST");
}
// the loop
void loop()
{
// TCP connection
String cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += "192.168.0.25";
cmd += "\",3000";
ser.println(cmd);
if(ser.find("Error"))
{
Serial.println("AT+CIPSTART error");
return;
}
// prepare GET string
String getStr = "GET /api/myservice";
getStr += "\r\n\r\n";
// send data length
cmd = "AT+CIPSEND=";
cmd += String(getStr.length());
ser.println(cmd);
if(ser.find(">")){
ser.print(getStr);
}
else
{
ser.println("AT+CIPCLOSE");
// alert user
Serial.println("AT+CIPCLOSE");
// CODE I FOUND FOR READING THE REPLY FROM SERVER:
while (ser.available())
{
// char c = ser.read();
Serial.write(ser.read());
// if (c == '\r') Serial.print('\n');
}
}
delay(1000);
}
ESP Details:
ESP-01
AT version: 0.40.0.0
If you are only struggling to read a response then the answer is simple;
You are closing the TCP connection before you try to read:
ser.println("AT+CIPCLOSE");
// alert user
Serial.println("AT+CIPCLOSE");
// CODE I FOUND FOR READING THE REPLY FROM SERVER:
while (ser.available())
{
Move the reading while into the above block just beneath ser.print(getStr); but add a delay between the two as well.

Arduino Serial.println is printing two lines

I am doing some simple arduino projects in an effort to learn some of the basics.
For this project I am trying to print a line sent through the serial monitor. When I print the line, my leading text prints along with the first character of the user input, and then a new line starts and the leading text prints again along with the rest of the user data. I'm not sure why this is happening.
Here is my code:
char data[30];
void setup()
{
Serial.begin(9600);
}
void loop()
{
if (Serial.available())
{
//reset the data array
for( int i = 0; i < sizeof(data); ++i )
{
data[i] = (char)0;
}
int count = 0;
//collect the message
while (Serial.available())
{
char character = Serial.read();
data[count] = character;
count++;
}
//Report the received message
Serial.print("Command received: ");
Serial.println(data);
delay(1000);
}
}
When I upload the code to my Arduino Uno, and open the serial monitor, I can type in a string like: "Test Message"
When I hit enter, I get the following result:
Command received: T
Command received: est Message
When what I was expecting was:
Command received: Test Message
Can someone point me in the right direction?
Thanks in advance for the help.
Serial.available() doesn't return a boolean it returns how many bytes are in the Arduino's serial buffer. Because you are moving that buffer into a list of 30 chars you should check that the serial buffer is 30 chars long with the condition Serial.available() > 30.
This could be causing the code to execute once as soon as the serial buffer has any data, hence it running for the first letter then again realising more has been written to the buffer.
I'd recommend also completely removing your data buffer and using the data direct from the serial's buffer. e.g
Serial.print("Command received: ");
while (Serial.available()) {
Serial.print((char)Serial.read());
}
Edit: How to wait until serial data finishes being sent
if (Serial.available() > 0) { // Serial has started sending
int lastsize = Serial.available(); // Make a note of the size
do {
lastsize = Serial.available(); // Make a note again so we know if it has changed
delay(100); // Give the sender chance to send more
} while (Serial.available() != lastsize) // Has more been received?
}
// Serial has stopped sending

Arduino Serial communication output

I have 2 Arduinos Leonardo and I want them to communicate itself, so I did the following code:
void setup() {
Serial.begin(9600);
Serial1.begin(9600);
}
void loop() {
String outMessage = ""; // String to hold input
while (Serial.available() > 0) { // check if at least 1 char is available
char inChar = Serial.read();
outMessage.concat(inChar); // add inChar to outMessage
}
if (outMessage != "") {
Serial.println("Sent: " + outMessage); // View Arduino 1 in Serial Monitor 1
Serial1.print(outMessage); // Send to Arduino 2
}
while (Serial1.available() > 0) {
Serial.print("Received: "); // View Arduino 1 in Serial Monitor 2
Serial.print(Serial1.read()); // Received from Arduino 1
Serial.println();
}
}
I want to send a message from Arduino 1, print in Serial Monitor and send via TX1 to Arduino 2 and vice-versa. The problem is that I don't receive what I was expecting. For instance if I type test:
Arduino 1:
Sent: test
Arduino 2:
Received: t
Received: e
Received: s
Received: t
I also tryed to do the receiving side like the sending side and use Serial.write but with no sucess.
Is there a easier way to do that or to fix it?
Thanks
Has mentioned by Hans, you need a protocol.
This is what I use to consider a message in Arduino to be a complete message:
char inData[10];
int index;
boolean started = false;
boolean ended = false;
String message =("I am Arduino 1 and I am ready");
void setup(){
Serial.begin(9600);
Serial.println(message);
}
void loop()
{
while(Serial.available() > 0)
{
char aChar = Serial.read();
if(aChar == '>')
{
started = true;
index = 0;
inData[index] = '\0';
}
else if(aChar == '<')
{
ended = true;
}
else if(started)
{
inData[index] = aChar;
index++;
inData[index] = '\0';
}
}
if(started && ended)
{
int inInt = atoi(inData);
Serial.println(inInt);
}
// Get ready for the next time
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
So, basically a message is considered completed only if it is between the special characters ><, like this: >message<. Then you can do the same on reading.
It does not have to be too complicated. If you look carefully at your last whlie-loop you can see that the software does not get a chance to read more than one character each time it passes through the loop. So that is what you get: one character at a time.
In your first while-loop you did better: you collected all the incoming letters until nothing was available and then sent them all at once. So if you make your last loop look more like the first one, you'll get a better result.
As mentioned a protocol to frame messages is needed between devices. A quick way to do this is to use Bill Porter's EasyTransfer library which does exactly what you are trying to do, over either UART or I2C. It has several examples.
Serial.read() reads only one byte every time you use it. A simple solution would be to store each byte on a char array while Serial.available>0 and then print the String with the whole message that was sent.
char message[40];
int count = 0;
while(Serial.available()>0){
message[count++] = Serial.read();
}
Serial.println(message);

Arduino Loop and alarm

I'm working on my first physical computing project with an arduino, well actually a seeduino stalker 2.1. I'm building a device to record water collection rates over time.
Getting the project set up and running hasn't been all that hard, until today that is. Inside the main loop I have a call to the a method that handles the logging. I also now an alarm delay to handle a timer repeat I need in order to summarize data and send it via SMS to a recipient number.
The issue is that when the alarm.repeat() is active it preempts the logging of the data. The question is: why is the logging method inside the loop not working when the alarm.delay is there?
void setup() {
Serial.begin(9600);
Wire.begin();
setTime(1,17,0,1,1,13); // set time
Alarm.timerRepeat(60, Repeats); //set repeater
}
void loop(){
logging(); //call logging
Alarm.delay(1300); //for repeater
}
void Repeats(){
Serial.println("the repeater fired"); // just to see it working
}
void logging(){
val = digitalRead(Sensor_Pin); // read Sensor_Pin
if (val == HIGH) {
// If Sensor N.C. (no with magnet) -> HIGH : Switch is open / LOW : Switch is closed
// If Sensor N.0. (nc with magnet) -> HIGH : Switch is closed / LOW : Switch is open
digitalWrite(Led_Pin, LOW); //Set Led low
//Serial.print("status -->");
//Serial.println("low");
//delay(500);
} else {
digitalWrite(Led_Pin, HIGH); //Set Led high
logdata();
}
}
void logdata(){
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File myFile = SD.open("datalog.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
//DateTime now = RTC.now();
//String myString = readTimestamp(now);
time_t t = now();
String aDate = String(year(t))+"/"+String(month(t))+"/"+String(day(t))+" "+String(hour(t))+":"+String(minute(t))+":"+String(second(t));
myFile.println(aDate);
// close the file:
myFile.close();
Serial.println(aDate);
delay(500); } else {
// if the file didn't open, print an error:
// Serial.println("error opening DATALOG.TXT");
}
}
Q: Why must I use Alarm.delay() instead of delay()? A: Task scheduling
is handled in the Alarm.delay function. Tasks are monitored and
triggered from within the Alarm.delay call so Alarm.delay should be
called whenever a delay is required in your sketch. If your sketch
waits on an external event (for example, a sensor change), make sure
you repeatedly call Alarm.delay while checking the sensor.
From the FAQ of the Alarm library. So it looks like Alarm.Delay is just like the standard delay but can be interrupted by scheduled events. Your logging call isn't scheduled, it just happens at the start of the loop. ..is your logging not happening at all? It looks like it should be called at the start of each loop, then a 1300 delay with your repeater firing during the delay.
On your logdata() function you're calling delay(50) instead of Alarm.delay(50).
As caude pointed, you have to use Alarm.delay when a delay is needed, otherwise the delay will mess up with the alarms.
I think you could have done in other way using timer library. If you say the data has to be logged every second,it easier to done by timer.Example code
#include <SimpleTimer.h>
// the timer object
SimpleTimer timer;
// a function to be executed periodically
void repeatMe() {
Serial.print("Uptime (s): ");
Serial.println(millis() / 1000);
}
void setup() {
Serial.begin(9600);
timer.setInterval(1000, repeatMe);
}
void loop() {
timer.run();
}

Resources