Two way serial communication with raspberry Pi (radio project)

Hi everybody,

I installed "music player deamon" (mpd, mpc) on my new rasberry pi. I created a playlist that contains the urls of some internet radio stations. I "tune" the stations by turning a potentiometer's knob, connected to my Arduino.

This is done using the "map" function: the pot's range is divided by the number of stations which are in my playlist. (The number of stations is initially set in variable "maxNumb". So the pot delivers values between 1 and 26)

The station number that mpc has to play is transfered via serial communication; on raspberry this value is read by a bash script that controls mpc. That works fine and stable:

int sensorValue = analogRead(A0);
    delay(100);
    int upperValue = sensorValue + 2;
    int lowerValue = sensorValue - 2;
  } while (sensorValue < upperValue && sensorValue > lowerValue);
  int tunerValue = map(sensorValue, 0, 1023, 1, maxNumb);
  
  if (tunerValue < 10)
  {
    Serial.print(0);
  }
  
  Serial.println(tunerValue,DEC);

Now to my problem:

If I add more stations I want to automatically change the value of variable "maxNumb", so that all stations can be addressed using the potentiometer.

Rasperry sends the number of stations that are on the playlist to Arduino via the serial interface:

echo "$NUMBEROFSTATIONS" > /dev/ttyACM0

The transfered number is between 20 and 99. If arduino receives this new value, variable "maxNumb" should change and the new stations should be selectable with the pod.

I try to receive this value with the following code (works well on arduino's serial monitor, but not together with raspberry):

if (Serial.available())
{
  char ch = Serial.read();
    if(index <  MaxChars && ch >= '0' && ch <= '9'){
      strValue[index++] = ch; // add the ASCII character to the string;
    }
    else
    {
      // here when buffer full or on the first non digit
      strValue[index] = 0;        // terminate the string with a 0
      maxNumb = atoi(strValue);  // use atoi to convert the string to an int
      index = 0;
    }
  }

Any ideas, why this works on the serial console but not when the data comes from the computer?

Schwabinger

Is the computer sending a terminating character after the numbers in the string?

Hi,

I tried the following to transmit the value "40" with no success:

echo 40 > /dev/ttyACM0
echo "40" > /dev/ttyACM0
echo 40\n > /dev/ttyACM0
echo "40\n" > /dev/ttyACM0
echo 40\n > /dev/ttyACM0
echo "40\n" > /dev/ttyACM0

Kind regards,

Schwabinger

So what do you recieve from the Pi? Print out the ASCII of what you recieve.

I turn the pot all the way to the right side ... so after sending the value 40, variable maxNumb should get that value and arduino should send this value back to the computer - but this does not happen:

root@raspberrypi:/home/pi# cat /dev/ttyACM0
26

26

26

26

26 is the value that was initially set to the varable "maxNumb" in my sketch.

Here is the code of whole sketch:

/*
 * 
 * Siehe Arduino Cookbook: safari books onlin
 * Uebertraegt Sendernummer zum Radio
 */
const int MaxChars = 2;
char strValue[MaxChars+1];
int index = 0;
int maxNumb = 26 ;
int value;
int upperValue;
int lowerValue;
char ch;

void setup()                    // run once, when the sketch starts
{
  Serial.begin(9600);           // set up Serial library at 9600 bps
}

void loop()                       // run over and over again
{
  int sensorValue = analogRead(A0);

do
  {  
int sensorValue = analogRead(A0);
    delay(100);
    int upperValue = sensorValue + 2;
    int lowerValue = sensorValue - 2;
  } while (sensorValue < upperValue && sensorValue > lowerValue);
  int tunerValue = map(sensorValue, 0, 1023, 1, maxNumb);
  
  if (tunerValue < 10)
  {
    Serial.print(0);
  }
  
  Serial.println(tunerValue,DEC);

delay(250);

if (Serial.available())
{
  char ch = Serial.read();
    if(index <  MaxChars && ch >= '0' && ch <= '9'){
      strValue[index++] = ch; // add the ASCII character to the string;
    }
    else
    {
      // here when buffer full or on the first non digit
      strValue[index] = 0;        // terminate the string with a 0
      maxNumb = atoi(strValue);  // use atoi to convert the string to an int
      index = 0;
    }
  }

}

Very bad code that, see the comments for this first fragment:-

void loop()                       // run over and over again
{
  int sensorValue = analogRead(A0); // this value is never used so why take it?

do
  {  
int sensorValue = analogRead(A0);  // this creates a new and different instance of the variable each time it is run.
                                                    // this means that you are filling your stack with useless variables which will eventually crash the system
    delay(100); // why? it is doing nothing for anything

Tried to change as advised - problem still exists ...:

/*
 * 
 * Siehe Arduino Cookbook: safari books online
 * Sends station numbers to Raspberri: ok
 * Receives max Numb of stations from Raspberry: nok
 */
const int MaxChars = 2;
char strValue[MaxChars+1];
int index = 0;
int maxNumb = 26 ;
int value;
int upperValue;
int lowerValue;
char ch;
int poti = 0;
int sensorValue;


void setup()                    // run once, when the sketch starts
{
  Serial.begin(9600);           // set up Serial library at 9600 bps

}

void loop()                       // run over and over again
{
 
do
  {  
sensorValue = analogRead(poti);
    int upperValue = sensorValue + 2;
    int lowerValue = sensorValue - 2;
  } while (sensorValue < upperValue && sensorValue > lowerValue);
  int tunerValue = map(sensorValue, 0, 1023, 1, maxNumb);
  if (tunerValue < 10)
  {
    Serial.print(0);
  }
  
  Serial.println(tunerValue,DEC);

delay(250);

if (Serial.available())
{
  char ch = Serial.read();
    if(index <  MaxChars && ch >= '0' && ch <= '9'){
      strValue[index++] = ch; // add the ASCII character to the string;
    }
    else
    {
      // here when buffer full or on the first non digit
      strValue[index] = 0;        // terminate the string with a 0
      maxNumb = atoi(strValue);  // use atoi to convert the string to an int
      index = 0;
    }
  }

}

Tried to change as advised - problem still exists ...:

Of course it will, we have not got over the silly mistakes you are making. You have to do that first before we can begin to sort out your logic.

PLEASE PLEASE PLEASE STOP creating variables inside loops.

I wonder if the bootloader is eating your data.

Immediately after the serial port is opened, the Arduino spends about 1500ms in the bootloader waiting to see if you are about to upload a new sketch.

During this time, any serial output sent from the PC side is interpreted by the bootloader, not your program. Mostly, it is ignored. Just like what you are seeing.

The unix 'echo' command opens the port and writes to it right away. This works fine on the serial monitor, but because of the bootloader problem it won't work on arduino.

If this reasoning is sound, you need a little program to open the serial port and wait a couple seconds before sending the playlist length, rather than using 'echo'.

-br

Edit: Another way, besides waiting a fixed time, is to wait for a character string from the arduino to signal 'ready'. Bitlash, for example, sends '\n> ' as a prompt when it's ready. You can see this at work in the bloader.js program, which uploads files to Arduino over the serial port:

The unix 'echo' command opens the port and writes to it right away.

If the arduino is connected through the serial GPIO pins this is not important.
However, if it is connected through the USB port this will cause the arduino to reset on opening AND on closing.
However you have to eliminate the rubbish arduino code first.

int upperValue;
int lowerValue;
char ch;
int poti = 0;
int sensorValue;

Some nice global variables...

do
  {  
sensorValue = analogRead(poti);
    int upperValue = sensorValue + 2;
    int lowerValue = sensorValue - 2;

One global variable and two new local variables...

  } while (sensorValue < upperValue && sensorValue > lowerValue);

These names refer to the GLOBAL variables.

OP: You REALLY need to learn about scope.

Thanks to everyone who is helping here ..:slight_smile:

Ardunio and computer are connected via USB.

Tried to take all variables out of the loop. Resulted in code that compiles ... but if I run it in the serial console nothing happens any more. The WHILE condition will never be true, so I will remove that.

*
 * 
 * Siehe Arduino Cookbook: safari books online
 * Sends station numbers to Raspberri: ok
 * Receives max Numb of stations from Raspberry: nok
 */
const int MaxChars = 2;
char strValue[MaxChars+1];
int index = 0;
int maxNumb = 26;
int upperValue;
int lowerValue;
char ch;
int poti = 0;
int sensorValue = 1;
int tunerValue = 1;


void setup()                    // run once, when the sketch starts
{
  Serial.begin(9600);           // set up Serial library at 9600 bps
}

void loop()                       // run over and over again
{
 
do
  {  
sensorValue = analogRead(poti);
    upperValue = sensorValue + 2;
    lowerValue = sensorValue - 2;
  } while (sensorValue < upperValue && sensorValue > lowerValue);
  tunerValue = map(sensorValue, 0, 1023, 1, maxNumb);
  if (tunerValue < 10)
  {
    Serial.print(0);
  }
  
  Serial.println(tunerValue,DEC);

delay(250);

if (Serial.available())
{
  char ch = Serial.read();
    if(index <  MaxChars && ch >= '0' && ch <= '9'){
      strValue[index++] = ch; // add the ASCII character to the string;
    }
    else
    {
      // here when buffer full or on the first non digit
      strValue[index] = 0;        // terminate the string with a 0
      maxNumb = atoi(strValue);  // use atoi to convert the string to an int
      index = 0;
    }
  }

}

Tried to take all variables out of the loop. Resulted in code that compiles ... but if I run it in the serial console nothing happens any more. The WHILE condition will never be true, so I will remove that.

All that you needed to remove was the int keyword that defined a new variable. Without the int keyword, the name refers to the existing, global, instance.

I removed some lines that are not used at the moment anyway:

/*
 * 
 * Siehe Arduino Cookbook: safari books online
 * Sends station numbers to Raspberri: ok
 * Receives max Numb of stations from Raspberry: nok
 */
const int MaxChars = 2;
char strValue[MaxChars+1];
int index = 0;
int maxNumb = 26;
int upperValue;
int lowerValue;
char ch;
int poti = 0;
int sensorValue = 10;
int tunerValue = 1;

void setup()                    // run once, when the sketch starts
{
  Serial.begin(9600);           // set up Serial library at 9600 bps
}

void loop()                       // run over and over again
{
 sensorValue = analogRead(poti);

 tunerValue = map(sensorValue, 0, 1023, 1, maxNumb);
  
  Serial.println(tunerValue,DEC);

delay(250);

if (Serial.available())
{
  char ch = Serial.read();
    if(index <  MaxChars && ch >= '0' && ch <= '9'){
      strValue[index++] = ch; // add the ASCII character to the string;
    }
    else
    {
      // here when buffer full or on the first non digit
      strValue[index] = 0;        // terminate the string with a 0
      maxNumb = atoi(strValue);  // use atoi to convert the string to an int
      index = 0;
    }
  }

}

At least at the built in serial console it does what I want it to do:

  • Starting with an initial value for the variable "maxNumb" (26) depending on the poti's position a number between 1 and 26 ist transfered via serial;
  • If I enter a different value in the console, maxNumb is updated; if for instance I enter 50, by turning the knob I can produce numbers between 1 and 50:

But I still have the problem, that I cannot use "echo" from my computer to fill this variable.

But I still have the problem, that I cannot use "echo" from my computer to fill this variable.

Why not? There is no debug stuff in the code that reads from the serial port. If you are using the Serial Monitor to see the Arduino output, try typing the value in the text field and hitting the send icon. If you are using some other method to see the serial output, describe that method.

Here is my scenario:

Arduino is connected to my Windows Desktop PC. This is where I write the Arduino code.
If I use the Serial Monitor and run the sketch, everything works fine. In the serial monitor I can fill variable "maxNumb" and the arduino sketch does what I want.

When I connect arduino to my Raspberry pi computer (the target system for my Internet radio):

  • Raspberry can receive tuner values between 1 and 26 (according to the pot position)
  • I cannot use "echo 40 > /dev/ttyACM0" to change the value of variable "maxNumb".

So the serial communication works from arduino to pi, but not the other way :frowning:

So the serial communication works from arduino to pi, but not the other way :frowning:

In the if(Serial.available() > 0) block, blink the onboard LED a few times. This will tell you whether anything is getting from the pi to the Arduino. If something, garbled or not, gets through, you know one thing. If nothing gets through, you know something else.

How are you, on the pi, receiving the serial data from the Arduino?

This is how I read on the pi what arduino is sending:

#!/bin/bash
set -x
ALTWERT=1
while true
do
WERT=$(head -n 1 < /dev/ttyACM0)
WERT=$(echo  $WERT | sed "s:\r::")
NUMBSTATIONS=$(mpc playlist | wc -l)

if [ $ALTWERT -ne $WERT ]; then
mpc play "$WERT"
ALTWERT=$WERT
#sleep 1
fi

#echo  "$NUMBSTATIONS\n"  > /dev/ttyACM0
done

Variable WERT is filled with what is coming from arduino. So by turning the pot I can "tune" through my playlist.

Variable NUMBSTATIONS hold the number of radio stations in my playlist. This is the value that I want to echo to the pot.

Echo is commented # in the code above;

But as I leared in this thread in the last two hours "echo" does not work in this case.

Perhaps you need to use cat to send the value to the serial port, rather than echo. I'd be happy to test it for you, if you send me the pi.

Just come over to Europe ... the beer is on me :slight_smile:

I found a c Program "ardunio-serial.c"

pi@raspberrypi ~/scripts $ ./arduino-serial -b 9600 -p /dev/ttyACM0 -d 1600 -s 30 -r
read: 30

With that I can transfer a value like "30" in this example from the raspberry to arduino.
Unfortunately my script does not catch that value.

Any idea, why?