Checking serial port for characters

Hi Guys,

Up until two weeks ago I had never tried coding on the ardunio platform, having used PicBASIC PRO to develop small projects in the past. However, after a lot of badgering from a friend I agreed to giving it a go, and over the past couple of weeks have turned the examples into something that might replace an old PIC based project.

One thing I need to to is communicate with a PC application. This is done in simple terms, in that the PC applications connects via serial to the PIC. The PIC code checks each time the code loops to see if there is a character in the buffer. If it finds a Q it jumps to a subroutine that sends a long string of data to the PC. If it finds an S then it jumps to a subroutine that awaits a similar long string and places it into the variables.

The process is handled by a case statement

    IF RCIF=1 THEN GOto coms                   ; Check to see id PC application connected

coms:

HSERIN [nTest]
    SELECT CASE nTest     
    CASE "Q"                    ; if Q then send data to PC
    Goto Term_TX 
    CASE "S"                    ; if S then receive data from PC
    goto Term_RX
    end select

With the two subrotines

Term_RX:

For Counter = 0 to 3
HSERIN 1000, RX_Bombed, [DEC3 NormTemp[Counter]]
NEXT

For Counter = 0 to 3
HSERIN 1000, RX_Bombed, [DEC3 AlarmLow[Counter]]
NEXT

etc 

etc

Term_TX:


Hserout [DEC3 Temperatures(0)]
Hserout [DEC3 Temperatures(1)]
Hserout [DEC3 Temperatures(2)]
Hserout [DEC3 Temperatures(3)]

HSEROUT [dec3 normtemp[0]]
HSEROUT [dec3 normtemp[1]]
HSEROUT [dec3 normtemp[2]]
HSEROUT [dec3 normtemp[3]]

Hserout [dec3 alarmlow[0]]
Hserout [dec3 alarmlow[1]]
Hserout [dec3 alarmlow[2]]
Hserout [dec3 alarmlow[3]]

etc
etc
etc

No I want to replicate this using my mega2560 with the default serial port

I've found details in the wiki that allow me to replicate the PBP DEC3 command to send data to just one decimal place, for example, the PC application is expecting 123 for a temperature reading of 12.3, but I'm confused as to which command "Serial.event ??" to do the initial checking for a Q or an S in the buffer, and how to jump out of the main loop to a subroutine that will accept the stream or build it.

The serial stream is 132 digits long. So for example where the four temperature probes read 23.4, 23.7, 23.6 and 24.1 the start of the string will be "234237236241" It then continues in the same way for the other variables. When the PC application received the string, it then splits the data up into text boxes, and placed the decimal back in, so text box1 = 23.4, text box2 23.7 etc

Sorry for the long winded post, but I wanted to include as much information as possible in order to help you guys understand what I'm looking for

Thanks

Malcolm

Use Serial.available to detect of there are characters received. Use Serial.read once you know that a character is received. Both in loop(), forget that the serial event exists.

Do a search fir Robin2's updated Serial Input Basics thread to get a better understanding how to approach your problem.

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example

...R

In C++, subroutines are called functions.

Serial.event() is a waste of time. If you create a function with that name it will be called automatically at the end of loop() if serial data has been received since the last time loop() ended.

You might just as well simply use Serial.available() as suggested and act as required if serial data is available. If Serial.event() was interrupt driven then it might be more useful, albeit more complicated to use.

Robin,

I’ve read through the post linked in your previous post but still can’t seem to get my head round things

I’ve modded the code thus:

char receivedChar;
boolean newData = false;

byte setpoint1 = 30;    // intial set temperature of 30c
byte setpoint2 = 30;
byte setpoint3 = 30;
byte setpoint4 = 30;

void setup() {
    Serial.begin(9600);
}

void loop() {
    recvOneChar();
    testData();
//    showNewData();
    
}

void recvOneChar() {
    if (Serial.available() > 0) {
        receivedChar = Serial.read();
        newData = true;
    }
}

void testData() {
    if (receivedChar = "Q") {
    Serial.print(setpoint1);
    Serial.print(setpoint2);
    Serial.print(setpoint3);
    Serial.print(setpoint4);
 newData = false;



    }
}

I’ve tried

if (receivedChar = "Q")
if (receivedChar = 'Q')
if (receivedChar = Q)

But it still fails to work… in fact it doesn’t wait for an entry from the serial monitor to send garbage back to the PC.

This does work to a fashion

void testData() {
    if (newData == true) {
    Serial.print(setpoint1);
    Serial.print(setpoint2);
    Serial.print(setpoint3);
    Serial.print(setpoint4);
 newData = false;

in that nothing is echoed back to the PC until a character is sent to the arduino, which you would expect as I’ve just changed the line that normally sends back what was originally input. But that’s juts it, it will send data back regardless of what’s sent, or even if the return key is hit.

All I need is a way of checking for a Q or S, and ignore any other response - any ideas

Malcolm

Ok, managed to solve that... should have used the "==" - Doh !!

But I now have one further issue. I want to send the temperature to one decimal place. I changed the variable to a float rather than byte, and added the comma after the variable in the serial out command, but where as the PIC sends the value as a three digit number, the arduino code inserts the decimal place. So 23.4C would be sent as 234 in the original PIC code, the arduino sends it as 23.4

I could edit the PC application, but that would be a lot of work

Is there a way to send a float variable without the decimal place ?

Malcolm

Multiply your float by ten and store the result in an int or a long.

float f = 12.3;
int i = f * 10;  // now holds 123

Delta_G:
Multiply your float by ten and store the result in an int or a long.

float f = 12.3;

int i = f * 10;  // now holds 123

Thanks...

I found this works based on your suggestion of multiplying by 10

 Serial.print((temperature1*10),0);

If you're only after one decimal place then why use float in the first place? Work in tenths of units. The float variables slow down your code and introduce inaccuracy to your calculations.

Guys need some advice regarding a strange issue I'm having with the comms between the code running on my mega2560 and a PC application.

I have the following function that gets called in the main loop

void recvOneChar() {
    if (Serial.available() > 0) 
   delay(500);
{
        receivedChar = Serial.read();
        newData = true;
    }
}

Then this routine that checks to see if the Q has been received and if it has then send a string of digits made up from the values in the variables listed

void testData() {
    if (receivedChar == 'Q') {
    Serial.print((temperature1*10),0);
    Serial.print((temperature2*10),0);
    Serial.print((temperature3*10),0);
    Serial.print((temperature4*10),0);
    
    Serial.print((daytemp1*10),0);
    Serial.print((daytemp2*10),0);
    Serial.print((daytemp3*10),0);
    Serial.print((daytemp4*10),0);

    Serial.print((AlarmLOW1*10),0);
    Serial.print((AlarmLOW2*10),0);
    Serial.print((AlarmLOW3*10),0);
    Serial.print((AlarmLOW4*10),0);

    Serial.print((AlarmHIGH1*10),0);
    Serial.print((AlarmHIGH2*10),0);
    Serial.print((AlarmHIGH3*10),0);
    Serial.print((AlarmHIGH4*10),0);
    }
    else{   

 newData = false;


    }

The reason I want it to respond using Q is that the PC application was originally written to work with a PIC with code written in picBASIC pro which works fine, so I'm having to replicate the "protocol" in the Arduino code.

If I use any of the three serial monitors I have to hand, and send a Q the serial monitor responds and the values of the variables are returned. However if I use the PC application, often the data is not displayed in the text boxes. Basically the PC code opens the port and then the user click on a button which sends a Q and then places the incoming data into a string which is then split up to populate the text boxes. So its doing nothing more than sending a Q in the same way as a serial monitor.

However, its very hit and miss as to whether or not the boxes in the application are filled. So I used a com sniffer to confirm if the data was being received back, and discovered that no data was being received back on the occasions when the boxes were not populated. Repeated clicks on the update button on the PC application which sends Q would sporadically get a result, which would be the values repeated however many times the update button was used.

The PC application code that handles this is shown below

[QueryAll]
    'Retrives the Habitat data from the Controller
    print #main.htem7, " Querying... "
    q$= "Q."
    print #commHandle, q$;                   'send the Q

 '   call delay 1000          'Delay 500ms
    getinfo$ = ""                            'Clear the string buffer

    [readit]
        if lof(#commHandle) = 0 then          'if we lose contact
            return                            'don't update the fields
        end if                                'otherwise grab the whole buffer
        getinfo$ = input$(#commHandle, lof(#commHandle))  'Load the data Rx'd from the Controller to a string buffer
        print #main.htem7, " Retrieved "

    Return

I've tried various delays from 500ms, to 2 seconds which have no effects.

The original PicBASIC code that checks the buffer is shown below ( the pause 2 statement is pause 2 milliseconds)

FOR TempWD = 0 TO 1000
    IF RCIF=1 THEN GOTO coms                   ; Check to see id PC application connected    
    PAUSE 2
NEXT TempWD   

coms:

HSERIN [nTest]
    SELECT CASE nTest   
    CASE "Q"                    ; if Q then send data to PC
    Goto Term_TX 
    CASE "S"                    ; if S then receive data from PC
    goto Term_RX
    end select
Goto main

Term_TX:


Hserout [DEC3 Temperatures(0)]
Hserout [DEC3 Temperatures(1)]
Hserout [DEC3 Temperatures(2)]
Hserout [DEC3 Temperatures(3)]

etc etc

Hoping that someone might be able to come up with some suggestions to try and help me get to the bottom of this.

Thanks

Malcolm

void recvOneChar() 
{
  if (Serial.available() > 0) 
     delay(500);

  receivedChar = Serial.read();
  newData = true;
   
}

If there's something available to read, wait long enough for the serial input buffer to overflow many times, then read what was (or isn't anymore) there anyway.
If there was nothing to read, don't wait, but read it anyway (it will read -1)

You should always post the complete code: I suspect that you need to unconditionally clear newData but without seeing the complete code it's a guess.

There is also no need to modify Robin's receive functions.

Malc-C:
I have the following function that gets called in the main loop

void recvOneChar() {

if (Serial.available() > 0)
  delay(500);
{
       receivedChar = Serial.read();
       newData = true;
   }
}

That looks like somebody might have been inspired by one of the examples in Serial Input Basics but then thought it was pity that the code was working so he thought he would "improve" it.

I use the word "he" because I suspect a "she" would have been content to leave working code alone.

...R

Hey I came her for constructive advice not to have digs made at me...

I've only been messing about with this C++ stuff for a couple of weeks so bound to get thing wrong or mess things up - I wasn't trying to improve on things or tread on someone toes...

Maybe I should stick to PicBasic as they are more welcoming on their forum and actually try to help newbies debug their issue...

For what its worth... I solved the issue, and whether the C++ code is right or not - if it works that is all that matters to me...

To quote Douglass Adams "so long and thanks for all the fish !"

I solved the issue

I would be interested to see how you solved it.

I've only been messing about with this C++ stuff for a couple of weeks

Methodical changes, and observation of the results, is far better than "messing about".

That you ARE "messing about" IS the problem.