Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
c-generic examples/rpi-wiringpi: New example demonstrating PubNub + R…
…aspberry Pi integration Uses the popular wiringPi library. First version, not tested on real RPi yet.
- Loading branch information
Showing
4 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,3 @@ | |||
*.o | |||
.deps | |||
pubnub-rpi-wiringpi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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; | |||
} |