Java webapp to control your GPIO ports of the Raspberry Pi using http or cronjobs

What is raspberry-pi-gpio-web-control?

raspberry-pi-gpio-web-control is a lightweight java based web application to control your GPIO ports of your Raspberry Pi over http.

Feature Summary

  • Control GPIO ports over http
  • Conditions on input ports to set output ports
  • Fully integrates analog input with RC circuits
  • Fully integrates any script, i.e. SPI interface or send notifications
  • Cronjob integration

It is based on documentation at RPi Low-level peripherals. It is tested with Winstone Servlet Container, but any other servlet engine will probably do, too.

Detailed Features:

  • Every port can be set as input, output or analog input (requires a simple circuit based on Reading Analogue Sensors).
  • Output ports can be set conditionally on values of input ports (i.e. darknessSensor1in==1&lamp1out=1, see cron.conf).
  • You can give each port a custom name to make your client more understandable.
  • You can define a default state on GPIO output ports.
  • You can define a blocking time for a GPIO output port (so it is not switched to fast in case the user makes a request twice).
  • You can define a toggle time for a GPIO output port (i.e. if you want to turn a port on for a defined period of time, it can be done with a single request).
  • You can set a simulation mode on GPIO ports for testing your client.
  • Controlling multiple ports in one requests are set one after another, but the code is optimized and nothing unnecessary is done in between (it takes about 2-5ms on an idle Raspberry Pi to set all 17 ports, some artificial load (e.g.'find /' in the background) will slow it down to 10-15ms).
  • Cronjobs (exact to the second) for output ports are based on Output ports can be set conditionally and you have a simple but powerful semaphore mechanism.
  • You may define your own variables with a prefix VIRTUAL which are persisted in memory (unknown virtual variables default to "0"). This is helpful to keep track of the state for toggling output ports.
  • You can integrate any script into the command chain in order to have a greater power. External scripts (or commands) must be prefixed with CMD (i.e. to handle other hardware like SPI, I2C or UART or to send notifications via e-mail or push notifications).

Planned Features

  • More status and configuration information requestable via json (disengageable)

Possible Unplaned Features

  • Bit sequences (especially with AUTO.TOGGLE.TIME) for serial output or to control a servo (probably an SPI interface)
  • Some kind of PLC (Programmable Logic Controller)
  • GPIO input state change notification (instead of polling)
  • Suggestions are welcome

Project Status

The project has started in summer 2012 and I use it by myself mainly for simple output (manually and with cronjobs). If I had to give it a release number, I would say it is a 0.88 release. Bug reports and feature requests are welcome.

Getting Started (from Source)

Install Depedencies

  1. Install Maven2 sudo apt-get install maven2 (it comes with a JRE and without javac)

  2. Install a JDK, e.g. sudo apt-get install oracle-java7-jdk

  3. Find correct JAVA_HOME (there are a lot of symlinks in between) using which and ls -l, i.e. on my raspberry it is now:

which javac

ls -l /usr/bin/javac
lrwxrwxrwx 1 root root 23 Jun  3 21:09 /usr/bin/javac -> /etc/alternatives/javac

ls -l /etc/alternatives/javac
lrwxrwxrwx 1 root root 48 Jun  3 21:09 /etc/alternatives/javac -> /usr/lib/jvm/jdk-7-oracle-arm-vfp-hflt/bin/javac
  1. Set JAVA_HOME and PATH at the end in file ~/.profile
export JAVA_HOME
export PATH
  1. Logout and login again and check your JAVA_HOME with echo $JAVA_HOME

  2. Check that you can call java and javac from the command line, e.g. java -version

  3. Install Mercurial sudo apt-get install mercurial

Get and Build the Project

  1. Get the project hg clone

  2. Change working directory to raspberry-pi-gpio-web-control with cd raspberry-pi-gpio-web-control

  3. Build the project mvn package (It takes about 3-7 minutes to build on Raspberry Pi).

  4. Get Winstone Servlet Container at and put the jar in the same directory as the

  5. Check the location and name of WINSTONE_JAR in

  6. Change the variable JAVA_HOME in It must be the same as in ~/.profile

First Configuration

  1. Copy gpio.conf.MUST_BE_CHANGED to gpio.conf and configure it.

  2. Start the servlet container sudo ./ You may have to mark the scripts executable chmod a+x first.

  3. Make a request, e.g. http://raspberrypi:8080/handle?g0=1&g1=0

    It will return {"g1":0,"g0":1}.

  4. Read the log file logs/raspberry-pi-gpio-web-control.log

  5. Stop the servlet container sudo ./ Make sure that there are no old Winstone processes running with old configurations! You can check that with ps -ef| grep java. In case you can not stop the process and you have to stop it with kill -9, you may consider rebooting as the GPIOs are still exported and the next start may fail.

  6. To enable cronjobs, you need to copy cron.conf.MAY_BE_CHANGED to cron.conf and configure it.

  7. To enable external commands, you need to copy cmd.conf.MAY_BE_CHANGED to cmd.conf and configure it.

Going Productive

  • Make sure you disable simulate mode in gpio.conf set simulate.gpios=false.
  • Turn down logging after testing in (this is important for performance on your Raspberry Pi).
  • A simple init.d script is also provided in file src/main/resources/init.d/gpio-winstone which can be installed with the follwing commands:
chmod a+x /home/pi/raspberry-pi-gpio-web-control/src/main/resources/init.d/gpio-winstone
sudo cp /home/pi/raspberry-pi-gpio-web-control/src/main/resources/init.d/gpio-winstone /etc/init.d/gpio-winstone
sudo update-rc.d gpio-winstone defaults 3 3

Do not forget that your GPIO ports may be operated by this process now (even after reboot)!

Configuration and Logging and Debugging

  • To change logging check and
  • For configuration check gpio.conf and (and cron.conf and cmd.conf).

Updating the Software

If you already installed a version from source, it is very easy to get new features and update to the latest release:

  1. Change working directory to raspberry-pi-gpio-web-control with cd raspberry-pi-gpio-web-control

  2. Get the latest release hg pull

  3. Update your local repository hg update

  4. Stop the servlet container sudo ./

  5. Rebuild the project mvn package

  6. Start the servlet container sudo ./

An iPhone Client

If you also need a client for your iPhone or iPad, you may want to have look at my free iPhone client: iControl Web.

Sampe Usage

Imagine you have the following configuration in gpio.conf:



# lamp1out has a "momentary contact switch"






The darknessSensor1in will return 1 if it is "dark" and 0 during daylight.

Manual Control

In order to set the lamp manually you could send the following request to turn on the lamp: http://raspberrypi:8080/handle?lamp1out=1. Response would be {"lamp1out":1}. GPIO.1 will be set to 1 and after 250ms, it toggles back to 0. You can not control the gpio for 10 seconds.

In order to check the sensor manually you could send the following request: http://raspberrypi:8080/handle?darknessSensor1in=IN. Response could be {"darknessSensor1in":1}. The state of the GPIO.0 is 1.

In order to check the state of the lamp manually you could send the following request: http://raspberrypi:8080/handle?lamp1out=IN. Response could be {"lamp1out":1}. The lamp is on and the state of the GPIO.1 is 1.

You could also send a conditional manual request (turn light on if it is dark enough): http://raspberrypi:8080/handle?darknessSensor1in==1&lamp1out=1. The answer would be {"lamp1out":1} if it was dark enough (and darknessSensor1in==1 is true) or {} if it is not dark enough (and darknessSensor1in==1 is false).

You could also check condition of another output port and react on that, e.g. http://raspberrypi:8080/handle?otherOutputPort==0&lamp1out=1.

You have to expect the following responses:

Value Meaning
-3 An analog input port has never been read (during process runtime).
-2 An output pin is blocked and can not be set now.
-1 A pin has no state (probably an error case).
empty The key and value are missing. The port is not configured or the port is configured as input and was tryed to set (or vice versa) or a conditional port was not set.
0 An output port was set to 0 or an input port currently returns 0.
1 An output port was set to 1 or an input port currently returns 1.


The following comparison operators are currently support:


Watch out that your values for VIRTUAL ports must be numeric in order to use it for conditions or you get a NumberFormatException.

Automatic Control

Imagine you want to turn on the lamp between 16:00 and 21:00 whenever it is dark enough and turn it off at 22:30. Remember that lamp1out has a "momentary contact switch" (Therefore we have to keep track if it is turned on or off and always set port 1 to turn it on or off).

Add the following lines to cron.conf:

0 * 16-21 ? * * :: VIRTUALlamp1alreadyOn==0&darknessSensor1in==1&lamp1out=1&VIRTUALlamp1alreadyOn=1

0 30 22 ? * * :: VIRTUALlamp1alreadyOn==1&lamp1out=1&VIRTUALlamp1alreadyOn=0

Every minute the virtual variable is checked VIRTUALlamp1alreadyOn. If lamp is not on, the darkness sensor darknessSensor1in is check. If both conditions are true, the lamp lamp1out is turned on and the state is changed. At 22:30, the lamp is turned off and the state is changed.

You can be creative here, e.g. if you want to recheck every 15 minutes if it is still dark enough (or if there was just a dark cloud), add the follwoing line:

30 */15 16-21 ? * * :: VIRTUALlamp1alreadyOn==1&darknessSensor1in==0&lamp1out=1&VIRTUALlamp1alreadyOn=0

Just to be sure check 30 seconds later to keep you out of concurrency trouble while setting/checking VIRTUALlamp1alreadyOn.

Analog INPUT

A simple but powerful circuit can be found at Reading Analogue Sensors which makes analog inputs simple to read and handle in software.

Please also read comments in gpio.conf about the exact internal processing.

Although it is an analog input port, you must explicitly start reading the value (IN) because it may be slow (and blocks your port and your http request). The last read value is saved internally and can be used afterwards with conditions (initial and error value is -1). Keep in mind that reading may be slow. With manual control, your response may be slow (since reading blocks the port). With automatic control, make sure that the read process has finished or you may check against a previous value.


The following request will read a new value for analogSensor2ti (request is blocked until it is read), and will afterwards check if analogSensor2ti > 10000 and may set lamp1out.


With the following two cronjobs you would set your lamp1out 15 minutes later than expected because reading take a little while and you check against an old value

0 */15 16-21 ? * * :: analogSensor2ti=IN

0 */15 16-21 ? * * :: analogSensor2ti!=-1&analogSensor2ti>10000&lamp1out=1

Given your analogSensor2ti takes 1200 milliseconds to be read at maximum, the following example is better:

0 */15 16-21 ? * * :: analogSensor2ti=IN

3 */15 16-21 ? * * :: analogSensor2ti!=-1&analogSensor2ti>10000&lamp1out=1

You can also do it all at once:

0 */15 16-21 ? * * :: analogSensor2ti=IN&analogSensor2ti!=-1&analogSensor2ti>10000&lamp1out=1

Please Note

The order of digital input ports and analog input ports is respected, but mixing output ports and input ports will change the internal processing (output ports are grouped and come last. Conditions break the groups into subgroups), e.g.

  • out1=1&analogTI=IN&out2=1 is handled internally as analogTI=IN&out1=1&out2=1

  • out1=1&in1=IN&out2=1&analogTI=IN&out3=1 is handled internally as in1=IN&analogTI=IN&out1=1&out2=1&out3=1

  • out1=1&in1==1&out2=1&analogTI=IN&out3=1 (given in1==1 is true) is handled internally as out1=1&in1==1&analogTI=IN&out2=1&out3=1

External commands/scripts

You can integrate any external command or script into the servers command chain. Configuration is done in cmd.conf. More information is descripted there, too. Make sure that the command/script is either full qualified and absolute (i.e. /full/qualified/path/and/file) or it has to be reachable via your PATH variable (keep in mind that the init.d-script may have different environment settings). You can not call every command via http (or automatically via cron), but only those, you have specified. You have to give your script a name to be addressed. This name must be prefixed with CMD (i.e. EXTCMD.NAME.1=date must be addressed CMDdate=1). You must call your script with a parameter (or in other words with a value within the GET-parameters), but your script does not have to act on it if you don't need it.

This feature is a generic implementation of my previous planned featue named "custom hooks" (pre/post) as it gives you full power to integrate anything (i.e. handle other hardware like SPI, I2C or UART or to send notifications via e-mail or push notifications).

Example of an alarm system

The following configuration is given:

You have a script which sends notifications (you can use the sample script which is located in src/main/resources/script/ for iPhone push notifications or Android notifications based on (configuration is done in cmd.conf for scripts):


  • You have a GPIO input port which is connected to a door-is-open-sensor named doorIsOpenIN.
  • Between 09:00 and 20:00 you want to be notified if the door is open. You have one cronjob:
#check door between 9:00 and 19:59 and notify if it is open 
0 * 9-19 ? * * :: doorIsOpenIN=IN&doorIsOpenIN==1&CMDnotify=1

Copyright 2012

Authors: Sebastian Bub (


Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.