diff --git a/Libraries/SerialController.h b/Libraries/SerialController.h new file mode 100644 index 0000000..104d4c0 --- /dev/null +++ b/Libraries/SerialController.h @@ -0,0 +1,288 @@ +#ifndef SERIAL_CONTROLLER_H +#define SERIAL_CONTROLLER_H + +#include +#include + +#ifndef MAX_KEY_LEN +#define MAX_KEY_LEN 32 +#endif + +#ifndef MAX_VAL_LEN +#define MAX_VAL_LEN 32 +#endif + +#define MAX_MSG_LEN (MAX_KEY_LEN + MAX_VAL_LEN + 3) + +typedef void (*voidCallback)(); +typedef void (*stringCallback)(char*); +typedef void (*intCallback)(int); +typedef void (*floatCallback)(float); + +struct StringF { + int maxSize; + int size; + char *str; + + StringF(int maxSize) : maxSize(maxSize), size(0) { + str = malloc(maxSize * sizeof(char)); + } + + ~StringF() { free(str); } + + void clear() { + memset(str, 0, maxSize); + size = 0; + } + + void append(char *string) { + int s = snprintf(str, maxSize, "%s%s", str, string); + size = s >= maxSize ? maxSize-1 : s; + } + + void append(char c) { + if (size < maxSize-1) { + str[size] = c; + size++; + } + } + + int toInt() { + return atoi(str); + } + + float toFloat() { + return atof(str); + } + + bool equals(StringF string) { + return strcmp(str, string.str) == 0; + } + + bool equals(char *string) { + return strcmp(str, string) == 0; + } +}; + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * SerialCallback + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +struct SerialCallback { + StringF key; + + SerialCallback() : key(MAX_KEY_LEN) {} + + typedef enum { + VOID, + STRING, + INT, + FLOAT + } ValueType; + + ValueType valueType; + + union { + voidCallback v; + stringCallback s; + intCallback i; + floatCallback f; + } callback; + + void respond(StringF value) { + switch (valueType) { + case VOID: + callback.v(); + break; + + case STRING: + callback.s(value.str); + break; + + case INT: + callback.i(value.toInt()); + break; + + case FLOAT: + callback.f(value.toFloat()); + break; + + default: + // bad valueType, do nothing + break; + } + } +}; + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * SerialController + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifndef SERIALCONTROLLER_MAX_CALLBACKS +#define SERIALCONTROLLER_MAX_CALLBACKS 16 +#endif + +// helper to define repetitious addCallback overloads +#define addCallbackDef(cbType, valType, u) \ + void addCallback(const char *keyString, cbType callback) { \ + SerialCallback *cb = callbacks + numCallbacks; \ + cb->key.str = keyString; \ + cb->valueType = SerialCallback::ValueType::valType; \ + cb->callback.u = callback; \ + numCallbacks++; \ + } + +class SerialController { +private: + enum { + WAIT_FOR_START, + PARSE_KEY, + PARSE_VALUE, + N_PARSE_STATES + } state; + + + int numCallbacks; + SerialCallback callbacks[SERIALCONTROLLER_MAX_CALLBACKS]; + StringF key; + StringF value; + bool steleProtocol; + + void waitForSerial(long baudrate) { + Serial.begin(baudrate); + while(!Serial); + + // wait for handshake if using steleProtocol + while(!handshake && steleProtocol) { + if (Serial.available() && Serial.read() == '{') { + handshake = true; + sendMessage("arduino-ready","1"); + } + } + } + + + void lookupAndRunCallback() { + if (steleProtocol && key.equals("wake-arduino")) { + sendMessage("arduino-ready", "1"); + return; + } + + for (int i=0; isteleProtocol = steleProtocol; + errorResponse = true; + waitForSerial(baudrate); + } + + + // overloaded callback-adding functions + addCallbackDef(voidCallback, VOID, v) + addCallbackDef(stringCallback, STRING, s) + addCallbackDef(intCallback, INT, i) + addCallbackDef(floatCallback, FLOAT, f) + + + // message sending + void sendMessage(char *messageKey, char *messageValue) { + char msg[MAX_MSG_LEN]; + int size = snprintf(msg, MAX_MSG_LEN, "{%s:%s}", messageKey, messageValue); + if (size > MAX_MSG_LEN) + Serial.println("{message-too-big:1}"); + else + Serial.println(msg); + } + + void sendMessage(char *messageKey) { + sendMessage(messageKey, "1"); + } + + void sendMessage(char *messageKey, int messageValue) { + char value[MAX_VAL_LEN]; + snprintf(value, MAX_VAL_LEN, "%d", messageValue); + sendMessage(messageKey, value); + } + + + void sendMessage(char *messageKey, float messageValue) { + char value[MAX_VAL_LEN]; + snprintf(value, MAX_VAL_LEN, "%f", messageValue); + sendMessage(messageKey, value); + } + + + // updating + void update() { + while (Serial.available() > 0) { + char c = Serial.read(); + switch (state) { + case WAIT_FOR_START: + if (c == '{') { + key.clear(); + value.clear(); + state = PARSE_KEY; + } + break; + + case PARSE_KEY: + if (c == '{') { + state = PARSE_KEY; + } + else if (c == ':') { + state = PARSE_VALUE; + } + else if (c == '}') { + // malformed input, look for next token + state = WAIT_FOR_START; + } + else { + key.append(c); + } + break; + + case PARSE_VALUE: + if (c == '}') { + lookupAndRunCallback(); + state = WAIT_FOR_START; + } + else { + value.append(c); + } + break; + + default: + // something's gone wrong, reset + state = WAIT_FOR_START; + break; + } + } + } +}; + +#endif diff --git a/Libraries/SerialController.hpp b/Libraries/SerialController.hpp deleted file mode 100644 index 1c18b85..0000000 --- a/Libraries/SerialController.hpp +++ /dev/null @@ -1,216 +0,0 @@ -#ifndef SERIAL_CONTROLLER_HPP -#define SERIAL_CONTROLLER_HPP - -#define MAX_STRING_LEN 128 - -#include - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -typedef enum { - WAIT_FOR_START, - PARSE_KEY, - PARSE_KEY_OVERFLOW, - PARSE_VALUE, - PARSE_VALUE_OVERFLOW, - N_PARSE_STATES -} parseState; - -static void cleanString(char* string); - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class SerialController { - private: - parseState state; - void (*callback)(char* messageKey, char* messageValue); - char key[MAX_STRING_LEN]; - char value[MAX_STRING_LEN]; - int keyIndex, valueIndex; - - public: - bool handshake; - - SerialManager() { state = WAIT_FOR_START; callback = NULL; } - void setup(long baudrate, void (*callback_)(char*, char*)) { - waitForSerial(baudrate); - callback = callback_; - } - - void waitForSerial(long baudrate) { - Serial.begin(baudrate); - while(!Serial); - - while(!handshake) { - if (Serial.available() && Serial.read() == '{') { - handshake = true; - sendMessage("arduino-ready","1"); - } - } - } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - void sendMessage(char* messageKey, char* messageValue) { - cleanString(messageKey); - cleanString(messageValue); - - char result[2*MAX_STRING_LEN+3]; - strcpy(result,"{"); - strcat(result,messageKey); - strcat(result,":"); - strcat(result,messageValue); - strcat(result,"}"); - - Serial.println(result); - } - - void sendMessage(char* messageKey, int messageValue) { - char stringValue[MAX_STRING_LEN]; - snprintf(stringValue, MAX_STRING_LEN, "%d", messageValue); - sendMessage(messageKey, stringValue); - } - - void sendMessage(char* messageKey, unsigned int messageValue) { - char stringValue[MAX_STRING_LEN]; - snprintf(stringValue, MAX_STRING_LEN, "%u", messageValue); - sendMessage(messageKey, stringValue); - } - - void sendMessage(char* messageKey, long int messageValue) { - char stringValue[MAX_STRING_LEN]; - snprintf(stringValue, MAX_STRING_LEN, "%ld", messageValue); - sendMessage(messageKey, stringValue); - } - - void sendMessage(char* messageKey, long unsigned int messageValue) { - char stringValue[MAX_STRING_LEN]; - snprintf(stringValue, MAX_STRING_LEN, "%lu", messageValue); - sendMessage(messageKey, stringValue); - } - - void sendMessage(char* messageKey, float messageValue) { - char stringValue[MAX_STRING_LEN]; - snprintf(stringValue, MAX_STRING_LEN, "%f", messageValue); - sendMessage(messageKey, stringValue); - } - - void update() { - while (Serial.available() > 0) { - char c = Serial.read(); - switch (state) { - case WAIT_FOR_START: - if (c == '{') { - state = PARSE_KEY; - strcpy(key,""); - strcpy(value,""); - keyIndex = 0; - valueIndex = 0; - } - break; - - case PARSE_KEY: - if (keyIndex == MAX_STRING_LEN-1) { - key[keyIndex] = 0; - state = PARSE_KEY_OVERFLOW; - } - else if (c == '{') { - strcpy(key,""); - strcpy(value,""); - keyIndex = 0; - valueIndex = 0; - } - else if (c == ':') { - key[keyIndex] = 0; - state = PARSE_VALUE; - } - else if (c == '}') { - key[keyIndex] = 0; - (*callback)(key,value); - state = WAIT_FOR_START; - } - else { - key[keyIndex] = c; - keyIndex++; - } - break; - - case PARSE_VALUE: - if (valueIndex == MAX_STRING_LEN - 1) { - value[valueIndex] = 0; - state = PARSE_VALUE_OVERFLOW; - } - else if (c == '}') { - value[valueIndex] = 0; - (*callback)(key,value); - state = WAIT_FOR_START; - } - else { - value[valueIndex] = c; - valueIndex++; - } - break; - - case PARSE_KEY_OVERFLOW: - if (c == ':') { - state = PARSE_VALUE; - } - else if (c == '{') { - strcpy(key,""); - strcpy(value,""); - keyIndex = 0; - valueIndex = 0; - state = PARSE_KEY; - } - break; - - case PARSE_VALUE_OVERFLOW: - if (c == '{') { - strcpy(key,""); - strcpy(value,""); - keyIndex = 0; - valueIndex = 0; - state = PARSE_KEY; - } - else if (c == '}') { - (*callback)(key,value); - state = WAIT_FOR_START; - } - break; - - default: - // something's gone wrong, reset - state = WAIT_FOR_START; - break; - } - } - } -}; - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -void cleanString(char* string) { - // remove '{', '}', and ':' delimiter characters from a string - char result[MAX_STRING_LEN]; - - int i = 0; - int j = 0; - while(string[i] != 0) { - if (string[i] == '{' - || string[i] == '}' - || string[i] == ':') { - i++; - } - else { - result[j] = string[i]; - i++; j++; - } - } - - // close string - result[j] = 0; - - string = result; -} - -#endif diff --git a/examples/SerialControllerExample.ino b/examples/SerialControllerExample.ino deleted file mode 100644 index 48b2dea..0000000 --- a/examples/SerialControllerExample.ino +++ /dev/null @@ -1,71 +0,0 @@ -#include "arduino-base/Libraries/SerialController.hpp" - -SerialController serialController; - -long baudrate = 115200; -int blinkrate = 1000; -bool blinking = true; -bool led_on = false; -long blinkcounter = 0; - -#define ledpin 13 - -void setup() { - - // Enables/disables debug messaging from ArduinoJson - boolean arduinoJsonDebug = false; - - // Ensure Serial Port is open and ready to communicate - serialController.setup(baudrate, &onParse); - - // For every sketch, we need to set up our IO - // Setup digital pins and default modes as needed, analog inputs are setup by default - pinMode(ledpin, OUTPUT); -} - -void loop() { - // update SerialController and check for new data - serialController.update(); - - if (blinking) { - if ((millis() - blinkcounter) >= blinkrate) { - led_on = !led_on; - digitalWrite(ledpin,led_on); - blinkcounter = millis(); - - // put led_on into a string via snprintf() - char ledOn[5]; - snprintf(ledOn,5,"%d",led_on); - - // send data to stele. note that they must both be strings! - serialController.sendMessage("led-status", ledOn); - } - } -} - -// this function will run when serialController reads new data -void onParse(char* message, char* value) { - if (strcmp(message, "led") == 0) { - // Turn-on led - digitalWrite(ledpin, atoi(value)); - } - else if (strcmp(message, "blinkrate") == 0) { - // set blinkrate - // NOTE: atoi() converts a string containing a number to an integer. - // use atof() if you want a float - blinkrate = atoi(value); - } - else if (strcmp(message, "blinking") == 0) { - // turn blinking on or off - blinking = atoi(value); - } - else if (strcmp(message, "wake-arduino") == 0 && strcmp(value, "1") == 0) { - // you must respond to this message, or else - // stele will believe it has lost connection to the arduino - serialController.sendMessage("arduino-ready", "1"); - } - else { - // helpfully alert us if we've sent something wrong :) - serialController.sendMessage("unknown-command", "1"); - } -} diff --git a/examples/SerialControllerExample/SerialControllerExample.ino b/examples/SerialControllerExample/SerialControllerExample.ino new file mode 100644 index 0000000..dfea5a7 --- /dev/null +++ b/examples/SerialControllerExample/SerialControllerExample.ino @@ -0,0 +1,56 @@ +#include + +#include "SerialController.h" + +SerialController serialController; + +long baudrate = 115200; +int blinkrate = 1000; +bool blinking = true; +bool led_on = false; +long blinkcounter = 0; + +#define ledpin 13 + +// a callback function that will be linked with the serial key "blinkrate" +void onBlinkrate(int rate) { + blinkrate = rate; +} + +// a callback function that will be linked with the serial key "blinking" +void onBlinking(int state) { + blinking = state; +} + + +void setup() { + // Ensure Serial Port is open and ready to communicate + serialController.setup(baudrate); + + // register a callback function to the serial key "blinking" + serialController.addCallback("blinking", onBlinking); + // register a callback function to the serial key "blinkrate" + serialController.addCallback("blinkrate", onBlinkrate); + // register a lambda callback to the serial key "led" + serialController.addCallback("led", [](int s){ digitalWrite(ledpin, s); }); + + // For every sketch, we need to set up our IO + // Setup digital pins and default modes as needed, analog inputs are setup by default + pinMode(ledpin, OUTPUT); +} + +void loop() { + // update SerialController and check for new data + serialController.update(); + + if (blinking) { + if ((millis() - blinkcounter) >= blinkrate) { + led_on = !led_on; + digitalWrite(ledpin,led_on); + blinkcounter = millis(); + + // send data to stele + serialController.sendMessage("led-status", led_on); + } + } +}