3

I'm running a shell script on a raspberry pi to communicate with a arduino. Every hour my ram gets used up by about 50 megabytes more.

The script itself just connects to ttyACM0 (the arduino's usb terminal) and then sends a char to it. The script runs once per minute to check the Temps. Values for the first argument can be: 'a', 'b' or 'T'. In the case of 'a' or 'b' it switches on/off a relay. In the case of 'T' the arduino returns 3 temperature values, which I store.

The script runs fine (I can control the relay and receive values, without the arduinos serial getting restarted). but I'm having issues because of my limited memory.

I don't have any other processes running on the device and already tried a fresh raspbian setup.

Can somebody tell me where and if there are memory leaks in this script and how I can prevent them?

EDIT: I found the culprit: the script runs hundreds of 'cat' commands with time, how do I get rid of them? I already tried killall cat, but this resets my raspberrys serial connection (I want to prohibit that, because it 'restarts' the arduino!) @mikeserv pointed me towards using head, which automatically quits after reading a defined number of lines, unfortunately that doesn't work either. And I can't seem to get the temperatures written in my output file without a pipe

head -n3 <&3 >>/home/pi/output

doesn't work as it's not exiting too and I don't get any output

I could kill all cat's every few minutes to free the mem, but this also resets my tty to arduino (so it reboots and loses the state for the relay)

Edit2: I did not manage to get this working (I tried several possibilities including minicom, screen etc...) but the receiving part is where I have trouble. Sending chars to the arduino works fine though!

#!/bin/bash

# READ / WRITE ARDUINO

exec 3<> /dev/ttyACM0

echo "connected, sleep for 1 sec..."
sleep 1

echo "send $1..."

echo "$1" >&3

if [ "$1" = "T" ]
then
        cat <&3 | cat >> /home/pi/output &2>1
else
        echo "nothing to save"
fi

echo "closing.."

exec 3>&-

exit 0

closed as off-topic by Braiam, Anthon, jasonwryan, Ramesh, Thomas Nyman Sep 28 '14 at 6:44

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions describing a problem that can't be reproduced and seemingly went away on its own (or went away when a typo was fixed) are off-topic as they are unlikely to help future readers." – Braiam, Anthon, jasonwryan, Ramesh, Thomas Nyman
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 2
    cat <&3 | cat >> /home/pi/output &2>1 results in backgrounded cat commands... – yeti Sep 27 '14 at 19:26
  • without the &2>1 it waits forever at this line, what else can I use? – Gotschi Sep 27 '14 at 19:34
  • why not cat <&3 >>/home/pi/output? this is also portable - so why not use a shell much less likely to hog resources - maybe #!/bin/dash? – mikeserv Sep 27 '14 at 19:36
  • 2
    Then when is $1 a 1? I think I might understand. The cat </dev/ttyACM0 |cat >file just hangs reading the tty - it never gets an EOF on </dev/ttyACM0 because when you close the descriptor you do so in the current shell not in cat's subshell. So you should be using instead head -n$GUARANTEED_NUMBER_OF_AVAILABLE_LINES or sed $LINESq or something - something that will explicitly quit input. – mikeserv Sep 27 '14 at 20:03
  • 2
    I don't understand what you're trying to do, but note that cat >> /home/pi/output &2>1 runs cat >> /home/pi/output, and runs the null command with the redirection 2>1 (i.e. standard error redirected to the file called 1), which creates an empty file called 1. Did you mean 2>&1, or 2>&1 &? – Gilles Sep 27 '14 at 23:40
3

When you:

exec 3<> /dev/ttyACM0

... you open a read/write file-descriptor to the usb serial tty on 3 which is automatically inherited by children - such as cloned subshells - and this is why you can read it with cat <&3 later in your backgrounded pipeline.

The thing is, though, because you background the pipeline - which puts it and all associated subshells in a separate process group - it is not closed for the pipeline processes later when you...

exec 3<&-

...and close that descriptor for the script's current shell processes. Instead...

cat </dev/ttyACM0 | cat >> file &

...hangs in the background, reading nothing for as long as there is nothing to read, and keeping an open line on that tty the whole time. This is because cat only quits its input on EOF, which, in this case, it never receives.

You can do:

cat /dev/tty

... at your prompt to approximate that behavior.

What you should be doing is explicitly quitting the input as soon as you have read as much as you need, or else explicitly killing cat after same. You might do this like:

head -n"$GUARANTEED_NUM_AVAIL_INPUT LINES" <&3 >>file

... or with sed [num]q or similar. In this way you should avoid having to |pipe at all, and likely can omit the &backgrounding entirely. Else to kill cat you might do:

cat <&3 >>file &
sleep 1 && kill "$!"

...but probably that is not necessary, as quitting the input is far more simple.

As an aside, it is worth noting that your script is already composed entirely of portable syntax, and so it is probably worth your while to alter the #!/bin/bash line and instead invoke a lighter-weight - and likely faster - shell instead. I recommend dash for something like this. Have a look here if interested in this line of thinking for a q&a on performance comparisons between various shells - to include both bash and dash.

  • 1
    This verifies my buffer overflow theory. +1 – eyoung100 Sep 27 '14 at 21:11
  • unfortunately its not solved, the head commands sum up too! and I can't seem to get anything written in my output without the pipe :/ – Gotschi Sep 27 '14 at 21:22
  • @Gotschi, Well, the pipe is a problem because it will buffer at 4096k when run without job control. Possibly you can use read to do what you need - like { IFS= read -r line1; IFS= read -r line2; IFS= read -r line3; printf %s\\n "$line1" "$line2" "$line3" } <&3 >>file, but there is also the usb buffer to consider. Interesting is that it works as is for one device and not for the other. How does it work if you exec 3</dev/ttyACM0 4>/dev/ttyACM0 and use echo >&4 "$1" ... head -n3 3<& or else get head to open the dev explicitly like head -n3 /dev/ttyACM0 >> file? – mikeserv Sep 27 '14 at 21:30
  • @Gotschi - Or does perhaps the arduino need the serial line closed before it will send the temps? For example, before you were doing open tty; cat tty &; close tty which would allow cat to receive after you close the tty. Actually, are you using any sort of terminal program here to create the tty in the first place minicom, screen, anything? Or is this the only open that happens at all? – mikeserv Sep 27 '14 at 21:42
  • Im just using the normal tty (I guess). Im not so sure what I'm doing at all and whats going wrong, but I made this so that the serial port on the arduino does not get restarted every time I query for the temps. Out of the reason that arduino does a restart (and loses the relay state) on a new serial connect. I used screen for initial testing, which just displays 3 lines with numbers as soon as i hit 'T' – Gotschi Sep 27 '14 at 21:47
2

I can't write you a fix, but I can explain what's happening.

  1. You're opening the Port.
  2. Your checking/sending values from the temperature sensor.
  3. You're closing the port.

Some possibilities:

  • The USB Buffer from the Arduino is Filling until you rerun the script, but the buffer is still full from the last run. You'r inadvertently causing a buffer overflow in your Comm Port, as the amounts of previous runs pile up in the buffer.
  • The same issue is occurring in the Raspberry Pi, as the buffer fills in the same way from the other direction.

You need to find a way to clear the buffer on both sides so that the gate opens, sends 3, and closes, opens, sends 3, and closes etc. Subshelling, as Yeti suggests is one way. I'm sure there are other, such as closing and reopening the buffer but not killing the CommPort. Since I'm no expert on Comm Drivers, I can only offer a theory.

Update

If [ "$1" = "T" ]
then
        cat <&3 | cat >> /home/pi/output &2>1
        cat <&3 | cat >> /dev/null
else
        echo "nothing to save"
        cat <&3 | cat >> /dev/null
fi
  • thank you for this information! but my problem belongs more to the pi as the arduino shows no signs of trouble. – Gotschi Sep 27 '14 at 19:47
  • See my comment in your OP... – eyoung100 Sep 27 '14 at 19:57
  • although your update seemed promising, I had no success :/ the cat processes were still not quitting. using head -n3 works - thanks to both of you! – Gotschi Sep 27 '14 at 20:32
  • When all else Fails count the characters in the buffer... oldest trick in the book :) Lost another checkmark to mikeserv lol – eyoung100 Sep 27 '14 at 20:41
  • 1
    @mikeserv Neither do I but I love the challenge :) – eyoung100 Sep 27 '14 at 20:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.