Raspberry Pi GPIO Web Control /

Filename Size Date modified Message
206 B
12.3 KB
1.9 KB
5.3 KB
1.2 KB
3.0 KB
1.4 KB
738 B


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

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. 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.


  • 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 look better.
  • You can define a default state on output ports.
  • You can define a blocking time for an output port (so it is not switched to fast in case the user makes a request twice).
  • You can define a toggle time for an 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 for testing your client.
  • Setting 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").

Planned Features

  • If GPIO ports are used to represent binary output values, blocking single ports is dangerous: Delayed/Queue requests
  • Automated tests
  • Custom hooks (pre/post), e.g. for notifications
  • 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)
  • Suggestions are welcome

Project Status

The project has started 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.85 release. Bug reports and feature requests are welcome.

Getting Started (from tgz)

  1. Download the latest version of the package as raspberry-pi-gpio-web-control-[version].tgz

  2. Extract it using tar xzf raspberry-pi-gpio-web-control-[version].tgz

  3. Install Java, e.g. sudo apt-get install openjdk-6-jdk

  4. Go on with 7. Getting Started (from Source)

Getting Started (from Source) - PREFERRED METHOD!

  1. Install Java, e.g. sudo apt-get install openjdk-6-jdk

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

  3. Install Maven2 sudo apt-get install maven2

  4. Install Mercurial sudo apt-get install mercurial

  5. Get the project hg clone

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

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

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

  9. Check the location and name of WINSTONE_JAR in

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

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

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

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

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

  14. 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.

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

Going Productive

  • Make sure you disable simulate mode.
  • 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, cron.conf and

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

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.