adding sml first time
This commit is contained in:
parent
26d4b8db8a
commit
4bf249cd77
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"stm32l0xx_hal.h": "c",
|
"stm32l0xx_hal.h": "c",
|
||||||
"lmic.h": "c"
|
"lmic.h": "c",
|
||||||
|
"main.h": "c"
|
||||||
}
|
}
|
||||||
}
|
}
|
12
extra_script.py
Normal file
12
extra_script.py
Normal 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
76
include/circular_buffer.h
Normal 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
109
include/sml.h
Normal 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
37
include/smlCrcTable.h
Normal 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
|
@ -17,6 +17,9 @@ build_flags=
|
|||||||
-D CFG_eu868
|
-D CFG_eu868
|
||||||
-D CFG_sx1276_radio
|
-D CFG_sx1276_radio
|
||||||
-D DISABLE_JOIN
|
-D DISABLE_JOIN
|
||||||
|
; -mfloat-abi=soft
|
||||||
|
-Wl,-u_printf_float
|
||||||
|
; extra_scripts = extra_script.py
|
||||||
upload_protocol=custom
|
upload_protocol=custom
|
||||||
upload_command = st-flash write $SOURCE 0x8000000
|
upload_command = st-flash write $SOURCE 0x8000000
|
||||||
debug_tool=stlink
|
debug_tool=stlink
|
||||||
|
176
src/circular_buffer.c
Normal file
176
src/circular_buffer.c
Normal 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;
|
||||||
|
}
|
161
src/main.c
161
src/main.c
@ -17,12 +17,20 @@
|
|||||||
*/
|
*/
|
||||||
/* USER CODE END Header */
|
/* USER CODE END Header */
|
||||||
/* Includes ------------------------------------------------------------------*/
|
/* Includes ------------------------------------------------------------------*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
/* Private includes ----------------------------------------------------------*/
|
/* Private includes ----------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN Includes */
|
/* USER CODE BEGIN Includes */
|
||||||
#include "lmic.h"
|
#include "lmic.h"
|
||||||
#include "debug.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 */
|
/* USER CODE END Includes */
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
/* Private typedef -----------------------------------------------------------*/
|
||||||
@ -33,6 +41,7 @@
|
|||||||
/* Private define ------------------------------------------------------------*/
|
/* Private define ------------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN PD */
|
/* USER CODE BEGIN PD */
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
/* USER CODE END PD */
|
/* USER CODE END PD */
|
||||||
|
|
||||||
@ -60,9 +69,147 @@ static void MX_RTC_Init(void);
|
|||||||
|
|
||||||
/* Private user code ---------------------------------------------------------*/
|
/* Private user code ---------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN 0 */
|
/* 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
|
#ifdef DISABLE_JOIN
|
||||||
// These callbacks are only used in over-the-air activation, so they are
|
// 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 readsensor(){
|
||||||
u2_t value = 0xDF; /// read from evrything ...make your own sensor
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,6 +415,9 @@ int main(void)
|
|||||||
HAL_TIM_Base_Start_IT(&htim2); // <----------- change to your setup
|
HAL_TIM_Base_Start_IT(&htim2); // <----------- change to your setup
|
||||||
__HAL_SPI_ENABLE(&hspi1); // <----------- 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;
|
osjob_t initjob;
|
||||||
|
|
||||||
// initialize runtime env
|
// initialize runtime env
|
||||||
|
421
src/sml.c
Normal file
421
src/sml.c
Normal 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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user