Skip to content

Commit

Permalink
c-generic examples/rpi-wiringpi: New example demonstrating PubNub + R…
Browse files Browse the repository at this point in the history
…aspberry Pi integration

Uses the popular wiringPi library. First version, not tested on real RPi yet.
  • Loading branch information
pasky committed Jan 10, 2013
1 parent 20ed19c commit dded7ab
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 0 deletions.
3 changes: 3 additions & 0 deletions c-generic/examples/rpi-wiringpi/.gitignore
@@ -0,0 +1,3 @@
*.o
.deps
pubnub-rpi-wiringpi
17 changes: 17 additions & 0 deletions c-generic/examples/rpi-wiringpi/Makefile
@@ -0,0 +1,17 @@
CUSTOM_CFLAGS=-Wall -ggdb3 -O3
SYS_CFLAGS=-std=gnu99 `pkg-config --cflags libpubnub`
LIBS=`pkg-config --libs libpubnub` -lwiringPi

OBJS=pubnub-rpi-wiringpi.o

all: pubnub-rpi-wiringpi

pubnub-rpi-wiringpi: pubnub-rpi-wiringpi.o
$(call cmd,link)

clean:
rm -f *.o pubnub-rpi-wiringpi

install:

-include ../../Makefile.lib
59 changes: 59 additions & 0 deletions c-generic/examples/rpi-wiringpi/README.md
@@ -0,0 +1,59 @@
PubNub-based Raspberry Pi Pin Control
=====================================

A simple PubNub-based appliance that supports remote control of
Raspberry Pi pins (both input, e.g. buttons, and output, e.g. LEDs).
It uses the popular [WiringPi](https://projects.drogon.net/raspberry-pi/wiringpi/)
library for low-level controls.

Synopsis
--------

./pubnub-rpi-wiringpi -i 1 -r 4,10 -w 12,13

[send to channel rpi_wiringpi_cmd]
{"dest_id": 1, "write": {"12": "1", "13": "0"}}

[send to channel rpi_wiringpi_cmd]
{"dest_id": 1, "read": [4, 10]}
[read reply from channel rpi_wiringpi_status]

Message Interface
-----------------

Each Pi has an id. By default, the id is randomly generated on program
startup, but it can be set manually using the -i commandline option.

The program will allow manipulating just a given set of pins; two sets
of read and write pins (non-overlapping) must be specified via the
-r and -w commandline arguments, with the pin numbers comma-separated.

Note that there is no consistently used Raspberry Pi pin numbering.
Here, we use the [WiringPi pin numbering](https://projects.drogon.net/raspberry-pi/wiringpi/pins/).

Two channels are used for communication. Upon startup, the player sends
a status message to the ``rpi_wiringpi_status'' channel:

{"id":1}

Pin value messages are also sent to the channel as Pi's responses to
commands regarding these pins:

{"id":1, "pins":{"4":0, "10":1}}

The Pi listens for messages addressed to it on the ``rpi_wiringpi_cmd''
channel:

{"dest_id":1}

triggers just an empty status message,

{"dest_id":1, "write":{"<pinNum>":"0 or 1", ...}}

will update voltage levels on given pins (and trigger a status message
with the pin values listed),

{"dest_id":1, "read":[<pinNum>, ...]}

will check current voltage level on given pins and trigger a status
message with the acquired values.
231 changes: 231 additions & 0 deletions c-generic/examples/rpi-wiringpi/pubnub-rpi-wiringpi.c
@@ -0,0 +1,231 @@
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#include <json.h>
#include <wiringPi.h>

#include "pubnub.h"
#include "pubnub-sync.h"


/* See README.md for a brief usage and functional description. */


/* ID identifying this particular RPi. If not passed as an argument,
* randomly generated on each program run. */
int rpi_id;

// 32 is even more than the current RPi rev. offers
enum pin_type { PIN_OFF, PIN_READ, PIN_WRITE } pins[32];
#define pins_n (sizeof(pins) / sizeof(pins[0]))


void
arg_help(char *argv0)
{
fprintf(stderr, "Usage: %s [-i RPI_ID] -r READPIN0,READPIN1,... -w WRITEPIN0,WRITEPIN1\n", argv0);
}

void
parse_pinset(const char *pinset_s, enum pin_type type, int wiringPi_mode)
{
const char *pinset_p = pinset_s;
do {
int pin = atoi(pinset_p);
if (pin > 0 && pin < pins_n) {
pins[pin] = type;
pinMode(pin, wiringPi_mode);
}
pinset_p = strchr(pinset_p, ',');
if (!pinset_p)
break;
pinset_p++;
} while (pinset_p);
}

void
publish(struct pubnub *p, struct pubnub_sync *sync, const char *channel, json_object *msg)
{
printf("pubnub publishing: %s\n", json_object_get_string(msg));
pubnub_publish(p, channel, msg, 0, NULL, NULL);

json_object_put(msg);

if (pubnub_sync_last_result(sync) != PNR_OK) {
msg = pubnub_sync_last_response(sync);
fprintf(stderr, "pubnub publish error: %d [%s]\n", pubnub_sync_last_result(sync), json_object_get_string(msg));
json_object_put(msg);
exit(EXIT_FAILURE);
}

msg = pubnub_sync_last_response(sync);
printf("pubnub publish ok: %s\n", json_object_get_string(msg));
json_object_put(msg);
}


void
pong(struct pubnub *p, struct pubnub_sync *sync)
{
json_object *msg = json_object_new_object();
json_object_object_add(msg, "id", json_object_new_int(rpi_id));
publish(p, sync, "rpi_mplayer_status", msg);
}

void
process_message(struct pubnub *p, struct pubnub_sync *sync, json_object *msg)
{
/* Ignore all messages but what's addressed to us. */

json_object *id = json_object_object_get(msg, "dest_id");
if (!id || !json_object_is_type(id, json_type_int))
return;
if (json_object_get_int(id) != rpi_id)
return;

/* Each reply will have the 'id' field. */

json_object *reply = json_object_new_object();
json_object_object_add(reply, "id", json_object_new_int(rpi_id));

json_object *reply_pins = json_object_new_object();

/* Any pins to set? */

json_object *write = json_object_object_get(msg, "write");
if (write && json_object_is_type(write, json_type_object)) {
json_object_object_foreach(write, pin, pinval) {
if (!json_object_is_type(pinval, json_type_int))
continue;
int pin_num = atoi(pin);
if (!(pin_num > 0 && pin_num < pins_n && pins[pin_num] == PIN_WRITE))
continue;
/* Set the pin, physically! */
digitalWrite(pin_num, !!json_object_get_int(pinval));
/* Store in the reply. */
json_object_object_add(reply_pins, pin, pinval);
}
}

/* Any pins to read? */
json_object *read = json_object_object_get(msg, "read");
if (read && json_object_is_type(read, json_type_array)) {
for (int i = 0; i < json_object_array_length(read); i++) {
json_object *pin = json_object_array_get_idx(read, i);
if (!json_object_is_type(pin, json_type_int))
continue;
int pin_num = json_object_get_int(pin);
if (!(pin_num > 0 && pin_num < pins_n && pins[pin_num] == PIN_READ))
continue;
/* Read the pin, physically! */
int pinval = digitalRead(pin_num);
/* Store in the reply. */
char pinstr[8];
snprintf(pinstr, sizeof(pinstr), "%d", pin_num);
json_object_object_add(reply_pins, pinstr, json_object_new_int(pinval));
}
}

/* Ok, publish status message. */

if (json_object_get_object(reply_pins)->head)
json_object_object_add(reply, "pins", reply_pins);
else
json_object_put(reply_pins);

publish(p, sync, "rpi_mplayer_status", reply);

json_object_put(reply);
}

int
main(int argc, char *argv[])
{
/* Initialize wiringPi. */

if (wiringPiSetup() < 0)
exit(EXIT_FAILURE);

/* Parse options. */

/* Default: */
srand(time(NULL));
rpi_id = rand();

int opt;
while ((opt = getopt(argc, argv, "i:r:w:")) != -1) {
switch (opt) {
case 'i':
rpi_id = atoi(optarg);
break;
case 'r':
parse_pinset(optarg, PIN_READ, INPUT);
break;
case 'w':
parse_pinset(optarg, PIN_WRITE, OUTPUT);
break;
default: /* '?' */
arg_help(argv[0]);
exit(EXIT_FAILURE);
}
}
if (optind < argc) {
arg_help(argv[0]);
exit(EXIT_FAILURE);
}


/* Initialize PubNub. */

struct pubnub_sync *sync = pubnub_sync_init();
struct pubnub *p = pubnub_init("demo", "demo", NULL, NULL, NULL, &pubnub_sync_callbacks, sync);


/* Advertise. */

pong(p, sync);


/* Command loop. */

do {
pubnub_subscribe(p, "rpi_wiringpi_cmd", 300, NULL, NULL);

if (pubnub_sync_last_result(sync) == PNR_TIMEOUT) {
fprintf(stderr, "Time out after 300s reached. Forcibly re-issuing.\n");
continue;
}
if (pubnub_sync_last_result(sync) != PNR_OK) {
struct json_object *msg = pubnub_sync_last_response(sync);
fprintf(stderr, "pubnub subscribe error: %d [%s]\n", pubnub_sync_last_result(sync), json_object_get_string(msg));
json_object_put(msg);
exit(EXIT_FAILURE);
}

struct json_object *msg = pubnub_sync_last_response(sync);
if (json_object_array_length(msg) == 0) {
printf("pubnub subscribe ok, no news\n");
} else {
for (int i = 0; i < json_object_array_length(msg); i++) {
struct json_object *msg1 = json_object_array_get_idx(msg, i);
printf("pubnub subscribe got msg: %s\n", json_object_get_string(msg1));
process_message(p, sync, msg1);
}
}

json_object_put(msg);
sleep(1);
} while (1);


/* We should never reach this. */

pubnub_done(p);
return EXIT_SUCCESS;
}

0 comments on commit dded7ab

Please sign in to comment.