a serial library to communicate with pebble time's smart strap interface
Dependents: xadow_smartstrap_for_pebble
Revision 0:e4dad9e53f06, committed 2015-11-04
- Comitter:
- KillingJacky
- Date:
- Wed Nov 04 09:58:41 2015 +0000
- Commit message:
- initial commit;
Changed in this revision
diff -r 000000000000 -r e4dad9e53f06 PebbleSerial.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PebbleSerial.c Wed Nov 04 09:58:41 2015 +0000 @@ -0,0 +1,460 @@ +/* This library is for communicating with a Pebble Time via the accessory port for Smart Straps. */ + +#include "PebbleSerial.h" + +#include "crc.h" +#include "encoding.h" + +#define PROTOCOL_VERSION 1 +#define GENERIC_SERVICE_VERSION 1 + +#define FRAME_MIN_LENGTH 8 +#define FRAME_VERSION_OFFSET 0 +#define FRAME_FLAGS_OFFSET 1 +#define FRAME_PROFILE_OFFSET 5 +#define FRAME_PAYLOAD_OFFSET 7 + +#define FLAGS_IS_READ_OFFSET 0 +#define FLAGS_IS_MASTER_OFFSET 1 +#define FLAGS_IS_NOTIFICATION_OFFSET 2 +#define FLAGS_RESERVED_OFFSET 3 +#define FLAGS_IS_READ_MASK (0x1 << FLAGS_IS_READ_OFFSET) +#define FLAGS_IS_MASTER_MASK (0x1 << FLAGS_IS_MASTER_OFFSET) +#define FLAGS_IS_NOTIFICATION_MASK (0x1 << FLAGS_IS_NOTIFICATION_OFFSET) +#define FLAGS_RESERVED_MASK (~(FLAGS_IS_READ_MASK | \ + FLAGS_IS_MASTER_MASK | \ + FLAGS_IS_NOTIFICATION_MASK)) +#define FLAGS_GET(flags, mask, offset) (((flags) & mask) >> offset) +#define FLAGS_SET(flags, mask, offset, value) \ + (flags) = ((flags) & ~mask) | (((value) << offset) & mask) + +typedef enum { + SmartstrapProfileInvalid = 0x00, + SmartstrapProfileLinkControl = 0x01, + SmartstrapProfileRawData = 0x02, + SmartstrapProfileGenericService = 0x03, + NumSmartstrapProfiles +} SmartstrapProfile; + +typedef enum { + LinkControlTypeInvalid = 0, + LinkControlTypeStatus = 1, + LinkControlTypeProfiles = 2, + LinkControlTypeBaud = 3, + NumLinkControlTypes +} LinkControlType; + +typedef enum { + LinkControlStatusOk = 0, + LinkControlStatusBaudRate = 1, + LinkControlStatusDisconnect = 2 +} LinkControlStatus; + +typedef struct { + uint8_t version; + uint32_t flags; + uint16_t profile; +} FrameHeader; + +typedef struct { + FrameHeader header; + uint8_t *payload; + uint8_t checksum; + size_t length; + size_t max_payload_length; + uint8_t footer_byte; + bool should_drop; + bool read_ready; + bool is_read; + EncodingStreamingContext encoding_ctx; +} PebbleFrameInfo; + +typedef struct __attribute__((packed)) { + uint8_t version; + uint16_t service_id; + uint16_t attribute_id; + uint8_t type; + uint8_t error; + uint16_t length; + uint8_t data[]; +} GenericServicePayload; + +static SmartstrapRequestType s_last_generic_service_type; +static uint32_t s_last_message_time = 0; +static PebbleFrameInfo s_frame; +static SmartstrapCallback s_callback; +static bool s_connected; +static PebbleBaud s_current_baud = PebbleBaudInvalid; +static PebbleBaud s_target_baud = PebbleBaudInvalid; +static const uint32_t BAUDS[] = { 9600, 14400, 19200, 28800, 38400, 57600, 67500, 115200, 125000, + 230400, 250000, 460800 }; +static uint16_t s_notify_service; +static uint16_t s_notify_attribute; +static const uint16_t *s_supported_services; +static uint8_t s_num_supported_services; +static struct { + bool can_respond; + uint16_t service_id; + uint16_t attribute_id; +} s_pending_response; + + +void prv_set_baud(PebbleBaud baud) { + if (baud == s_current_baud) { + return; + } + s_current_baud = baud; + s_callback(SmartstrapCmdSetBaudRate, BAUDS[baud]); + s_callback(SmartstrapCmdSetTxEnabled, true); + s_callback(SmartstrapCmdSetTxEnabled, false); +} + +void pebble_init(SmartstrapCallback callback, PebbleBaud baud, const uint16_t *services, + uint8_t num_services) { + s_callback = callback; + s_target_baud = baud; + s_supported_services = services; + s_num_supported_services = num_services; + prv_set_baud(PebbleBaud9600); +} + +void pebble_prepare_for_read(uint8_t *buffer, size_t length) { + s_frame = (PebbleFrameInfo) { + .payload = buffer, + .max_payload_length = length, + .read_ready = true + }; +} + +static void prv_send_flag(void) { + s_callback(SmartstrapCmdWriteByte, ENCODING_FLAG); +} + +static void prv_send_byte(uint8_t data, uint8_t *parity) { + crc8_calculate_byte_streaming(data, parity); + if (encoding_encode(&data)) { + s_callback(SmartstrapCmdWriteByte, ENCODING_ESCAPE); + } + s_callback(SmartstrapCmdWriteByte, data); +} + +static void prv_write_internal(SmartstrapProfile profile, const uint8_t *data1, size_t length1, + const uint8_t *data2, size_t length2, bool is_notify) { + uint8_t parity = 0; + + // enable tx + s_callback(SmartstrapCmdSetTxEnabled, true); + + // send flag + prv_send_flag(); + + // send version + prv_send_byte(PROTOCOL_VERSION, &parity); + + // send header flags (currently just hard-coded) + if (is_notify) { + prv_send_byte(0x04, &parity); + } else { + prv_send_byte(0, &parity); + } + prv_send_byte(0, &parity); + prv_send_byte(0, &parity); + prv_send_byte(0, &parity); + + // send profile (currently well within a single byte) + prv_send_byte(profile, &parity); + prv_send_byte(0, &parity); + + // send data + size_t i; + for (i = 0; i < length1; ++i) { + prv_send_byte(data1[i], &parity); + } + for (i = 0; i < length2; ++i) { + prv_send_byte(data2[i], &parity); + } + + // send parity + prv_send_byte(parity, &parity); + + // send flag + prv_send_flag(); + + // flush and disable tx + s_callback(SmartstrapCmdSetTxEnabled, false); +} + +static bool prv_supports_raw_data_profile(void) { + uint8_t i; + for (i = 0; i < s_num_supported_services; i++) { + if (s_supported_services[i] == 0x0000) { + return true; + } + } + return false; +} + +static bool prv_supports_generic_profile(void) { + uint8_t i; + for (i = 0; i < s_num_supported_services; i++) { + if (s_supported_services[i] > 0x0000) { + return true; + } + } + return false; +} + +static void prv_handle_link_control(uint8_t *buffer) { + // we will re-use the buffer for the response + LinkControlType type = buffer[1]; + if (type == LinkControlTypeStatus) { + if (s_current_baud != s_target_baud) { + buffer[2] = LinkControlStatusBaudRate; + } else { + buffer[2] = LinkControlStatusOk; + s_connected = true; + } + prv_write_internal(SmartstrapProfileLinkControl, buffer, 3, NULL, 0, false); + } else if (type == LinkControlTypeProfiles) { + uint16_t profiles[2]; + uint8_t num_profiles = 0; + if (prv_supports_raw_data_profile()) { + profiles[num_profiles++] = SmartstrapProfileRawData; + } + if (prv_supports_generic_profile()) { + profiles[num_profiles++] = SmartstrapProfileGenericService; + } + prv_write_internal(SmartstrapProfileLinkControl, buffer, 2, (uint8_t *)profiles, + num_profiles * sizeof(uint16_t), false); + } else if (type == LinkControlTypeBaud) { + buffer[2] = 0x05; + prv_write_internal(SmartstrapProfileLinkControl, buffer, 3, NULL, 0, false); + prv_set_baud(s_target_baud); + } +} + +static bool prv_handle_generic_service(GenericServicePayload *data) { + if (data->error != 0) { + return true; + } + + uint16_t service_id = data->service_id; + uint16_t attribute_id = data->attribute_id; + s_last_generic_service_type = data->type; + uint16_t length = data->length; + if ((service_id == 0x0101) && (attribute_id == 0x0002)) { + // notification info attribute + if (s_notify_service) { + uint16_t info[2] = {s_notify_service, s_notify_attribute}; + length = sizeof(info); + s_pending_response.can_respond = true; + s_pending_response.service_id = service_id; + s_pending_response.attribute_id = attribute_id; + pebble_write(true, (uint8_t *)&info, length); + } + return true; + } else if ((service_id == 0x0101) && (attribute_id == 0x0001)) { + // this is a service discovery frame + s_pending_response.can_respond = true; + s_pending_response.service_id = service_id; + s_pending_response.attribute_id = attribute_id; + pebble_write(true, (uint8_t *)s_supported_services, + s_num_supported_services * sizeof(uint16_t)); + return true; + } + return false; +} + +static void prv_store_byte(const uint8_t data) { + // Find which field this byte belongs to based on the number of bytes we've received so far + if (s_frame.length >= FRAME_PAYLOAD_OFFSET) { + // This byte is part of either the payload or the checksum + const uint32_t payload_length = s_frame.length - FRAME_PAYLOAD_OFFSET; + if (payload_length > s_frame.max_payload_length) { + // The payload is longer than the payload buffer so drop this byte + s_frame.should_drop = true; + } else { + // The checksum byte comes after the payload in the frame. This byte we are receiving could + // be the checksum byte, or it could be part of the payload; we don't know at this point. So, + // we will store it temporarily in footer_byte and if we've previously stored a byte there, + // will copy that previous byte into the payload. This avoids us overrunning the payload + // buffer if it's conservatively sized. + if (payload_length > 0) { + // put the previous byte into the payload buffer + s_frame.payload[payload_length - 1] = s_frame.footer_byte; + } + s_frame.footer_byte = data; + } + } else if (s_frame.length >= FRAME_PROFILE_OFFSET) { + // This byte is part of the profile field + const uint32_t byte_offset = s_frame.length - FRAME_PROFILE_OFFSET; + s_frame.header.profile |= (data << (byte_offset * 8)); + } else if (s_frame.length >= FRAME_FLAGS_OFFSET) { + // This byte is part of the flags field + const uint32_t byte_offset = s_frame.length - FRAME_FLAGS_OFFSET; + s_frame.header.flags |= (data << (byte_offset * 8)); + } else { + // The version field should always be first (and a single byte) + s_frame.header.version = data; + } + + // increment the length run the CRC calculation + s_frame.length++; + crc8_calculate_byte_streaming(data, &s_frame.checksum); +} + +static void prv_frame_validate(void) { + if ((s_frame.should_drop == false) && + (s_frame.header.version > 0) && + (s_frame.header.version <= PROTOCOL_VERSION) && + (FLAGS_GET(s_frame.header.flags, FLAGS_IS_MASTER_MASK, FLAGS_IS_MASTER_OFFSET) == 1) && + (FLAGS_GET(s_frame.header.flags, FLAGS_RESERVED_MASK, FLAGS_RESERVED_OFFSET) == 0) && + (s_frame.header.profile > SmartstrapProfileInvalid) && + (s_frame.header.profile < NumSmartstrapProfiles) && + (s_frame.length >= FRAME_MIN_LENGTH) && + (s_frame.checksum == 0)) { + // this is a valid frame + } else { + // drop the frame + s_frame.should_drop = true; + } +} + +bool pebble_handle_byte(uint8_t data, uint16_t *service_id, uint16_t *attribute_id, size_t *length, + SmartstrapRequestType *type, uint32_t time) { + if (!s_frame.read_ready) { + // we shouldn't be reading new data + return false; + } + + bool encoding_err, should_store = false; + bool is_complete = encoding_streaming_decode(&s_frame.encoding_ctx, &data, &should_store, + &encoding_err); + if (encoding_err) { + s_frame.should_drop = true; + } else if (is_complete) { + prv_frame_validate(); + } else if (should_store) { + prv_store_byte(data); + } + + if (s_frame.should_drop || is_complete) { + // prepare the encoding context for the next frame + encoding_streaming_decode_reset(&s_frame.encoding_ctx); + } + + if (is_complete) { + bool give_to_user = false; + if (s_frame.should_drop) { + // reset the frame + pebble_prepare_for_read(s_frame.payload, s_frame.max_payload_length); + } else if (s_frame.header.profile == SmartstrapProfileLinkControl) { + s_last_message_time = time; + // handle this link control frame + prv_handle_link_control(s_frame.payload); + // prepare for the next frame + pebble_prepare_for_read(s_frame.payload, s_frame.max_payload_length); + } else if (s_frame.header.profile == SmartstrapProfileGenericService) { + GenericServicePayload header = *(GenericServicePayload *)s_frame.payload; + memmove(s_frame.payload, &s_frame.payload[sizeof(header)], header.length); + // handle this generic service frame + if (prv_handle_generic_service(&header)) { + s_last_message_time = time; + // we handled it, so prepare for the next frame + pebble_prepare_for_read(s_frame.payload, s_frame.max_payload_length); + } else { + // pass up to user to handle + give_to_user = true; + *service_id = header.service_id; + *attribute_id = header.attribute_id; + *length = header.length; + *type = header.type; + } + } else { + give_to_user = true; + *service_id = 0; + *attribute_id = 0; + *length = s_frame.length - FRAME_MIN_LENGTH; + if (FLAGS_GET(s_frame.header.flags, FLAGS_IS_READ_MASK, FLAGS_IS_READ_OFFSET)) { + if (*length) { + *type = SmartstrapRequestTypeWriteRead; + } else { + *type = SmartstrapRequestTypeRead; + } + } else { + *type = SmartstrapRequestTypeWrite; + } + } + if (give_to_user) { + s_last_message_time = time; + s_frame.read_ready = false; + s_pending_response.service_id = *service_id; + s_pending_response.attribute_id = *attribute_id; + s_pending_response.can_respond = true; + return true; + } + } + + if (time < s_last_message_time) { + // wrapped around + s_last_message_time = time; + } else if (time - s_last_message_time > 10000) { + // haven't received a valid frame in over 10 seconds so reset the baudrate + prv_set_baud(PebbleBaud9600); + s_connected = false; + } + + return false; +} + +bool pebble_write(bool success, const uint8_t *buffer, uint16_t length) { + if (!s_pending_response.can_respond) { + return false; + } + if (s_pending_response.service_id == 0) { + if (s_pending_response.attribute_id != 0) { + return false; + } + prv_write_internal(SmartstrapProfileRawData, buffer, length, NULL, 0, false); + } else if (s_pending_response.service_id < 0x00FF) { + return false; + } else { + GenericServicePayload frame = (GenericServicePayload ) { + .version = GENERIC_SERVICE_VERSION, + .service_id = s_pending_response.service_id, + .attribute_id = s_pending_response.attribute_id, + .type = s_last_generic_service_type, + .error = success ? 0 : 1, + .length = length + }; + prv_write_internal(SmartstrapProfileGenericService, (uint8_t *)&frame, sizeof(frame), buffer, + length, false); + } + s_pending_response.can_respond = false; + return true; +} + +void pebble_notify(uint16_t service_id, uint16_t attribute_id) { + s_notify_service = service_id; + s_notify_attribute = attribute_id; + SmartstrapProfile profile; + if (service_id == 0) { + profile = SmartstrapProfileRawData; + } else { + profile = SmartstrapProfileGenericService; + } + s_callback(SmartstrapCmdSetTxEnabled, true); + s_callback(SmartstrapCmdWriteBreak, 0); + s_callback(SmartstrapCmdWriteBreak, 0); + s_callback(SmartstrapCmdWriteBreak, 0); + s_callback(SmartstrapCmdSetTxEnabled, false); + prv_write_internal(profile, NULL, 0, NULL, 0, true); +} + +bool pebble_is_connected(uint32_t time) { + if (time - s_last_message_time > 10000) { + prv_set_baud(PebbleBaud9600); + s_connected = false; + } + return s_connected; +}
diff -r 000000000000 -r e4dad9e53f06 PebbleSerial.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PebbleSerial.h Wed Nov 04 09:58:41 2015 +0000 @@ -0,0 +1,62 @@ +#ifndef __PEBBLE_SERIAL_H__ +#define __PEBBLE_SERIAL_H__ + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +#define PEBBLE_MIN_PAYLOAD (20 + PEBBLE_PAYLOAD_OVERHEAD) +#define PEBBLE_PAYLOAD_OVERHEAD 9 + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define GET_PAYLOAD_BUFFER_SIZE(max_data_length) \ + MAX(max_data_length + PEBBLE_PAYLOAD_OVERHEAD, PEBBLE_MIN_PAYLOAD) + +typedef enum { + SmartstrapCmdSetBaudRate, + SmartstrapCmdSetTxEnabled, + SmartstrapCmdWriteByte, + SmartstrapCmdWriteBreak +} SmartstrapCmd; + +typedef enum { + PebbleBaud9600, + PebbleBaud14400, + PebbleBaud19200, + PebbleBaud28800, + PebbleBaud38400, + PebbleBaud57600, + PebbleBaud62500, + PebbleBaud115200, + PebbleBaud125000, + PebbleBaud230400, + PebbleBaud250000, + PebbleBaud460800, + + PebbleBaudInvalid +} PebbleBaud; + +typedef enum { + SmartstrapResultOk = 0, + SmartstrapResultNotSupported +} SmartstrapResult; + +typedef enum { + SmartstrapRequestTypeRead = 0, + SmartstrapRequestTypeWrite = 1, + SmartstrapRequestTypeWriteRead = 2 +} SmartstrapRequestType; + + +typedef void (*SmartstrapCallback)(SmartstrapCmd cmd, uint32_t arg); + +void pebble_init(SmartstrapCallback callback, PebbleBaud baud, const uint16_t *services, + uint8_t num_services); +void pebble_prepare_for_read(uint8_t *buffer, size_t length); +bool pebble_handle_byte(uint8_t data, uint16_t *service_id, uint16_t *attribute_id, size_t *length, + SmartstrapRequestType *type, uint32_t time_ms); +bool pebble_write(bool success, const uint8_t *buffer, uint16_t length); +void pebble_notify(uint16_t service_id, uint16_t attribute_id); +bool pebble_is_connected(uint32_t time); + +#endif // __PEBBLE_SERIAL_H__
diff -r 000000000000 -r e4dad9e53f06 crc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crc.c Wed Nov 04 09:58:41 2015 +0000 @@ -0,0 +1,23 @@ +#include "crc.h" + +void crc8_calculate_byte_streaming(const uint8_t data, uint8_t *crc) { + // Optimal polynomial chosen based on + // http://users.ece.cmu.edu/~koopman/roses/dsn04/koopman04_crc_poly_embedded.pdf + // Note that this is different than the standard CRC-8 polynomial, because the + // standard CRC-8 polynomial is not particularly good. + + // nibble lookup table for (x^8 + x^5 + x^3 + x^2 + x + 1) + static const uint8_t lookup_table[] = + { 0, 47, 94, 113, 188, 147, 226, 205, 87, 120, 9, 38, 235, 196, + 181, 154 }; + + int i; + for (i = 2; i > 0; i--) { + uint8_t nibble = data; + if (i % 2 == 0) { + nibble >>= 4; + } + int index = nibble ^ (*crc >> 4); + *crc = lookup_table[index & 0xf] ^ (*crc << 4); + } +}
diff -r 000000000000 -r e4dad9e53f06 crc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crc.h Wed Nov 04 09:58:41 2015 +0000 @@ -0,0 +1,5 @@ +#pragma once + +#include <stdint.h> + +void crc8_calculate_byte_streaming(const uint8_t data, uint8_t *crc);
diff -r 000000000000 -r e4dad9e53f06 encoding.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/encoding.c Wed Nov 04 09:58:41 2015 +0000 @@ -0,0 +1,46 @@ +#include "encoding.h" + +void encoding_streaming_decode_reset(EncodingStreamingContext *ctx) { + ctx->escape = false; +} + +bool encoding_streaming_decode(EncodingStreamingContext *ctx, uint8_t *data, bool *should_store, + bool *encoding_error) { + bool is_complete = false; + *encoding_error = false; + *should_store = false; + if (*data == ENCODING_FLAG) { + if (ctx->escape) { + // extra escape character before flag + ctx->escape = false; + *encoding_error = true; + } + // we've reached the end of the frame + is_complete = true; + } else if (*data == ENCODING_ESCAPE) { + if (ctx->escape) { + // invalid sequence + ctx->escape = false; + *encoding_error = true; + } else { + // ignore this character and escape the next one + ctx->escape = true; + } + } else { + if (ctx->escape) { + *data ^= ENCODING_ESCAPE_MASK; + ctx->escape = false; + } + *should_store = true; + } + + return is_complete; +} + +bool encoding_encode(uint8_t *data) { + if (*data == ENCODING_FLAG || *data == ENCODING_ESCAPE) { + *data ^= ENCODING_ESCAPE_MASK; + return true; + } + return false; +}
diff -r 000000000000 -r e4dad9e53f06 encoding.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/encoding.h Wed Nov 04 09:58:41 2015 +0000 @@ -0,0 +1,17 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +static const uint8_t ENCODING_FLAG = 0x7E; +static const uint8_t ENCODING_ESCAPE = 0x7D; +static const uint8_t ENCODING_ESCAPE_MASK = 0x20; + +typedef struct { + bool escape; +} EncodingStreamingContext; + +void encoding_streaming_decode_reset(EncodingStreamingContext *ctx); +bool encoding_streaming_decode(EncodingStreamingContext *ctx, uint8_t *data, bool *complete, + bool *is_invalid); +bool encoding_encode(uint8_t *data);
diff -r 000000000000 -r e4dad9e53f06 mbedPebbleSerial.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbedPebbleSerial.cpp Wed Nov 04 09:58:41 2015 +0000 @@ -0,0 +1,141 @@ +/* + * This is an Arduino library wrapper around the PebbleSerial library. + */ +#include "mbed.h" +#include "BufferedSerial.h" +#include "mbedPebbleSerial.h" + +static uint8_t *s_buffer; +static size_t s_buffer_length; +static uint8_t s_pin; + +extern BufferedSerial serial; + +#define _BV(bit) (1<<(bit)) +#define cbi(sfr, bit) ((sfr) &= ~_BV(bit)) +#define sbi(sfr, bit) ((sfr) |= _BV(bit)) + + +static void board_begin(void) { +} + +static void board_set_tx_enabled(bool enabled) { + if (enabled) { + // enable transmitter + sbi(LPC_USART->TER, 7); + // disable receiver + sbi(LPC_USART->RS485CTRL, 1); + } else { + // disable transmitter + cbi(LPC_USART->TER, 7); + // enable receiver + cbi(LPC_USART->RS485CTRL, 1); + } +} + +static void board_set_even_parity(bool enabled) { + if (enabled) { + sbi(LPC_USART->LCR, 3); + sbi(LPC_USART->LCR, 4); + } else { + cbi(LPC_USART->LCR, 3); + cbi(LPC_USART->LCR, 4); + } +} + +static void board_flush_uart() +{ + while (serial._txbuf.available()); + while (!(LPC_USART->LSR & _BV(5))); +} + +static void prv_cmd_cb(SmartstrapCmd cmd, uint32_t arg) { + switch (cmd) { + case SmartstrapCmdSetBaudRate: + serial.baud(arg); + break; + case SmartstrapCmdSetTxEnabled: + if (!arg) board_flush_uart(); + board_set_tx_enabled(arg); + break; + case SmartstrapCmdWriteByte: + serial.putc((uint8_t)arg); + break; + case SmartstrapCmdWriteBreak: + board_set_even_parity(true); + serial.putc((uint8_t)0); + // need to flush before changing parity + board_flush_uart(); + board_set_even_parity(false); + break; + default: + break; + } +} + +static void prv_begin(uint8_t *buffer, size_t length, Baud baud, + const uint16_t *services, uint8_t num_services) { + s_buffer = buffer; + s_buffer_length = length; + + pebble_init(prv_cmd_cb, (PebbleBaud)baud, services, num_services); + pebble_prepare_for_read(s_buffer, s_buffer_length); +} + +void mbedPebbleSerial::begin(uint8_t *buffer, size_t length, Baud baud, + const uint16_t *services, uint8_t num_services) { + prv_begin(buffer, length, baud, services, num_services); +} + +static int prv_available_bytes(void) { + return serial.readable(); +} + +static uint8_t prv_read_byte(void) { + return (uint8_t)serial.getc(); +} + +bool mbedPebbleSerial::feed(uint16_t *service_id, uint16_t *attribute_id, size_t *length, + RequestType *type) { + SmartstrapRequestType request_type; + bool did_feed = false; + while (prv_available_bytes()) { + did_feed = true; + if (pebble_handle_byte(prv_read_byte(), service_id, attribute_id, length, &request_type, us_ticker_read()/1000)) { + // we have a full frame + pebble_prepare_for_read(s_buffer, s_buffer_length); + switch (request_type) { + case SmartstrapRequestTypeRead: + *type = RequestTypeRead; + break; + case SmartstrapRequestTypeWrite: + *type = RequestTypeWrite; + break; + case SmartstrapRequestTypeWriteRead: + *type = RequestTypeWriteRead; + break; + default: + break; + } + return true; + } + } + + if (!did_feed) { + // allow the pebble code to dicsonnect if we haven't gotten any messages recently + pebble_is_connected(us_ticker_read()/1000); + } + return false; +} + +bool mbedPebbleSerial::write(bool success, const uint8_t *payload, size_t length) { + return pebble_write(success, payload, length); +} + +void mbedPebbleSerial::notify(uint16_t service_id, uint16_t attribute_id) { + pebble_notify(service_id, attribute_id); +} + +bool mbedPebbleSerial::is_connected(void) { + return pebble_is_connected(us_ticker_read()/1000); +}
diff -r 000000000000 -r e4dad9e53f06 mbedPebbleSerial.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbedPebbleSerial.h Wed Nov 04 09:58:41 2015 +0000 @@ -0,0 +1,44 @@ +/* + * This is an Arduino library wrapper around the PebbleSerial library. + */ +#ifndef __ARDUINO_PEBBLE_SERIAL_H__ +#define __ARDUINO_PEBBLE_SERIAL_H__ + +#define USE_HARDWARE_SERIAL 0 + +#include "mbed.h" +extern "C" { +#include "PebbleSerial.h" +}; + +typedef enum { + Baud9600, + Baud14400, + Baud19200, + Baud28800, + Baud38400, + Baud57600, + Baud62500, + Baud115200, + Baud125000, + Baud230400, + Baud250000, + Baud460800, +} Baud; + +typedef enum { + RequestTypeRead, + RequestTypeWrite, + RequestTypeWriteRead +} RequestType; + +class mbedPebbleSerial { +public: + static void begin(uint8_t *buffer, size_t length, Baud baud, const uint16_t *services, uint8_t num_services); + static bool feed(uint16_t *service_id, uint16_t *attribute_id, size_t *length, RequestType *type); + static bool write(bool success, const uint8_t *payload, size_t length); + static void notify(uint16_t service_id, uint16_t attribute_id); + static bool is_connected(void); +}; + +#endif //__ARDUINO_PEBBLE_SERIAL_H__