Serial Interface

Hi Folks,

I'm in the process of developing a cross plaform interface for the arduino using Runtime Revolution programming environment. Unfortunately I don't know C and this is limiting what I'm able to do on the Arduino side. At the moment I have coded to set all Analogue pins to output, and all digital to input. You will see from my code that I've done this in a very primitive way but it's all I know. The problem is that when I send to the Arduino it responds very inconsistently. I'm guessing that a lot of the data sent is getting lost because of my poor programming in C.

Any help or advice would be greatly appreciated.

Best

Jim H

#include <stdlib.h>
char buffer[4];
int received;
int analogValue = 0;
int analogValue1 = 1;
int analogValue2 = 2;
int analogValue3 = 3;
int analogValue4 = 4;
int analogValue5 = 5;
int outputPin2 = 2;
int outputPin4 = 4;
int outputPin7 = 7;
int outputPin8 = 8;
int outputPin12 = 12;
int outputPin13 = 13;
int pwmPin9 = 9;
int val;
void setup() {
  // open the serial port at 9600 bps:
  Serial.begin(9600);
      received = 0;
    buffer[received] = '\0';
    pinMode(outputPin2, OUTPUT);
    pinMode(outputPin4, OUTPUT);
    pinMode(outputPin7, OUTPUT);
    pinMode(outputPin8, OUTPUT);
    pinMode(pwmPin9, OUTPUT);
    pinMode(outputPin12, OUTPUT);
    pinMode(outputPin13, OUTPUT);
}
void loop() {
  // read the analog input on pin 0:
  analogValue = analogRead(0);
      Serial.print("@");
  Serial.println(analogValue);
    analogValue1 = analogRead(1);
      Serial.print("*");
  Serial.println(analogValue1);
      analogValue2 = analogRead(2);
      Serial.print("$");
  Serial.println(analogValue2);
        analogValue3 = analogRead(3);
      Serial.print("%");
  Serial.println(analogValue3);
        analogValue4 = analogRead(4);
      Serial.print("^");
  Serial.println(analogValue4);
        analogValue5 = analogRead(5);
      Serial.print("!");
  Serial.println(analogValue5);
    if (Serial.available()) {
       buffer[received++] = Serial.read();
        buffer[received] = '\0';
        if (received >= (sizeof(buffer)-1))
        {
int myInt = atoi(buffer); 
    analogWrite(pwmPin9, myInt);
            received = 0;
        }
    val = Serial.read();
    if (val == 'V') {
      digitalWrite(outputPin2, HIGH);
          }
          if (val == 'F') {
      digitalWrite(outputPin4, HIGH);
          }
          if (val == 'Y') {
      digitalWrite(outputPin7, HIGH);
          }
          if (val == 'H') {
      digitalWrite(outputPin8, HIGH);
          }
          if (val == 'W') {
      digitalWrite(outputPin12, HIGH);
          }
          if (val == 'E') {
      digitalWrite(outputPin13, HIGH);
          } 
          if (val == 'B') {
      digitalWrite(outputPin2, LOW);
          }
          if (val == 'G') {
      digitalWrite(outputPin4, LOW);
          }
          if (val == 'T') {
      digitalWrite(outputPin7, LOW);
          }
          if (val == 'L') {
      digitalWrite(outputPin8, LOW);
          }
          if (val == 'Q') {
      digitalWrite(outputPin12, LOW);
          }
          if (val == 'R') {
      digitalWrite(outputPin13, LOW);
    }
  }
}

What are you trying to do? There doesn't seem to be doing much but echoing the analog input and switching some bits. What's being inconsistant?

Hi,

Thanks for the reply. It's very simple really. All I need to do is read all the analog ins whilst also being able set all the digital outs (including 3 character calls 000 - 255 to the PWM pins) via the serial port. It seems as though there are too many processes going on though because when I send the letters "H", "L" etc to switch the digital pins, only a few ever respond at a time and it's very unpredictable.

Cheers

Jim H

before this line of code:

val = Serial.read();

you should check with SerialAvailable() first.

How fast are you sending data from the PC ?

Also you could simplify the long chain of if statements by using a switch statement, but that is probably not your problem, just cosmetics :slight_smile:

9600 baud. I've dropped in the if serialAvailable(); too but it's made no difference. I've also set the analog in pins to report only when I send a "P" so I can reduce the amount of traffic but it's still dropping commands.

I'm not sure what else to do.

Jim H

I've also used switchCase as suggested but I'm getting no response at all in the console??

    switch (val) {
    case 'V':
      digitalWrite(outputPin2, HIGH);
    case 'F':
      digitalWrite(outputPin4, HIGH);
    case 'Y':
      digitalWrite(outputPin7, HIGH);
    case 'H':
      digitalWrite(outputPin8, HIGH);
    case 'W':
      digitalWrite(outputPin12, HIGH);
    case 'E':
      digitalWrite(outputPin13, HIGH);
    case 'B':
      digitalWrite(outputPin2, LOW);
    case 'G':
      digitalWrite(outputPin4, LOW);
    case 'T':
      digitalWrite(outputPin7, LOW);
    case 'L':
      digitalWrite(outputPin8, LOW);
    case 'Q':
      digitalWrite(outputPin12, LOW);
    case 'R':
      digitalWrite(outputPin13, LOW);
      // break;
    }
  }

Cheers

Jim H

You have Val declared as an integer, and are using it in the switch statement as if it was a char. Maybee that's part of the problem ?

I don't quite get this code, especially not the way you're reading the serial port.

First you check if there's something in the receive buffer. If so, you read one byte, put in a buffer, and if this buffer is full you set analog pin 9 to the numeric value of the string in this buffer and then clear the buffer.

Then you read another byte and do stuff depending on the value of this byte. Then you loop.

I think you want this code to do the following:

If the read character is a command, act upon this command. If it's not a command, assume it's an ASCII character between '0' and '9' and add it to a buffer for further processing.

If so, then your code is nearly there, but you read two bytes from the receive buffer thus skipping every other byte both when filling the buffer and checking for commands. Processing commands will only work if you interleave every command with another byte, e.g. "xLxHxF" etc, but this will fill your buffer with junk.

If your code is supposed to do what I think, you should do something like this instead:

if (Serial.available())
{
  char c = Serial.read();

  if (c >= '0' or c <= '9')
  {
     // Fill and process buffer here
  }
  else // Not numeric, check for command here
  {
    switch (c)
    {
      case 'L':
        // Do stuff
        break;
      case 'F':
        // Do something else
        break;
    }
  }
}

Or perhaps I completely misunderstand the purpose of your code?

Ahhh, that makes a LOT of sense and yes that IS what I'm trying to do. I'm off to give it a try.

Thanks a million in the mean time.

Jim H (hopeful!)

Well this "feels" much better but now it's not working at all. I've probably put the curly braces in the wrong places or something but I can't work it out.

Any ideas?

#include <stdlib.h>
char buffer[4];
int received;
int analogValue = 0;
int analogValue1 = 1;
int analogValue2 = 2;
int analogValue3 = 3;
int analogValue4 = 4;
int analogValue5 = 5;
int outputPin2 = 2;
int outputPin4 = 4;
int outputPin7 = 7;
int outputPin8 = 8;
int outputPin12 = 12;
int outputPin13 = 13;
int pwmPin9 = 9;
char c;
int analogReadOn;
void setup() {
// open the serial port at 9600 bps:
Serial.begin(9600);
received = 0;
buffer[received] = '\0';
pinMode(outputPin2, OUTPUT);
pinMode(outputPin4, OUTPUT);
pinMode(outputPin7, OUTPUT);
pinMode(outputPin8, OUTPUT);
pinMode(pwmPin9, OUTPUT);
pinMode(outputPin12, OUTPUT);
pinMode(outputPin13, OUTPUT);
}
void loop() {
if (Serial.available())
{
char c = Serial.read();
if (c >= '0' or c <= '9')
{
buffer[received++] = Serial.read();
buffer[received] = '\0';
if (received >= (sizeof(buffer)-1))
{
int myInt = atoi(buffer);
analogWrite(pwmPin9, myInt);
received = 0;
}
}
else // Not numeric, check for command here
{
switch (c)
{
//respond to incomming commands:
case 'V':
digitalWrite(outputPin2, HIGH);
break;
case 'F':
digitalWrite(outputPin4, HIGH);
break;
case 'Y':
digitalWrite(outputPin7, HIGH);
break;
case 'H':
digitalWrite(outputPin8, HIGH);
break;
case 'W':
digitalWrite(outputPin12, HIGH);
break;
case 'E':
digitalWrite(outputPin13, HIGH);
break;
case 'B':
digitalWrite(outputPin2, LOW);
break;
case 'G':
digitalWrite(outputPin4, LOW);
break;
case 'T':
digitalWrite(outputPin7, LOW);
break;
case 'L':
digitalWrite(outputPin8, LOW);
break;
case 'Q':
digitalWrite(outputPin12, LOW);
break;
case 'R':
digitalWrite(outputPin13, LOW);
break;
case 'P':
// read and print the analog input on pins 0 -> 5:
analogValue = analogRead(0);
Serial.print("@");
Serial.println(analogValue);
analogValue1 = analogRead(1);
Serial.print("*");
Serial.println(analogValue1);
analogValue2 = analogRead(2);
Serial.print("$");
Serial.println(analogValue2);
analogValue3 = analogRead(3);
Serial.print("%");
Serial.println(analogValue3);
analogValue4 = analogRead(4);
Serial.print("^");
Serial.println(analogValue4);
analogValue5 = analogRead(5);
Serial.print("!");
Serial.println(analogValue5);
}
}
}
}

Thanks

Jim H

Well this "feels" much better but now it's not working at all. I've probably put the curly braces in the wrong places or something but I can't work it out.

You've done the same mistake again :slight_smile: Take a look at the piece of code that fills your buffer - you're calling Serial.read() again, thus reading two bytes for every loop.

buffer[received++] = Serial.read(); should be

buffer[received++] = c;

Then you'll read just one byte for every loop.

I could not resist restructuring your code a bit while looking for bugs. The following piece of code compiles without warnings on 0011, but I don't have an Arduino available here so I have not been able to test it. Please let me know if it works if you try it :slight_smile:

#define OUTPUTPIN2 2
#define OUTPUTPIN4 4
#define OUTPUTPIN7 7
#define OUTPUTPIN8 8
#define OUTPUTPIN12 12
#define OUTPUTPIN13 13
#define PWMPIN 9

void setup() {
 Serial.begin(9600);
 pinMode(OUTPUTPIN2, OUTPUT);
 pinMode(OUTPUTPIN4, OUTPUT);
 pinMode(OUTPUTPIN7, OUTPUT);
 pinMode(OUTPUTPIN8, OUTPUT);
 pinMode(OUTPUTPIN12, OUTPUT);
 pinMode(OUTPUTPIN13, OUTPUT);
 pinMode(PWMPIN, OUTPUT);
}

void loop()
{
  static char buffer[] = "000";
  static int received = 0;

  if (Serial.available())
  {
    char c = Serial.read();

    if (c >= '0' or c <= '9')
    {
      buffer[received++] = (char) Serial.read();
      if (received >= (sizeof(buffer)-1))
      {
        analogWrite(PWMPIN, atoi(buffer));
        received = 0;
      }
    }
    else // Not numeric, check for command here
      do_command(c);
  }
}

//respond to incomming commands:
void do_command(char c)
{
  switch (c)
  {
    case 'V':
      digitalWrite(OUTPUTPIN2, HIGH);
      break;
    case 'F':
      digitalWrite(OUTPUTPIN4, HIGH);
      break;
    case 'Y':
      digitalWrite(OUTPUTPIN7, HIGH);
      break;
    case 'H':
      digitalWrite(OUTPUTPIN8, HIGH);
      break;
    case 'W':
      digitalWrite(OUTPUTPIN12, HIGH);
      break;
    case 'E':
      digitalWrite(OUTPUTPIN13, HIGH);
      break;
    case 'B':
      digitalWrite(OUTPUTPIN2, LOW);
      break;
    case 'G':
      digitalWrite(OUTPUTPIN4, LOW);
      break;
    case 'T':
      digitalWrite(OUTPUTPIN7, LOW);
      break;
    case 'L':
      digitalWrite(OUTPUTPIN8, LOW);
      break;
    case 'Q':
      digitalWrite(OUTPUTPIN12, LOW);
      break;
    case 'R':
      digitalWrite(OUTPUTPIN13, LOW);
      break;
    case 'P':
    {
      // read and print the analog input on pins 0 -> 5:
      int i;
      char b[] = "@*$%^!";
      
      for (i = 0; b[i] != '\0'; i++)
      {
        Serial.print(b[i]);
        Serial.println(analogRead(i));
      }
    }
  }
}

That beautifully simplified version compiles fine but unfortunately I get no response using it at all. I'm having a little more success with stripping out the surplus "Serial.Read" from the older version, as you suggested, but I'm still not getting faultless responses to commands sent from the console. It does follow a pattern though - if I send a string of 6 letters the 3rd Pin and the 6th Pin don't respond so presumably the third character is being swallowed up somewhere?

Any thoughts?

Cheers

Jim H

That beautifully simplified version compiles fine but unfortunately I get no response using it at all.

Well, that's hardly surprising as I did the same mistake as you did ;D I used Serial.read() twice in the loop. Try this fixed version. I've also added a lot of debug statements so you can see where it goes wrong.

#define OUTPUTPIN2 2
#define OUTPUTPIN4 4
#define OUTPUTPIN7 7
#define OUTPUTPIN8 8
#define OUTPUTPIN12 12
#define OUTPUTPIN13 13
#define PWMPIN 9

void setup() {
 Serial.begin(9600);
 pinMode(OUTPUTPIN2, OUTPUT);
 pinMode(OUTPUTPIN4, OUTPUT);
 pinMode(OUTPUTPIN7, OUTPUT);
 pinMode(OUTPUTPIN8, OUTPUT);
 pinMode(OUTPUTPIN12, OUTPUT);
 pinMode(OUTPUTPIN13, OUTPUT);
 pinMode(PWMPIN, OUTPUT);
}

void loop()
{
  static char buffer[] = "000";
  static int received = 0;

  if (Serial.available())
  {
    char c = Serial.read();
Serial.print("Received ");
Serial.println(c);
    if (c >= '0' or c <= '9')
    {
Serial.print(c);
Serial.println(" is a numeric character");
      buffer[received++] = c;
      if (buffer[received] == '\0')
      {
Serial.print("Buffer full, value: ");
Serial.println(buffer);
        analogWrite(PWMPIN, atoi(buffer));
        received = 0;
      }
    }
    else // Not numeric, check for command here
      do_command(c);
  }
}

//respond to incomming commands:
void do_command(char c)
{
Serial.print("Do command ");
Serial.println(c);
  switch (c)
  {
    case 'V':
      digitalWrite(OUTPUTPIN2, HIGH);
      break;
    case 'F':
      digitalWrite(OUTPUTPIN4, HIGH);
      break;
    case 'Y':
      digitalWrite(OUTPUTPIN7, HIGH);
      break;
    case 'H':
      digitalWrite(OUTPUTPIN8, HIGH);
      break;
    case 'W':
      digitalWrite(OUTPUTPIN12, HIGH);
      break;
    case 'E':
      digitalWrite(OUTPUTPIN13, HIGH);
      break;
    case 'B':
      digitalWrite(OUTPUTPIN2, LOW);
      break;
    case 'G':
      digitalWrite(OUTPUTPIN4, LOW);
      break;
    case 'T':
      digitalWrite(OUTPUTPIN7, LOW);
      break;
    case 'L':
      digitalWrite(OUTPUTPIN8, LOW);
      break;
    case 'Q':
      digitalWrite(OUTPUTPIN12, LOW);
      break;
    case 'R':
      digitalWrite(OUTPUTPIN13, LOW);
      break;
    case 'P':
    {
      // read and print the analog input on pins 0 -> 5:
      int i;
      char b[] = "@*$%^!";
      
      for (i = 0; b[i] != '\0'; i++)
      {
        Serial.print(b[i]);
        Serial.println(analogRead(i));
      }
      break;
    }
    default:
      Serial.print("Invalid command ");
      Serial.println(c);
      break;
  }
}

Debugging statements - great! Now the problem becomes much clearer:

A
Received value is A is a numeric character
B
Received value is B is a numeric character
1
Received value is 1 is a numeric character
Buffer full, value: AB1

The characters are being treated as numbers and are being put into the buffer even when they were created as "char"????

The characters are being treated as numbers and are being put into the buffer even when they were created as "char"????

Yes, they are, and the reason is quite obvious. I've made a "small" mistake - "if (c >= '0' or c <= '9')" is always true. Replace "or" with "and" and I think the code should work.

I'm quite good at making silly little mistakes like that.

"Silly" mistakes or no, it works perfectly now!!

For a while there I thought it might just not be possible to have so many messages going back and forth without dropping data - you proved me well and truely wrong.

Thanks so much for you're expert help - I really appreciate it.

Jim H

Hi, Dont want to hijack your thread but dont think this is worth a new one! im using some of this code (Thanks!!!) but cant get it to work. i've taken the section that takes the numerical input and does something with it, but as soon as i press a key it moves out of the function. can anyone see what ive done wrong?

thanks

int getNumber() 
{
  static char buffer[] = "000";
  static int received = 0;
  while(!Serial.available());
  if (Serial.available())
  {
    char c = Serial.read();
    //Serial.println(c);
    while (received < 4) {
      if (c >= '0' or c <= '9')
      {
        Serial.print(c);
        buffer[received++] = c;
        if (buffer[received] == '\0')
        {
          Serial.println("");
          Serial.print("Total slots set to: ");
          Serial.println(buffer);
          return atoi(buffer);
          received = 0;
        }
      }
    }
  }
}

im using some of this code (Thanks!!!) but cant get it to work. i've taken the section that takes the numerical input and does something with it, but as soon as i press a key it moves out of the function. can anyone see what ive done wrong?

If I understand your code correctly, the intention of this function is to retrieve three digits from the serial port, and then return the numeric value of the string. Is this correct?

If so, there's several issues with your code.

int getNumber()
{
  static char buffer[] = "000";
  static int received = 0;

First of all, you don't need to make these static as you don't need to keep the contents of these variables after the function exits. The keyword "static" in C has two different meanings, depending on where you use it. "static" function- or variable-declarations outside a function will hide the function or variable to all code outside the file they appear in. "static" variables inside a function are only initialized the first time the function is called, and they keep their values after the function has exited.

  while(!Serial.available());

Here you listen for an incoming byte on the serial port. That's OK, but I don't think it's the best approach in this function.

  if (Serial.available())
  {
    char c = Serial.read();
    //Serial.println(c);
    while (received < 4) {

You read three bytes, regardless of what they are. So if you receive non-numeric values, your function will fail. You will receive fewer digits than you expect. Also, further down in the code you also check that you've reached the end of the buffer, so one of these checks is redundant. But, as we'll see further down this doesn't matter because you always read no more than one byte.

      if (c >= '0' or c <= '9')

I don't blame you for this one, as I'm the one who wrote this line :wink: This statement will always be true - any value will be equal or larger than '0' OR equal or less than '9'. Replace 'or' with 'and', and it will work as intended.

      {
        Serial.print(c);
        buffer[received++] = c;
        if (buffer[received] == '\0')

buffer[4] is '\0', but why do you check this here? You've already limited the loop to four characters in the while-statement.

        {
          Serial.println("");
          Serial.print("Total slots set to: ");
          Serial.println(buffer);
          return atoi(buffer);
          received = 0;

You return from the function before you reset "received". Since "received" is static, it will contain the value 4 from now on, and your function won't work.

Also, you only read one character from the serial port in this function. So if you send "123" to the Ardunio, the string will read "111" because you only read from serial once. You must read from the serial port in the loop to retreive new bytes.

I've rewritten this function to do what I think you want it to do: Wait until three digits has been received, and then return the numeric value of the string.

int getNumber(void)
{
  int i = 0;
  char s[] = "000"; // Three character buffer. C-strings are NULL-terminated,
                    // so the array will actually contain four characters, where
                    // the fourth is zero and indicates the end of the string. This
                    // is what we look for to determine when we've reached the end
                    // of the string
  
  while (s[i] != '\0') // Repeat until you've reached the end of the buffer
  {
    char c = Serial.read(); // Read one character.
    
    if (c >= '0' and c <= '9') // If it's between 0 and 9, add to buffer.
      s[i++] = c;
  }
  
  return atoi(s);  // Buffer is full, return its numeric value.
}

As you see I don't bother with Serial.available() in this function. It is not needed, because Serial.read() will return -1 if there's nothing available. And since -1 won't be put in the buffer anyway, it doesn't matter. Serial.available() is useful in many other circumstances though.

I hope this was useful. Don't hesitate to ask if you run into to problems. The only way to learn how to code is to make a lot of mistakes and then figure out what the hell went wrong :smiley:

Stuck again trying to create a method to differentiate the incoming commands for different PWM pins:

void loop()
{
static char buffer[] = "000";
static int received = 0;
if (Serial.available())
{
char c = Serial.read();
// if (incomming is an identifier character)
if (c == '$')
{
Serial.print(c);
Serial.println(" is our identifier character");
char d = Serial.read();
buffer[received++] = d;
char e = Serial.read();
buffer[received++] = e;
char f = Serial.read();
buffer[received++] = f;
if (buffer[received] == '\0')
{
Serial.print("Buffer full, value: ");
Serial.println(buffer);
analogWrite(PWMPIN3, atoi(buffer));
received = 0;
}
else if (c == '@')
{
Serial.print(c);
Serial.println(" is our identifier character");
char d = Serial.read();
buffer[received++] = d;
char e = Serial.read();
buffer[received++] = e;
char f = Serial.read();
buffer[received++] = f;
if (buffer[received] == '\0')
{
Serial.print("Buffer full, value: ");
Serial.println(buffer);
analogWrite(PWMPIN5, atoi(buffer));
received = 0;
}
}
}
else // Not a PWM command, check for further input commands
do_command(c);
Serial.print("c is still ");
Serial.println(c);
}
}

Any thoughts?

Many Thanks again and a Very Merry Xmas!

Jim H