I'm building a remote vehicle with the Raspberry Pi. I've got everything working except a few little things. I'm trying to implement some CGI scripts in my server in order to control the car from an URL.

The C program which controls the motors works fine as I have tested it from the command line. The problem comes when I try to call it from the CGI file, it executes everything inside the program except the commands that move the motors.

Thi is my CGI file:


echo -e "Content-type: text/html\n\n"
echo "<h1>Halo Alex</h1>"

And my C file:

#include <wiringSerial.h>
#include <unistd.h>
#include <stdio.h>

#define MOVE_BACKWARD 0xD9
#define SPEED        0x7F

int main(void){

   int handle;
   handle = serialOpen ("/dev/ttyAMA0", 19200);

   serialPutchar (handle, MOVE_BACKWARD ) ;
   serialPutchar (handle, SPEED ) ;
   serialPutchar (handle, SPEED ) ;

       printf("Hello Alex");
   return 1;

So when I go to the URL I can see:

"Hello Alex"
"Halo Alex"

But the other commands are not executed and I don't know why. Do you have any idea?

  • try running your code with the same user as your webserver runs
    – Gotschi
    Dec 12, 2013 at 11:48

2 Answers 2


the other commands are not executed I don't know why. Do you have any idea?

My first guess would be permissions, because if you are using apache, by default it starts with privileges so it can open a low numbered (< 1024) port (HTTP is normally 80), then it calls setuid() to run as an unprivileged user (and/or it forks worker processes with an unprivileged UID that handle incoming requests). The apache user is often 48 (www or apache -- but see below), although all this can be configured. On raspbian it may be 33, www-data. You'll see how to find out if you keep reading.

So that's my #1 guess...

I don't know why.

Partially because you have not checked. I haven't used wiringPi, but I presume this call:

handle = serialOpen ("/dev/ttyAMA0", 19200);

Is not guaranteed to succeed. If a result < 0 indicates failure and the wiringPi API makes use of errno (it probably does if this call wraps a normal open()):

#include <errno.h>;
#include <string.h>;  // for strerror()

const char *dev = "/dev/ttyAMA0";

if ((handle = serialOpen (dev, 19200)) < 0) {
    printf("Could not open %s: %s\n", dev, strerror(errno));
    return -1;

If wiringPi doesn't set errno in this situation, then change this appropriately. Checks like this may seem like a waste of time when you are first developing something ("I'll go back and make all this properly robust later, right now I just want to see it work..."), but as this example demonstrates, it's usually more of a waste of time not to include error checking from when you first write the code. As in: if you are using a call you know can fail, wrap it in a check, now, not later!

/dev/ttyAMA0 looks like a serial line to me, and I bet if you try ls -l /dev/ttyAMA0 you'll see:

crw-rw---- 1 root tty 204, 64 Dec 11 10:26 /dev/ttyAMA0
 ^^ ^^  ^^   ^^^^ ^^^
 u  g   o     u    g

The first bit are the permissions, indicating the owner/user (u) and any one in the owning group (g) have read-write access to the node. Anyone else (o = other) have no permission to do anything. The owning user is root and the owning group is tty. If apache executes your program as the apache user, it will have apache's profile:

> id apache
uid=48(apache) gid=48(apache) groups=48(apache)

I don't have apache installed on the pi but the user there might actually be:

> id www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)

The easiest way to find out is to look at top while the server is running; the process will be something like apache or httpd. There will probably be several such processes, and one of them may be running as USER root, but the others will be something else. That's the apache user, and iding it will turn up something like the above -- the user has it's own group, and that is the only group it is part of. You can be in more than one group, however -- checkout id pi.

There are three obvious ways around the permissions issue:

  • Run apache as root. Not impossible, and if you are just doing this on your own LAN, security is not a concern (this is why it sets uid down normally). OTOH, if you are just learning about the server, it might be wise to keep this safety mechanism in...this is the worst of the three options.

  • Change the ownership of /dev/ttyAMAO. To do this, you need to set up something via udev or with a line in /etc/rc.local, because the device nodes are created at every boot. They are not real files that persist on disk. You want to leave the tty group with access in case there is some purpose to that, and tty is a group, not a user,1 and you can't add a group to another group -- so you shouldn't change the owning group. You'll have to change the owning user to www-data, which won't matter since root will still have access. This is probably the safest option, but the next one is not unreasonable either.

  • Add apache to the tty group. This is slightly less safe since it will have access to a bunch of other dev nodes too, but realistically, if you are not serving pages on line, it's the most orthodox, simple, and all round best option.

    > sudo usermod -aG tty www-data
    > id www-data
    uid=33(www-data) gid=33(www-data) groups=33(www-data),5(tty)

    Voila, the apache user is now permanently in the tty group and has read-write permissions on /dev/ttyAMA0. To undo that:

    > sudo usermod -G www-data www-data

1 See man 5 passwd and man 5 group -- the 5 is important, that's the section number, see man man.


I have a similar application; controlling stepper motors over the web. I am using a raspi 3B configured with apache as a webserver. The goal is to submit commands to the server via a web page on a client machine. The stepper motors are driven by Pololu Tic controllers connected to the raspi 3B. The tic controllers are setup to accept commands via serial (they can also be configured to take commands via i2c) I only mention this as reference for other future web searches.

The serial commands are generated by python scripts through cgi (if that is proper terminology). I can run the python scripts successfully from the command prompt, but they were "stopping" mid-program the moment the python script attempted to open the serial port.

Adding apache (www-data) to the tty group as you suggested did not do the trick initially. In addition to adding apache to the tty group, I also added it to the dialout group. This worked.

> sudo usermod -aG dialout www-data
>  id = www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data),5(tty),20(dialout)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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