Skip to content

Commit

Permalink
Improved js0n parsing support, Facebook shorthand functions
Browse files Browse the repository at this point in the history
  • Loading branch information
tcr committed Mar 27, 2013
1 parent e93e1b6 commit 7fcfe15
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 112 deletions.
48 changes: 44 additions & 4 deletions Lifegraph.cpp
Expand Up @@ -140,12 +140,12 @@ void debugWifiState () {
* Facebook
*/

void FacebookAPI::get (const char *path, const char *access_token) {
void FacebookAPI::get (const char *access_token, const char *path) {
this->hasBody = false;
this->_headers("GET", path, access_token);
}

void FacebookAPI::post (const char *path, const char *access_token) {
void FacebookAPI::post (const char *access_token, const char *path) {
this->hasBody = true;
this->_headers("POST", path, access_token);
}
Expand All @@ -161,13 +161,13 @@ int FacebookAPI::request ( js0n_user_cb_t cb ) {
}

js0n_parser_t parser;
parser.buf = this->buffer;
parser.buffer = this->buffer;
parser.stream = &wifly;
parser.user_cb = cb;

int status_code = 0;
readResponseHeaders(&status_code, (int *) &parser.length);
if (status_code != 0 && status_code < 400) {
if (status_code != 0 && status_code < 500) {
int parse_status = js0n_parse ( &parser );
}
return status_code;
Expand Down Expand Up @@ -218,6 +218,46 @@ void FacebookAPI::_headers (const char *method, const char *path, const char *ac
wifly.println();
}

/**
* Convenience methods
*/

// postStatus

int FacebookAPI::postStatus (const char *access_token, const char *status) {
Facebook.post(access_token, "me/feed");
Facebook.form("message", status);
return Facebook.request();
}

// unreadNotifications

boolean notifications_flag;

int notifications_cb ( js0n_parser_t * parser )
{
CB_BEGIN;
if (CB_MATCHES_KEY("unread")) {
CB_GET_NEXT_TOKEN;
if (parser->buffer[0] != '0') {
notifications_flag = 1;
}
}
CB_END;
}

int FacebookAPI::unreadNotifications (const char *access_token, boolean *notifications_flag_ret) {
notifications_flag = 0;
Facebook.get(access_token, "me/notifications?limit=1");
int status_code = Facebook.request(notifications_cb);
*notifications_flag_ret = notifications_flag;
return status_code;
}

/**
* First-class object
*/

// We have to initialize a static buffer for parsing JSON keys
// and strings. This only needs to be as big as your largest key/string.
uint8_t buf[300];
Expand Down
51 changes: 14 additions & 37 deletions Lifegraph.h
@@ -1,43 +1,12 @@
#ifndef _JS0N_H_
#define _JS0N_H_

#include <js0n.h>
#include <Arduino.h>
#include <WiFlyHQ.h>
#include <stdint.h>
#include <stdbool.h>
#include <SoftwareSerial.h>

#ifdef __cplusplus
extern "C" {
#endif

extern WiFly wifly;

typedef struct js0n_parser;

/* user callback type */
typedef int ( * js0n_user_cb_t ) ( uint8_t *buf, uint16_t length, uint16_t level );

typedef struct js0n_parser
{
// public:
Stream *stream;
uint16_t length;
js0n_user_cb_t user_cb;

// private:
uint8_t current;
uint16_t cursor;
uint8_t depth;
uint8_t utf8_remain;
uint16_t gostate;
uint8_t *buf;
uint16_t mark;
uint8_t live;
} js0n_parser_t;

int js0n_parse ( js0n_parser_t * parser );

// wifi

boolean connectWifi (SoftwareSerial *wifiSerial, const char *ssid, const char *pass);
Expand All @@ -59,16 +28,24 @@ class FacebookAPI {
int request ( js0n_user_cb_t cb );
void form (const char *name, const char *value);
void chunk (const char *str, int len);

int postStatus (const char *access_token, const char *status);
int unreadNotifications (const char *access_token, boolean *notifications_flag_ret);

private:
boolean hasBody;
void _headers (const char *method, const char *path, const char *access_token);
};

extern FacebookAPI Facebook;
#define crBegin static int __state=0; switch(__state) { case 0:
#define crReturn(x) do { __state=__LINE__; return x; \
case __LINE__:; } while (0)
#define crFinish }

#ifdef __cplusplus
}
#endif
#define CB_BEGIN crBegin; while (true) {
#define CB_END crReturn(0); } crFinish;
#define CB_GET_NEXT_TOKEN crReturn(0);
#define CB_MATCHES(x) (strncmp((char *) parser->buffer, x, parser->token_length) == 0)
#define CB_MATCHES_KEY(x) parser->token_type == JSON_MAP_KEY && CB_MATCHES(x)

#endif
extern FacebookAPI Facebook;
73 changes: 12 additions & 61 deletions examples/notificationlight.ino
Expand Up @@ -19,12 +19,12 @@ const char myPassword[] = "...";

// To make a request, you'll need an access token.
// For temporary one, use the Graph API Explorer: https://developers.facebook.com/tools/explorer
// and request a token with "read_mailbox" and "publish_stream" permissions.
// and request a token with "manage_notifications" and "publish_stream" permissions.
// But take note: Graph API Explorer access tokens expires every hour.
const char access_token[] = "...";

// Pin our LED is connected to.
int light = 12;
int light = 13;

/**
* Setup
Expand All @@ -45,71 +45,22 @@ void setup()
} else {
Serial.println("Joined wifi network.");
}

// Uncomment these lines to post a status update:
// Facebook.post("me/feed", access_token);
// Facebook.form("message", "Posting a status update from my Arduino!");
// int status_code = Facebook.request();
//
// Serial.print("Status: ");
// Serial.println(status_code);
}


/**
* Loop
*/

// Our response parsing is a state machine.
// 0 = no content
// 1 = error
// 2 = unread parsing
// 3 = no unread messages
// 4 = unread messages!
int state = 0;

// Our callback for parsing JSON.
// NOTE: Don't perform any serial tasks (such as logging) in this function.
// They are slow and will overflow the JSON parsing. Instead, we create
// a state variable that indicates the notification state.
int notifications_cb ( uint8_t *cursor, uint16_t length, uint16_t level )
{
// after "unread", we should immediately read the next value.
// if it's non-0, we've received unread notifications.
if (state == 2) {
if (cursor[0] != '0') {
state = 4;
} else {
state = 3;
}
}
// "error", perhaps access token is expired
if (strncmp((char *) cursor, "error", length) == 0) {
state = 1;
}
// "unread", we are reading the # of unread messages
if (strncmp((char *) cursor, "unread", length) == 0) {
state = 2;
}
}

void loop()
{
// Reset light state.
state = 0;

// Make the GET request to the Facebook API.
Facebook.get("me/inbox?limit=1&fields=unread", access_token);
int status_code = Facebook.request ( notifications_cb );
// Read if there are unread notifications on the server.
boolean notifications_flag;
int status_code = Facebook.unreadNotifications ( access_token, &notifications_flag );

// Update light state.
if (state > 0) {
digitalWrite(light, state == 4 ? HIGH : LOW);
// If the request is successful, update the light.
if (status_code == 200) {
digitalWrite(light, notifications_flag ? HIGH : LOW);
}

// Notify terminal of parsing status.
Serial.print("Done. Response: ");
// Notify terminal of our success.
Serial.print("Response: ");
Serial.print(status_code);
Serial.print(", notification state: ");
Serial.println(state, HEX);
Serial.print(" Unread notifications:");
Serial.println(notifications_flag, HEX);
}
48 changes: 38 additions & 10 deletions js0n.cpp
@@ -1,7 +1,7 @@
#include <Arduino.h>
#include "Lifegraph.h"
#include "js0n.h"

/* this code is based on the work by jeremie miller, which is part of the public domain
* https://github.com/lifegraph/js0n
* git://github.com/quartzjer/js0n.git */

#define CASES_a_TO_z \
Expand Down Expand Up @@ -79,7 +79,7 @@ typedef enum
void PUSH ( js0n_parser_t * parser, int refpos )
{
parser->mark = refpos == 0 ? 0 : 300 - refpos;
parser->buf[parser->mark++] = parser->current;
parser->buffer[parser->mark++] = parser->current;
if (parser->mark > 299) {
parser->mark -= 300;
}
Expand All @@ -93,17 +93,16 @@ void CAP ( js0n_parser_t * parser, int refpos )

/* notify the user */
if (length > 0) {
if (parser->user_cb != NULL) {
parser->user_cb ( parser->buf, length, parser->depth );
}
parser->token_length = length;
parser->user_cb ( parser );
parser->mark = 0;
}
}

void next_char ( js0n_parser_t * parser )
{
parser->live = parser->stream->readBytes((char *) &(parser->current), 1);
parser->buf[parser->mark++] = parser->current;
parser->buffer[parser->mark++] = parser->current;
if (parser->mark > 299) {
parser->mark -= 300;
}
Expand All @@ -124,6 +123,10 @@ int l_bad ( js0n_parser_t * parser )

int l_up ( js0n_parser_t * parser )
{
parser->token_length = 0;
parser->token_type = parser->current == '{' ? JSON_START_MAP : JSON_START_ARRAY;
parser->user_cb ( parser );

PUSH ( parser, 0 );
++parser->depth;
next_char ( parser );
Expand All @@ -133,6 +136,8 @@ int l_up ( js0n_parser_t * parser )

int l_down ( js0n_parser_t * parser )
{
parser->token_type = parser->current == '}' ? JSON_END_MAP : JSON_END_ARRAY;

--parser->depth;
CAP ( parser, 0 );
next_char(parser);
Expand Down Expand Up @@ -230,6 +235,10 @@ int l_utf_continue ( js0n_parser_t * parser )
return 0;
}

int json_cb_noop ( js0n_parser_t * parser ) {
return 0;
}

int js0n_parse ( js0n_parser_t * parser )
{
int ret;
Expand All @@ -240,31 +249,39 @@ int js0n_parse ( js0n_parser_t * parser )
parser->utf8_remain = 0;
parser->live = 1;
parser->gostate = JS0N_STATE_GOSTRUCT;
if (parser->user_cb == NULL) {
parser->user_cb = json_cb_noop;
}

next_char(parser);

boolean is_key = false;

while ( parser->cursor <= parser->length && parser->live )
{
switch ( parser->gostate )
{
case JS0N_STATE_GOSTRUCT:
switch ( parser->current )
{
case ',':
is_key = true;
case '\t':
case ' ':
case '\r':
case '\n':
case ':':
case ',':
ret = l_loop ( parser );
break;

case '"':
parser->token_type = is_key ? JSON_MAP_KEY : JSON_STRING;
ret = l_qup ( parser );
break;

case '[':
case '{':
is_key = true;
case '[':
ret = l_up ( parser );
break;

Expand All @@ -273,11 +290,21 @@ int js0n_parse ( js0n_parser_t * parser )
ret = l_down ( parser );
break;

case '-':
case 't':
parser->token_type = JSON_TRUE;
if (0) {
case 'f':
parser->token_type = JSON_FALSE;
}
if (0) {
case 'n':
parser->token_type = JSON_NULL;
}
if (0) {
case '-':
case CASES_0_TO_9:
parser->token_type = JSON_DOUBLE;
}
ret = l_bare ( parser );
break;

Expand Down Expand Up @@ -329,6 +356,7 @@ int js0n_parse ( js0n_parser_t * parser )

case '"':
ret = l_qdown ( parser );
is_key = false;
break;

case CASES_UTF8_2:
Expand Down

0 comments on commit 7fcfe15

Please sign in to comment.