adding sml first time

This commit is contained in:
Dominik Kuhn 2023-12-29 19:20:47 +01:00
parent 26d4b8db8a
commit 4bf249cd77
9 changed files with 995 additions and 3 deletions

View File

@ -1,6 +1,7 @@
{
"files.associations": {
"stm32l0xx_hal.h": "c",
"lmic.h": "c"
"lmic.h": "c",
"main.h": "c"
}
}

12
extra_script.py Normal file
View File

@ -0,0 +1,12 @@
Import("env")
#
# Dump build environment (for debug)
# print(env.Dump())
#
env.Append(
LINKFLAGS=[
"-u _printf_float"
]
)

76
include/circular_buffer.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef CIRCULAR_BUFFER_H_
#define CIRCULAR_BUFFER_H_
#include <stdbool.h>
#include <stdint.h>
/// 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_

109
include/sml.h Normal file
View File

@ -0,0 +1,109 @@
#ifndef SML_H
#define SML_H
#include <stdbool.h>
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

37
include/smlCrcTable.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef SML_CRC_TABLE_H
#define SML_CRC_TABLE_H
#include <stdint.h>
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

View File

@ -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

176
src/circular_buffer.c Normal file
View File

@ -0,0 +1,176 @@
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <assert.h>
#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;
}

View File

@ -17,12 +17,20 @@
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include <stdlib.h>
#include <stdio.h>
#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

421
src/sml.c Normal file
View File

@ -0,0 +1,421 @@
#include <stdio.h>
#include <string.h>
#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 <Arduino.h>
#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);
}