View on GitHub

Pi Control Service

Control a Raspberry Pi from anywhere

Download this project as a .zip file Download this project as a tar.gz file

Why I made this

This project started because I had an external web server and I wanted it to get some data from sensors on a Raspberry Pi in my apartment. I didn't want to mess with a bunch of stupid networking, so that the two could talk to each other. Instead, I decided to take advantage of RabbitMQ. Messaging queues, like RabbitMQ, are a common tactic for letting separate application components communicate in a simple, scalable way. It's also a great fit for controlling the Raspberry Pi in many Internet of Things type projects.

What it does

Using the GPIO service (pi_control_service.GPIOControlService) you have access to the Raspberry Pi's digital GPIO pins. You can turn pins on and off as well as read their values. Using the custom action service (pi_control_service.CustomActionControlService) you can call methods on an "actions" class you implement. This allows you to do just about anything, like: access I2C, SPI, or the serial port, and issue system commands.

Install it

pip install Pi-Control-Service

GPIO control service

The GPIO service (pi_control_service.GPIOControlService) exposes access to the Raspberry Pi's digital GPIO pins. Using it you can turn pins on and off as well as read their values. The next two sections cover specifics of its configuration and usage.

Configuring the GPIO control service

A config file, written in YAML, is used to define the initial pin setup. If a pin is not defined here it will not be available to Pi-Control-Service. You can save this file anywhere. You will provide a path to the file in your code. The following snippet shows an example configuration file:

18:
  mode: OUT
  initial: HIGH
23:
  mode: OUT
  initial: LOW

Starting the GPIO service

This part runs on your Raspberry Pi. It initializes the desired GPIO pins, connects to RabbitMQ and starts listening for messages.

from pi_control_service import GPIOControlService


# The RabbitMQ connection string
RABBIT_URL='some_actual_connection_string'

# A unique string you make up to identify a single Raspberry Pi
DEVICE_KEY='my_awesome_raspberry_pi'

# Path to the config file referenced in the section above
PIN_CONFIG = '/path/to/config/file.yml'

gpio_service = GPIOControlService(
    rabbit_url=RABBIT_URL,
    device_key=DEVICE_KEY,
    pin_config=PIN_CONFIG)

gpio_service.start()

Note: In addition to the example above, the GPIOControlService class takes an optional argument reconnect_attempts. This is in integer represneting the number of times to attempt reconnection with RabbitMQ. These attempts will be made if the connection to RabbitMQ is interrupted after the start() method is called.

Using the GPIO service

For convenience there is a client library you can install and use on the computer that will be remotely controlling a Raspberry Pi. The same RABBIT_URL and DEVICE_KEY values referenced in the section above are also used to connect the client.

Installing the client

pip install Pi-Control-Client

Using the client

from pi_control_client import GPIOClient

# The RabbitMQ connection string (must match the one used when starting the service)
RABBIT_URL='some_actual_connection_string'

# A unique string you make up to identify a single Raspberry Pi (must match the one used when starting the service)
DEVICE_KEY='my_awesome_raspberry_pi'

pins_client = GPIOClient(rabbit_url=RABBIT_URL)

# Get all pins config
result = pins_client.read_config(DEVICE_KEY)

# Get a pin config
result = pins_client.read_config(DEVICE_KEY, 18)

# Turn a pin on
pins_client.on(DEVICE_KEY, 18)

# Turn a pin off
pins_client.off(DEVICE_KEY, 18)

# Read a pin value
result = pins_client.read_value(DEVICE_KEY, 18)

If you are already familar with RabbitMQ, you can implement your own client using any language. Things to know:

Turn a pin on

{
    "pin": 18,
    "action": "on"
}

Turn a pin off

{
    "pin": 18,
    "action": "off"
}

Read a pin value

{
    "pin": 18,
    "action": "read"
}

Read config for all pins

{
    "action": "get_config"
}

Read config for one pin

{
    "pin": 18,
    "action": "get_config"
}

Custom action service

The custom action service (pi_control_service.CustomActionControlService) allows you to call methods on a special "actions" class that you define. Using it, the possibilities are almost limitless.

Starting the custom action service

This part runs on your Raspberry Pi. It connects to RabbitMQ and starts listening for messages.

import subprocess

from pi_control_service import CustomActionControlService


# The RabbitMQ connection string
RABBIT_URL='some_actual_connection_string'

# A unique string you make up to identify a single Raspberry Pi
DEVICE_KEY='my_awesome_raspberry_pi'

# This class contains all the custom actions that the service will expose
class Actions(object):

    def hello_world(self):
        return {'message': "I do what I want!"}

    def cpu_temp(self):
        return subprocess.check_output(["/opt/vc/bin/vcgencmd", "measure_temp"])


custom_actions = CustomActionControlService(
    rabbit_url=RABBIT_URL,
    device_key=DEVICE_KEY,
    actions=Actions())

custom_actions.start()

Using the custom action service

For convenience there is a client library you can install and use on the computer that will be remotely controlling a Raspberry Pi. The same RABBIT_URL and DEVICE_KEY values referenced in the section above are also used to connect the client.

Installing the client

pip install Pi-Control-Client

Using the client

from pi_control_client import CustomActionClient

# The RabbitMQ connection string (must match the one used when starting the service)
RABBIT_URL='some_actual_connection_string'

# A unique string you make up to identify a single Raspberry Pi (must match the one used when starting the service)
DEVICE_KEY='my_awesome_raspberry_pi'

actions_client = CustomActionClient(rabbit_url=RABBIT_URL)

# Call a custom action
result = actions_client.call(DEVICE_KEY, 'name_of_action_method')

If you are already familar with RabbitMQ, you can implement your own client using any language. Things to know:

Call an action

{
    "action": "name_of_action_method"
}