diff --git a/.vscode/settings.json b/.vscode/settings.json index 3bea27a..3ae5abb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "files.associations": { "stm32l0xx_hal.h": "c", - "lmic.h": "c" + "lmic.h": "c", + "main.h": "c" } } \ No newline at end of file diff --git a/extra_script.py b/extra_script.py new file mode 100644 index 0000000..0ac04d8 --- /dev/null +++ b/extra_script.py @@ -0,0 +1,12 @@ +Import("env") + +# +# Dump build environment (for debug) +# print(env.Dump()) +# + +env.Append( + LINKFLAGS=[ + "-u _printf_float" + ] +) \ No newline at end of file diff --git a/include/circular_buffer.h b/include/circular_buffer.h new file mode 100644 index 0000000..45d76bf --- /dev/null +++ b/include/circular_buffer.h @@ -0,0 +1,76 @@ +#ifndef CIRCULAR_BUFFER_H_ +#define CIRCULAR_BUFFER_H_ + +#include +#include + +/// Opaque circular buffer structure +typedef struct circular_buf_t circular_buf_t; + +/// Handle type, the way users interact with the API +typedef circular_buf_t* cbuf_handle_t; + +/// Pass in a storage buffer and size, returns a circular buffer handle +/// Requires: buffer is not NULL, size > 0 (size > 1 for the threadsafe +// version, because it holds size - 1 elements) +/// Ensures: me has been created and is returned in an empty state +cbuf_handle_t circular_buf_init(uint8_t* buffer, size_t size); + +/// Free a circular buffer structure +/// Requires: me is valid and created by circular_buf_init +/// Does not free data buffer; owner is responsible for that +void circular_buf_free(cbuf_handle_t me); + +/// Reset the circular buffer to empty, head == tail. Data not cleared +/// Requires: me is valid and created by circular_buf_init +void circular_buf_reset(cbuf_handle_t me); + +/// Put that continues to add data if the buffer is full +/// Old data is overwritten +/// Note: if you are using the threadsafe version, this API cannot be used, because +/// it modifies the tail pointer in some cases. Use circular_buf_try_put instead. +/// Requires: me is valid and created by circular_buf_init +void circular_buf_put(cbuf_handle_t me, uint8_t data); + +/// Put that rejects new data if the buffer is full +/// Note: if you are using the threadsafe version, *this* is the put you should use +/// Requires: me is valid and created by circular_buf_init +/// Returns 0 on success, -1 if buffer is full +int circular_buf_try_put(cbuf_handle_t me, uint8_t data); + +/// Retrieve a value from the buffer +/// Requires: me is valid and created by circular_buf_init +/// Returns 0 on success, -1 if the buffer is empty +int circular_buf_get(cbuf_handle_t me, uint8_t* data); + +/// CHecks if the buffer is empty +/// Requires: me is valid and created by circular_buf_init +/// Returns true if the buffer is empty +bool circular_buf_empty(cbuf_handle_t me); + +/// Checks if the buffer is full +/// Requires: me is valid and created by circular_buf_init +/// Returns true if the buffer is full +bool circular_buf_full(cbuf_handle_t me); + +/// Check the capacity of the buffer +/// Requires: me is valid and created by circular_buf_init +/// Returns the maximum capacity of the buffer +size_t circular_buf_capacity(cbuf_handle_t me); + +/// Check the number of elements stored in the buffer +/// Requires: me is valid and created by circular_buf_init +/// Returns the current number of elements in the buffer +size_t circular_buf_size(cbuf_handle_t me); + +/// Look ahead at values stored in the circular buffer without removing the data +/// Requires: +/// - me is valid and created by circular_buf_init +/// - look_ahead_counter is less than or equal to the value returned by circular_buf_size() +/// Returns 0 if successful, -1 if data is not available +int circular_buf_peek(cbuf_handle_t me, uint8_t* data, unsigned int look_ahead_counter); + +// TODO: int circular_buf_get_range(circular_buf_t me, uint8_t *data, size_t len); +// TODO: int circular_buf_put_range(circular_buf_t me, uint8_t * data, size_t len); + +#endif // CIRCULAR_BUFFER_H_ \ No newline at end of file diff --git a/include/sml.h b/include/sml.h new file mode 100644 index 0000000..defa674 --- /dev/null +++ b/include/sml.h @@ -0,0 +1,109 @@ +#ifndef SML_H +#define SML_H + +#include + +typedef enum { + SML_START, + SML_END, + SML_VERSION, + SML_NEXT, + SML_LISTSTART, + SML_LISTEND, + SML_LISTEXTENDED, + SML_DATA, + SML_HDATA, + SML_DATAEND, + SML_BLOCKSTART, + SML_BLOCKEND, + SML_CHECKSUM, + SML_CHECKSUM_ERROR, /* calculated checksum does not match */ + SML_UNEXPECTED, /* unexpected byte received */ + SML_FINAL, /* final state, checksum OK */ + SML_DATA_SIGNED_INT, + SML_DATA_UNSIGNED_INT, + SML_DATA_OCTET_STRING, +} sml_states_t; + +typedef enum { + SML_YEAR = 1, + SML_MONTH = 2, + SML_WEEK = 3, + SML_DAY = 4, + SML_HOUR = 5, + SML_MIN = 6, + SML_SECOND = 7, + SML_DEGREE = 8, + SML_DEGREE_CELSIUS = 9, + SML_CURRENCY = 10, + SML_METRE = 11, + SML_METRE_PER_SECOND = 12, + SML_CUBIC_METRE = 13, + SML_CUBIC_METRE_CORRECTED = 14, + SML_CUBIC_METRE_PER_HOUR = 15, + SML_CUBIC_METRE_PER_HOUR_CORRECTED = 16, + SML_CUBIC_METRE_PER_DAY = 17, + SML_CUBIC_METRE_PER_DAY_CORRECTED = 18, + SML_LITRE = 19, + SML_KILOGRAM = 20, + SML_NEWTON = 21, + SML_NEWTONMETER = 22, + SML_PASCAL = 23, + SML_BAR = 24, + SML_JOULE = 25, + SML_JOULE_PER_HOUR = 26, + SML_WATT = 27, + SML_VOLT_AMPERE = 28, + SML_VAR = 29, + SML_WATT_HOUR = 30, + SML_VOLT_AMPERE_HOUR = 31, + SML_VAR_HOUR = 32, + SML_AMPERE = 33, + SML_COULOMB = 34, + SML_VOLT = 35, + SML_VOLT_PER_METRE = 36, + SML_FARAD = 37, + SML_OHM = 38, + SML_OHM_METRE = 39, + SML_WEBER = 40, + SML_TESLA = 41, + SML_AMPERE_PER_METRE = 42, + SML_HENRY = 43, + SML_HERTZ = 44, + SML_ACTIVE_ENERGY_METER_CONSTANT_OR_PULSE_VALUE = 45, + SML_REACTIVE_ENERGY_METER_CONSTANT_OR_PULSE_VALUE = 46, + SML_APPARENT_ENERGY_METER_CONSTANT_OR_PULSE_VALUE = 47, + SML_VOLT_SQUARED_HOURS = 48, + SML_AMPERE_SQUARED_HOURS = 49, + SML_KILOGRAM_PER_SECOND = 50, + SML_KELVIN = 52, + SML_VOLT_SQUARED_HOUR_METER_CONSTANT_OR_PULSE_VALUE = 53, + SML_AMPERE_SQUARED_HOUR_METER_CONSTANT_OR_PULSE_VALUE = 54, + SML_METER_CONSTANT_OR_PULSE_VALUE = 55, + SML_PERCENTAGE = 56, + SML_AMPERE_HOUR = 57, + SML_ENERGY_PER_VOLUME = 60, + SML_CALORIFIC_VALUE = 61, + SML_MOLE_PERCENT = 62, + SML_MASS_DENSITY = 63, + SML_PASCAL_SECOND = 64, + SML_RESERVED = 253, + SML_OTHER_UNIT = 254, + SML_COUNT = 255 +} sml_units_t; + +sml_states_t smlState(unsigned char *byte); +bool smlOBISCheck(const unsigned char *obis); +void smlOBISManufacturer(unsigned char *str, int maxSize); +void smlOBISByUnit(long long int *wh, signed char *scaler, sml_units_t unit); + +// Be aware that double on Arduino UNO is just 32 bit +void smlOBISWh(double *wh); +void smlOBISW(double *w); +void smlOBISVolt(double *v); +void smlOBISAmpere(double *a); +void smlOBISHertz(double *h); +void smlOBISDegree(double *d); + + +#endif diff --git a/include/smlCrcTable.h b/include/smlCrcTable.h new file mode 100644 index 0000000..f49344e --- /dev/null +++ b/include/smlCrcTable.h @@ -0,0 +1,37 @@ +#ifndef SML_CRC_TABLE_H +#define SML_CRC_TABLE_H + +#include + +static const uint16_t smlCrcTable[256] = + {0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, 0x8C48, + 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, 0x1081, 0x0108, + 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, 0x9CC9, 0x8D40, 0xBFDB, + 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, 0x2102, 0x308B, 0x0210, 0x1399, + 0x6726, 0x76AF, 0x4434, 0x55BD, 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, + 0xFAE7, 0xC87C, 0xD9F5, 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, + 0x54B5, 0x453C, 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, + 0xC974, 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, + 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, 0x5285, + 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, 0xDECD, 0xCF44, + 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, 0x6306, 0x728F, 0x4014, + 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, + 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, + 0x242A, 0x16B1, 0x0738, 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, + 0x9AF9, 0x8B70, 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, + 0xF0B7, 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, + 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, 0x18C1, + 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, 0xA50A, 0xB483, + 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, 0x2942, 0x38CB, 0x0A50, + 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, 0xB58B, 0xA402, 0x9699, 0x8710, + 0xF3AF, 0xE226, 0xD0BD, 0xC134, 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, + 0x6E6E, 0x5CF5, 0x4D7C, 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, + 0xA33A, 0xB2B3, 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, + 0x3EFB, 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, + 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, 0xE70E, + 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, 0x6B46, 0x7ACF, + 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, 0xF78F, 0xE606, 0xD49D, + 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, 0x7BC7, 0x6A4E, 0x58D5, 0x495C, + 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78}; + +#endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index fcc81d7..ecee515 100755 --- a/platformio.ini +++ b/platformio.ini @@ -17,6 +17,9 @@ build_flags= -D CFG_eu868 -D CFG_sx1276_radio -D DISABLE_JOIN +; -mfloat-abi=soft + -Wl,-u_printf_float +; extra_scripts = extra_script.py upload_protocol=custom upload_command = st-flash write $SOURCE 0x8000000 debug_tool=stlink diff --git a/src/circular_buffer.c b/src/circular_buffer.c new file mode 100644 index 0000000..9a3461f --- /dev/null +++ b/src/circular_buffer.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include + +#include "circular_buffer.h" + +// The definition of our circular buffer structure is hidden from the user +struct circular_buf_t +{ + uint8_t* buffer; + size_t head; + size_t tail; + size_t max; // of the buffer + bool full; +}; + +#pragma mark - Private Functions - + +static inline size_t advance_headtail_value(size_t value, size_t max) +{ + return (value + 1) % max; +} + +static void advance_head_pointer(cbuf_handle_t me) +{ + assert(me); + + if(circular_buf_full(me)) + { + me->tail = advance_headtail_value(me->tail, me->max); + } + + me->head = advance_headtail_value(me->head, me->max); + me->full = (me->head == me->tail); +} + +#pragma mark - APIs - + +cbuf_handle_t circular_buf_init(uint8_t* buffer, size_t size) +{ + assert(buffer && size); + + cbuf_handle_t cbuf = malloc(sizeof(circular_buf_t)); + assert(cbuf); + + cbuf->buffer = buffer; + cbuf->max = size; + circular_buf_reset(cbuf); + + assert(circular_buf_empty(cbuf)); + + return cbuf; +} + +void circular_buf_free(cbuf_handle_t me) +{ + assert(me); + free(me); +} + +void circular_buf_reset(cbuf_handle_t me) +{ + assert(me); + + me->head = 0; + me->tail = 0; + me->full = false; +} + +size_t circular_buf_size(cbuf_handle_t me) +{ + assert(me); + + size_t size = me->max; + + if(!circular_buf_full(me)) + { + if(me->head >= me->tail) + { + size = (me->head - me->tail); + } + else + { + size = (me->max + me->head - me->tail); + } + } + + return size; +} + +size_t circular_buf_capacity(cbuf_handle_t me) +{ + assert(me); + + return me->max; +} + +void circular_buf_put(cbuf_handle_t me, uint8_t data) +{ + assert(me && me->buffer); + + me->buffer[me->head] = data; + + advance_head_pointer(me); +} + +int circular_buf_try_put(cbuf_handle_t me, uint8_t data) +{ + int r = -1; + + assert(me && me->buffer); + + if(!circular_buf_full(me)) + { + me->buffer[me->head] = data; + advance_head_pointer(me); + r = 0; + } + + return r; +} + +int circular_buf_get(cbuf_handle_t me, uint8_t* data) +{ + assert(me && data && me->buffer); + + int r = -1; + + if(!circular_buf_empty(me)) + { + *data = me->buffer[me->tail]; + me->tail = advance_headtail_value(me->tail, me->max); + me->full = false; + r = 0; + } + + return r; +} + +bool circular_buf_empty(cbuf_handle_t me) +{ + assert(me); + + return (!circular_buf_full(me) && (me->head == me->tail)); +} + +bool circular_buf_full(cbuf_handle_t me) +{ + assert(me); + + return me->full; +} + +int circular_buf_peek(cbuf_handle_t me, uint8_t* data, unsigned int look_ahead_counter) +{ + int r = -1; + size_t pos; + + assert(me && data && me->buffer); + + // We can't look beyond the current buffer size + if(circular_buf_empty(me) || look_ahead_counter > circular_buf_size(me)) + { + return r; + } + + pos = me->tail; + for(unsigned int i = 0; i < look_ahead_counter; i++) + { + data[i] = me->buffer[pos]; + pos = advance_headtail_value(pos, me->max); + } + + return 0; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c index 0f6de59..0a564bf 100644 --- a/src/main.c +++ b/src/main.c @@ -17,12 +17,20 @@ */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ +#include +#include + #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "lmic.h" #include "debug.h" +// include security credentials OTAA, check secconfig_example.h for more information +#include "secconfig.h" + +#include "sml.h" +#include "circular_buffer.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -33,6 +41,7 @@ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ +#define BUFFER_SIZE 1024 /* USER CODE END PD */ @@ -60,9 +69,147 @@ static void MX_RTC_Init(void); /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ +const u1_t test_sml_frame[] = { 0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x76, + 0x05, 0x98, 0xE0, 0xCE, 0x95, 0x62, 0x00, 0x62, 0x00, + 0x72, 0x63, 0x01, 0x01, 0x76, 0x01, 0x01, 0x08, 0x48, + 0x61, 0x67, 0x08, 0x42, 0x19, 0xAD, 0x08, 0x31, 0x30, + 0x30, 0x31, 0x31, 0x38, 0x35, 0x01, 0x01, 0x63, 0x5B, + 0x20, 0x00, 0x76, 0x05, 0x98, 0xE0, 0xCE, 0x96, 0x62, + 0x00, 0x62, 0x00, 0x72, 0x63, 0x07, 0x01, 0x77, 0x01, + 0x08, 0x31, 0x30, 0x30, 0x31, 0x31, 0x38, 0x35, 0x01, + 0x01, 0x75, 0x77, 0x07, 0x81, 0x81, 0xC7, 0x82, 0x03, + 0xFF, 0x01, 0x01, 0x01, 0x01, 0x06, 0x48, 0x41, 0x47, + 0x45, 0x52, 0x01, 0x77, 0x07, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x08, 0x31, 0x30, + 0x30, 0x31, 0x31, 0x38, 0x35, 0x01, 0x77, 0x07, 0x01, + 0x00, 0x02, 0x08, 0x01, 0xFF, 0x62, 0x82, 0x01, 0x62, + 0x1E, 0x52, 0xFF, 0x69, 0x00, 0x00, 0x00, 0x00, 0x41, + 0xC4, 0x98, 0x8F, 0x01, 0x77, 0x07, 0x00, 0x00, 0x60, + 0x01, 0xFF, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x0B, 0x30, + 0x30, 0x30, 0x30, 0x31, 0x31, 0x36, 0x39, 0x31, 0x37, + 0x01, 0x77, 0x07, 0x01, 0x00, 0x01, 0x07, 0x01, 0xFF, + 0x62, 0x82, 0x01, 0x62, 0x1B, 0x52, 0xFC, 0x55, 0xFC, + 0xA4, 0x98, 0x84, 0x01, 0x01, 0x01, 0x63, 0xFE, 0xDB, + 0x00, 0x76, 0x05, 0x98, 0xE0, 0xCE, 0x97, 0x62, 0x00, + 0x62, 0x00, 0x72, 0x63, 0x02, 0x01, 0x71, 0x01, 0x63, + 0x1B, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x1B, 0x1B, 0x1B, + 0x1A, 0x02, 0xDB, 0x3A }; + +double T1Wh = -2, SumWh = -2; + +typedef struct { + const unsigned char OBIS[6]; + void (*Handler)(); +} OBISHandler; + + +void PowerT1() { smlOBISWh(&T1Wh); } + +void PowerSum() { smlOBISWh(&SumWh); } + +// clang-format off +OBISHandler OBISHandlers[] = { + {{ 0x01, 0x00, 0x01, 0x08, 0x01, 0xff }, &PowerT1}, /* 1- 0: 1. 8.1*255 (T1) */ + {{ 0x01, 0x00, 0x01, 0x08, 0x00, 0xff }, &PowerSum}, /* 1- 0: 1. 8.0*255 (T1 + T2) */ + {{ 0, 0 }} +}; +// clang-format on + +sml_states_t currentState; + +uint8_t * buffer; +cbuf_handle_t buffer_handle; + +// void print_buffer(){ + +// unsigned int i = 0; +// unsigned int j = 0; +// char b[5]; +// Serial.print(F("Size: ")); +// Serial.print(myBuffer.size()); +// Serial.println(""); +// Serial.println(F("--- ")); +// for (j = 0; j < myBuffer.size(); j++) { +// i++; +// sprintf(b, "0x%02X", myBuffer[j]); +// Serial.print(b); +// if (j < myBuffer.size() - 1) { +// Serial.print(", "); +// } +// else { +// Serial.println(""); +// Serial.println(F("--- ")); +// } +// if ((i % 15) == 0) { +// Serial.println(""); +// i = 0; +// } +// } +// } + +void readByte(unsigned char *currentChar) +{ + char floatBuffer[20] = "Hallo Welt !!"; + unsigned int i = 0, iHandler = 0; + // Serial.printf("%02x",currentChar); + currentState = smlState(currentChar); + if (currentState == SML_START) { + circular_buf_reset(buffer_handle); + circular_buf_put(buffer_handle, 0x1B); + circular_buf_put(buffer_handle, 0x1B); + circular_buf_put(buffer_handle, 0x1B); + /* reset local vars */ + T1Wh = -3; + SumWh = -3; + } + else { + if ( circular_buf_size(buffer_handle) < BUFFER_SIZE) { + circular_buf_put(buffer_handle, *currentChar); + } + else { + debug_str(">>> Message larger than MAX_BUF_SIZE\n"); + } + } + if(currentState == SML_LISTEND) { + /* check handlers on last received list */ + for (iHandler = 0; OBISHandlers[iHandler].Handler != 0 && + !(smlOBISCheck(OBISHandlers[iHandler].OBIS)); + iHandler++) + ; + if (OBISHandlers[iHandler].Handler != 0) { + OBISHandlers[iHandler].Handler(); + } + } + if (currentState == SML_UNEXPECTED) { + debug_str(">>> Unexpected byte\n"); + } + if (currentState == SML_FINAL) { + debug_str(">>> Successfully received a complete message!\n"); + // print_buffer(); + + debug_str("\n"); + + debug_str("Power T1 (1-0:1.8.1)..: "); + sprintf(floatBuffer, " %10.3f", T1Wh ); + // dtostrf(T1Wh, 10, 3, floatBuffer); + // for(int i = 0; i < strlen(floatBuffer); i++){ + // debug_char(floatBuffer[i]); + // }; + debug_str(floatBuffer); + debug_str("\n"); + + debug_str("Power T1+T2 (1-0:1.8.0)..: "); + sprintf(floatBuffer, " %10.3f", SumWh ); + // dtostrf(SumWh, 10, 3, floatBuffer); + debug_str(floatBuffer); + debug_str("\n\n\n\n"); + } + if (currentState == SML_CHECKSUM_ERROR) { + debug_str(">>> Checksum error.\n"); + } +} + -// include security credentials OTAA, check secconfig_example.h for more information -#include "secconfig.h" #ifdef DISABLE_JOIN // These callbacks are only used in over-the-air activation, so they are @@ -127,6 +274,13 @@ void initfunc (osjob_t* j) { u2_t readsensor(){ u2_t value = 0xDF; /// read from evrything ...make your own sensor + + + for(int i = 0; i < sizeof(test_sml_frame); i++){ + readByte( &test_sml_frame[i] ); + }; + + return value; } @@ -261,6 +415,9 @@ int main(void) HAL_TIM_Base_Start_IT(&htim2); // <----------- change to your setup __HAL_SPI_ENABLE(&hspi1); // <----------- change to your setup + buffer = malloc(BUFFER_SIZE * sizeof(uint8_t)); + buffer_handle = circular_buf_init(buffer, BUFFER_SIZE); + osjob_t initjob; // initialize runtime env diff --git a/src/sml.c b/src/sml.c new file mode 100644 index 0000000..710aafc --- /dev/null +++ b/src/sml.c @@ -0,0 +1,421 @@ +#include +#include + +#include "sml.h" +#include "smlCrcTable.h" + +#ifdef SML_DEBUG +char logBuff[200]; + +#ifdef SML_NATIVE +#define SML_LOG(...) \ + do { \ + printf(__VA_ARGS__); \ + } while (0) +#define SML_TREELOG(level, ...) \ + do { \ + printf("%.*s", level, " "); \ + printf(__VA_ARGS__); \ + } while (0) +#elif ARDUINO +#include +#define SML_LOG(...) \ + do { \ + sprintf(logBuff, __VA_ARGS__); \ + Serial.print(logBuff); \ + } while (0) +#define SML_TREELOG(level, ...) \ + do { \ + sprintf(logBuff, __VA_ARGS__); \ + Serial.print(logBuff); \ + } while (0) +#endif + +#else +#define SML_LOG(...) \ + do { \ + } while (0) +#define SML_TREELOG(level, ...) \ + do { \ + } while (0) +#endif + +#define MAX_LIST_SIZE 80 +#define MAX_TREE_SIZE 10 + +static sml_states_t currentState = SML_START; +static char nodes[MAX_TREE_SIZE]; +static unsigned char currentLevel = 0; +static unsigned short crc = 0xFFFF; +static signed char sc; +static unsigned short crcMine = 0xFFFF; +static unsigned short crcReceived = 0x0000; +static unsigned char len = 4; +static unsigned char listBuffer[MAX_LIST_SIZE]; /* keeps a list + as length + state + data */ +static unsigned char listPos = 0; + +void crc16(unsigned char *byte) +{ +#ifdef ARDUINO + crc = + pgm_read_word_near(&smlCrcTable[(*byte ^ crc) & 0xff]) ^ (crc >> 8 & 0xff); +#else + crc = smlCrcTable[(*byte ^ crc) & 0xff] ^ (crc >> 8 & 0xff); +#endif +} + +void setState(sml_states_t state, int byteLen) +{ + currentState = state; + len = byteLen; +} + +void pushListBuffer(unsigned char byte) +{ + if (listPos < MAX_LIST_SIZE) { + listBuffer[listPos++] = byte; + } +} + +void reduceList() +{ + if (currentLevel >= 0 && nodes[currentLevel] > 0) + nodes[currentLevel]--; +} + +void smlNewList(unsigned char size) +{ + reduceList(); + if (currentLevel < MAX_TREE_SIZE) + currentLevel++; + nodes[currentLevel] = size; + SML_TREELOG(currentLevel, "LISTSTART on level %i with %i nodes\n", + currentLevel, size); + setState(SML_LISTSTART, size); + // @todo workaround for lists inside obis lists + if (size > 5) { + listPos = 0; + memset(listBuffer, '\0', MAX_LIST_SIZE); + } + else { + pushListBuffer(size); + pushListBuffer(currentState); + } +} + +void checkMagicByte(unsigned char *byte) +{ + unsigned int size = 0; + while (currentLevel > 0 && nodes[currentLevel] == 0) { + /* go back in tree if no nodes remaining */ + SML_TREELOG(currentLevel, "back to previous list\n"); + currentLevel--; + } + if (*byte > 0x70 && *byte <= 0x7F) { + /* new list */ + size = *byte & 0x0F; + smlNewList(size); + } + else if (*byte >= 0x01 && *byte <= 0x6F && nodes[currentLevel] > 0) { + if (*byte == 0x01) { + /* no data, get next */ + SML_TREELOG(currentLevel, " Data %i (empty)\n", nodes[currentLevel]); + pushListBuffer(0); + pushListBuffer(currentState); + if (nodes[currentLevel] == 1) { + setState(SML_LISTEND, 1); + SML_TREELOG(currentLevel, "LISTEND\n"); + } + else { + setState(SML_NEXT, 1); + } + } + else { + size = (*byte & 0x0F) - 1; + setState(SML_DATA, size); + if ((*byte & 0xF0) == 0x50) { + setState(SML_DATA_SIGNED_INT, size); + } + else if ((*byte & 0xF0) == 0x60) { + setState(SML_DATA_UNSIGNED_INT, size); + } + else if ((*byte & 0xF0) == 0x00) { + setState(SML_DATA_OCTET_STRING, size); + } + SML_TREELOG(currentLevel, + " Data %i (length = %i%s): ", nodes[currentLevel], size, + (currentState == SML_DATA_SIGNED_INT) ? ", signed int" + : (currentState == SML_DATA_UNSIGNED_INT) ? ", unsigned int" + : (currentState == SML_DATA_OCTET_STRING) ? ", octet string" + : ""); + pushListBuffer(size); + pushListBuffer(currentState); + } + reduceList(); + } + else if (*byte == 0x00) { + /* end of block */ + reduceList(); + SML_TREELOG(currentLevel, "End of block at level %i\n", currentLevel); + if (currentLevel == 0) { + setState(SML_NEXT, 1); + } + else { + setState(SML_BLOCKEND, 1); + } + } + else if (*byte & 0x80) { + // MSB bit is set, another TL byte will follow + if (*byte >= 0x80 && *byte <= 0x8F) { + // Datatype Octet String + setState(SML_HDATA, (*byte & 0x0F) << 4); + } + else if (*byte >= 0xF0 && *byte <= 0xFF) { + /* Datatype List of ...*/ + setState(SML_LISTEXTENDED, (*byte & 0x0F) << 4); + } + } + else if (*byte == 0x1B && currentLevel == 0) { + /* end sequence */ + setState(SML_END, 3); + } + else { + /* Unexpected Byte */ + SML_TREELOG(currentLevel, + "UNEXPECTED magicbyte >%02X< at currentLevel %i\n", *byte, + currentLevel); + setState(SML_UNEXPECTED, 4); + } +} + +sml_states_t smlState(unsigned char *currentByte) +{ + unsigned char size; + if (len > 0) + len--; + crc16(currentByte); + switch (currentState) { + case SML_UNEXPECTED: + case SML_CHECKSUM_ERROR: + case SML_FINAL: + case SML_START: + currentState = SML_START; + currentLevel = 0; // Reset current level at the begin of a new transmission + // to prevent problems + if (*currentByte != 0x1b) + setState(SML_UNEXPECTED, 4); + if (len == 0) { + SML_TREELOG(0, "START\n"); + /* completely clean any garbage from crc checksum */ + crc = 0xFFFF; + *currentByte = 0x1b; + crc16(currentByte); + crc16(currentByte); + crc16(currentByte); + crc16(currentByte); + setState(SML_VERSION, 4); + } + break; + case SML_VERSION: + if (*currentByte != 0x01) + setState(SML_UNEXPECTED, 4); + if (len == 0) { + setState(SML_BLOCKSTART, 1); + } + break; + case SML_END: + if (*currentByte != 0x1b) { + SML_LOG("UNEXPECTED char >%02X< at SML_END\n", *currentByte); + setState(SML_UNEXPECTED, 4); + } + if (len == 0) { + setState(SML_CHECKSUM, 4); + } + break; + case SML_CHECKSUM: + // SML_LOG("CHECK: %02X\n", currentByte); + if (len == 2) { + crcMine = crc ^ 0xFFFF; + } + if (len == 1) { + crcReceived += *currentByte; + } + if (len == 0) { + crcReceived = crcReceived | (*currentByte << 8); + SML_LOG("Received checksum: %02X\n", crcReceived); + SML_LOG("Calculated checksum: %02X\n", crcMine); + if (crcMine == crcReceived) { + setState(SML_FINAL, 4); + } + else { + setState(SML_CHECKSUM_ERROR, 4); + } + crc = 0xFFFF; + crcReceived = 0x000; /* reset CRC */ + } + break; + case SML_HDATA: + size = len + *currentByte - 1; + setState(SML_DATA, size); + pushListBuffer(size); + pushListBuffer(currentState); + SML_TREELOG(currentLevel, " Data (length = %i): ", size); + break; + case SML_LISTEXTENDED: + size = len + (*currentByte & 0x0F); + SML_TREELOG(currentLevel, "Extended List with Size=%i\n", size); + smlNewList(size); + break; + case SML_DATA: + case SML_DATA_SIGNED_INT: + case SML_DATA_UNSIGNED_INT: + case SML_DATA_OCTET_STRING: + SML_LOG("%02X ", *currentByte); + pushListBuffer(*currentByte); + if (nodes[currentLevel] == 0 && len == 0) { + SML_LOG("\n"); + SML_TREELOG(currentLevel, "LISTEND on level %i\n", currentLevel); + currentState = SML_LISTEND; + } + else if (len == 0) { + currentState = SML_DATAEND; + SML_LOG("\n"); + } + break; + case SML_DATAEND: + case SML_NEXT: + case SML_LISTSTART: + case SML_LISTEND: + case SML_BLOCKSTART: + case SML_BLOCKEND: + checkMagicByte(currentByte); + break; + } + return currentState; +} + +bool smlOBISCheck(const unsigned char *obis) +{ + return (memcmp(obis, &listBuffer[2], 6) == 0); +} + +void smlOBISManufacturer(unsigned char *str, int maxSize) +{ + int i = 0, pos = 0, size = 0; + while (i < listPos) { + size = (int)listBuffer[i]; + i++; + pos++; + if (pos == 6) { + /* get manufacturer at position 6 in list */ + size = (size > maxSize - 1) ? maxSize : size; + memcpy(str, &listBuffer[i + 1], size); + str[size + 1] = 0; + } + i += size + 1; + } +} + +void smlPow(double *val, signed char *scaler) +{ + if (*scaler < 0) { + while (*scaler++) { + *val /= 10; + } + } + else { + while (*scaler--) { + *val *= 10; + } + } +} + +void smlOBISByUnit(long long int *val, signed char *scaler, sml_units_t unit) +{ + unsigned char i = 0, pos = 0, size = 0, y = 0, skip = 0; + sml_states_t type; + *val = -1; /* unknown or error */ + while (i < listPos) { + pos++; + size = (int)listBuffer[i++]; + type = (sml_states_t)listBuffer[i++]; + if (type == SML_LISTSTART && size > 0) { + // skip a list inside an obis list + skip = size; + while (skip > 0) { + size = (int)listBuffer[i++]; + type = (sml_states_t)listBuffer[i++]; + i += size; + skip--; + } + size = 0; + } + if (pos == 4 && listBuffer[i] != unit) { + /* return unknown (-1) if unit does not match */ + return; + } + if (pos == 5) { + *scaler = listBuffer[i]; + } + if (pos == 6) { + y = size; + // initialize 64bit signed integer based on MSB from received value + *val = + (type == SML_DATA_SIGNED_INT && (listBuffer[i] & (1 << 7))) ? ~0 : 0; + for (y = 0; y < size; y++) { + // left shift received bytes to 64 bit signed integer + *val = (*val << 8) | listBuffer[i + y]; + } + } + i += size; + } +} + +void smlOBISWh(double *wh) +{ + long long int val; + smlOBISByUnit(&val, &sc, SML_WATT_HOUR); + *wh = val; + smlPow(wh, &sc); +} + +void smlOBISW(double *w) +{ + long long int val; + smlOBISByUnit(&val, &sc, SML_WATT); + *w = val; + smlPow(w, &sc); +} + +void smlOBISVolt(double *v) +{ + long long int val; + smlOBISByUnit(&val, &sc, SML_VOLT); + *v = val; + smlPow(v, &sc); +} + +void smlOBISAmpere(double *a) +{ + long long int val; + smlOBISByUnit(&val, &sc, SML_AMPERE); + *a = val; + smlPow(a, &sc); +} + +void smlOBISHertz(double *h) +{ + long long int val; + smlOBISByUnit(&val, &sc, SML_HERTZ); + *h = val; + smlPow(h, &sc); +} + +void smlOBISDegree(double *d) +{ + long long int val; + smlOBISByUnit(&val, &sc, SML_DEGREE); + *d = val; + smlPow(d, &sc); +}