The KPN SenML library helps you create and parse senml documents in both json and cbor format. The library can be used for sending sensor data and receiving actuator commands.
Fork of kpn_senml by
senml_helpers.cpp@0:a9259748d982, 2018-05-19 (annotated)
- Committer:
- kpniot
- Date:
- Sat May 19 17:35:20 2018 +0000
- Revision:
- 0:a9259748d982
first commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
kpniot | 0:a9259748d982 | 1 | /* _ __ ____ _ _ |
kpniot | 0:a9259748d982 | 2 | * | |/ / | _ \ | \ | | |
kpniot | 0:a9259748d982 | 3 | * | ' / | |_) | | \| | |
kpniot | 0:a9259748d982 | 4 | * | . \ | __/ | |\ | |
kpniot | 0:a9259748d982 | 5 | * |_|\_\ |_| |_| \_| |
kpniot | 0:a9259748d982 | 6 | * |
kpniot | 0:a9259748d982 | 7 | * (c) 2018 KPN |
kpniot | 0:a9259748d982 | 8 | * License: MIT License. |
kpniot | 0:a9259748d982 | 9 | * Author: Jan Bogaerts |
kpniot | 0:a9259748d982 | 10 | * |
kpniot | 0:a9259748d982 | 11 | * felper functions |
kpniot | 0:a9259748d982 | 12 | */ |
kpniot | 0:a9259748d982 | 13 | |
kpniot | 0:a9259748d982 | 14 | #include <senml_helpers.h> |
kpniot | 0:a9259748d982 | 15 | |
kpniot | 0:a9259748d982 | 16 | #ifdef ESP32 |
kpniot | 0:a9259748d982 | 17 | #include <base64.h> |
kpniot | 0:a9259748d982 | 18 | #include <arduino.h> //needed for sprintf |
kpniot | 0:a9259748d982 | 19 | #elif __MBED__ |
kpniot | 0:a9259748d982 | 20 | #include <base64.h> |
kpniot | 0:a9259748d982 | 21 | int base64_enc_len(int plainLen) { |
kpniot | 0:a9259748d982 | 22 | int n = plainLen; |
kpniot | 0:a9259748d982 | 23 | return (n + 2 - ((n + 2) % 3)) / 3 * 4; |
kpniot | 0:a9259748d982 | 24 | } |
kpniot | 0:a9259748d982 | 25 | #else |
kpniot | 0:a9259748d982 | 26 | #include <Base64.h> |
kpniot | 0:a9259748d982 | 27 | #endif |
kpniot | 0:a9259748d982 | 28 | |
kpniot | 0:a9259748d982 | 29 | |
kpniot | 0:a9259748d982 | 30 | |
kpniot | 0:a9259748d982 | 31 | //global reference to the stream and stream configuration. This should save us memory |
kpniot | 0:a9259748d982 | 32 | //by not having to pass these values continuously on the stack. |
kpniot | 0:a9259748d982 | 33 | StreamContext* _streamCtx = NULL; |
kpniot | 0:a9259748d982 | 34 | |
kpniot | 0:a9259748d982 | 35 | void printDouble(double f, unsigned int digits) |
kpniot | 0:a9259748d982 | 36 | { |
kpniot | 0:a9259748d982 | 37 | #ifdef __MBED__ |
kpniot | 0:a9259748d982 | 38 | char s[30]; |
kpniot | 0:a9259748d982 | 39 | sprintf(s, "%f", f); |
kpniot | 0:a9259748d982 | 40 | printText(s, strlen(s)); |
kpniot | 0:a9259748d982 | 41 | #else |
kpniot | 0:a9259748d982 | 42 | String temp(f, digits); |
kpniot | 0:a9259748d982 | 43 | const char* s = temp.c_str(); |
kpniot | 0:a9259748d982 | 44 | int length = temp.length(); |
kpniot | 0:a9259748d982 | 45 | |
kpniot | 0:a9259748d982 | 46 | for(int i = length -1; i >0; i--){ //remove unwanted trailing 0 |
kpniot | 0:a9259748d982 | 47 | if(s[i] != '0'){ |
kpniot | 0:a9259748d982 | 48 | length = i; |
kpniot | 0:a9259748d982 | 49 | if(s[i] == '.') //if we end on something like x. then add a last 0, so that it becomes x.0 |
kpniot | 0:a9259748d982 | 50 | length++; |
kpniot | 0:a9259748d982 | 51 | break; |
kpniot | 0:a9259748d982 | 52 | } |
kpniot | 0:a9259748d982 | 53 | } |
kpniot | 0:a9259748d982 | 54 | printText(s, length + 1); |
kpniot | 0:a9259748d982 | 55 | #endif |
kpniot | 0:a9259748d982 | 56 | } |
kpniot | 0:a9259748d982 | 57 | |
kpniot | 0:a9259748d982 | 58 | |
kpniot | 0:a9259748d982 | 59 | |
kpniot | 0:a9259748d982 | 60 | void printBinaryAsBase64(const unsigned char* data, unsigned int length) |
kpniot | 0:a9259748d982 | 61 | { |
kpniot | 0:a9259748d982 | 62 | #ifdef ESP32 |
kpniot | 0:a9259748d982 | 63 | String encoded = base64::encode((uint8_t*)data, length); |
kpniot | 0:a9259748d982 | 64 | printText(encoded.c_str(), encoded.length()); |
kpniot | 0:a9259748d982 | 65 | #else |
kpniot | 0:a9259748d982 | 66 | int encodedLen = base64_enc_len(length); |
kpniot | 0:a9259748d982 | 67 | char encoded[encodedLen]; |
kpniot | 0:a9259748d982 | 68 | |
kpniot | 0:a9259748d982 | 69 | #ifdef __MBED__ |
kpniot | 0:a9259748d982 | 70 | // todo: check result of function |
kpniot | 0:a9259748d982 | 71 | size_t olen; |
kpniot | 0:a9259748d982 | 72 | mbedtls_base64_encode((unsigned char*)encoded, encodedLen, &olen, data, length); |
kpniot | 0:a9259748d982 | 73 | #else |
kpniot | 0:a9259748d982 | 74 | // note input is consumed in this step: it will be empty afterwards |
kpniot | 0:a9259748d982 | 75 | base64_encode(encoded, (char*)data, length); |
kpniot | 0:a9259748d982 | 76 | #endif |
kpniot | 0:a9259748d982 | 77 | printText(encoded, encodedLen); |
kpniot | 0:a9259748d982 | 78 | #endif |
kpniot | 0:a9259748d982 | 79 | } |
kpniot | 0:a9259748d982 | 80 | |
kpniot | 0:a9259748d982 | 81 | void printUnit(SenMLUnit unit) |
kpniot | 0:a9259748d982 | 82 | { |
kpniot | 0:a9259748d982 | 83 | if(unit != SENML_UNIT_NONE) |
kpniot | 0:a9259748d982 | 84 | printText(senml_units_names[unit], strlen(senml_units_names[unit])); |
kpniot | 0:a9259748d982 | 85 | } |
kpniot | 0:a9259748d982 | 86 | |
kpniot | 0:a9259748d982 | 87 | void printText(const char* value, int len) |
kpniot | 0:a9259748d982 | 88 | { |
kpniot | 0:a9259748d982 | 89 | char hexTable[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; |
kpniot | 0:a9259748d982 | 90 | |
kpniot | 0:a9259748d982 | 91 | if(_streamCtx->dataAsBlob){ |
kpniot | 0:a9259748d982 | 92 | if(_streamCtx->format == SENML_RAW){ |
kpniot | 0:a9259748d982 | 93 | for(int i = 0; i < len; i++){ |
kpniot | 0:a9259748d982 | 94 | if(_streamCtx->data.blob.curPos >= _streamCtx->data.blob.length) return; //if we reached the end of the buffer, stop rendering otherwise we overwrite some other mem which is not good. |
kpniot | 0:a9259748d982 | 95 | _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++] = value[i]; |
kpniot | 0:a9259748d982 | 96 | } |
kpniot | 0:a9259748d982 | 97 | } |
kpniot | 0:a9259748d982 | 98 | else{ |
kpniot | 0:a9259748d982 | 99 | for(int i = 0; i < len; i++){ |
kpniot | 0:a9259748d982 | 100 | if(_streamCtx->data.blob.curPos >= _streamCtx->data.blob.length) return; //if we reached the end of the buffer, stop rendering otherwise we overwrite some other mem which is not good. |
kpniot | 0:a9259748d982 | 101 | _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++] = hexTable[value[i] / 16]; |
kpniot | 0:a9259748d982 | 102 | _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++] = hexTable[value[i] % 16]; |
kpniot | 0:a9259748d982 | 103 | } |
kpniot | 0:a9259748d982 | 104 | } |
kpniot | 0:a9259748d982 | 105 | } |
kpniot | 0:a9259748d982 | 106 | else{ |
kpniot | 0:a9259748d982 | 107 | if(_streamCtx->format == SENML_RAW){ |
kpniot | 0:a9259748d982 | 108 | #ifdef __MBED__ |
kpniot | 0:a9259748d982 | 109 | for(int i = 0; i < len; i++) |
kpniot | 0:a9259748d982 | 110 | _streamCtx->data.stream->putc(value[i]); |
kpniot | 0:a9259748d982 | 111 | #else |
kpniot | 0:a9259748d982 | 112 | _streamCtx->data.stream->write(value, len); |
kpniot | 0:a9259748d982 | 113 | #endif |
kpniot | 0:a9259748d982 | 114 | } |
kpniot | 0:a9259748d982 | 115 | else if (_streamCtx->format == SENML_HEX){ |
kpniot | 0:a9259748d982 | 116 | for(int i = 0; i< len; i++){ |
kpniot | 0:a9259748d982 | 117 | #ifdef __MBED__ |
kpniot | 0:a9259748d982 | 118 | _streamCtx->data.stream->putc(hexTable[value[i] / 16]); |
kpniot | 0:a9259748d982 | 119 | _streamCtx->data.stream->putc(hexTable[value[i] % 16]); |
kpniot | 0:a9259748d982 | 120 | #else |
kpniot | 0:a9259748d982 | 121 | _streamCtx->data.stream->print(hexTable[value[i] / 16]); |
kpniot | 0:a9259748d982 | 122 | _streamCtx->data.stream->print(hexTable[value[i] % 16]); |
kpniot | 0:a9259748d982 | 123 | #endif |
kpniot | 0:a9259748d982 | 124 | } |
kpniot | 0:a9259748d982 | 125 | } |
kpniot | 0:a9259748d982 | 126 | } |
kpniot | 0:a9259748d982 | 127 | } |
kpniot | 0:a9259748d982 | 128 | |
kpniot | 0:a9259748d982 | 129 | static bool peeked = false; //if we peek the stream in HEX format, we actually need to read 2 bytes, so the peek doesn't work, but we need to read the actual value. |
kpniot | 0:a9259748d982 | 130 | static int peekVal = 0; //these values are removed by the compiler if no cbor is used. |
kpniot | 0:a9259748d982 | 131 | |
kpniot | 0:a9259748d982 | 132 | int readChar(){ |
kpniot | 0:a9259748d982 | 133 | if(peeked == true){ |
kpniot | 0:a9259748d982 | 134 | peeked = false; |
kpniot | 0:a9259748d982 | 135 | return peekVal; |
kpniot | 0:a9259748d982 | 136 | } |
kpniot | 0:a9259748d982 | 137 | int res; |
kpniot | 0:a9259748d982 | 138 | if(_streamCtx->dataAsBlob){ |
kpniot | 0:a9259748d982 | 139 | if(_streamCtx->data.blob.curPos < _streamCtx->data.blob.length) //peekchar has to return -1 if there is no more data, so check for this. |
kpniot | 0:a9259748d982 | 140 | res = _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++]; |
kpniot | 0:a9259748d982 | 141 | else |
kpniot | 0:a9259748d982 | 142 | return -1; |
kpniot | 0:a9259748d982 | 143 | if(_streamCtx->format == SENML_HEX){ |
kpniot | 0:a9259748d982 | 144 | int resB = _streamCtx->data.blob.data[_streamCtx->data.blob.curPos++]; |
kpniot | 0:a9259748d982 | 145 | res = (res > '9')? (res &~ 0x20) - 'A' + 10: (res - '0'); |
kpniot | 0:a9259748d982 | 146 | resB = (resB > '9')? (resB &~ 0x20) - 'A' + 10: (resB - '0'); |
kpniot | 0:a9259748d982 | 147 | res = (res * 16) + resB; |
kpniot | 0:a9259748d982 | 148 | } |
kpniot | 0:a9259748d982 | 149 | } |
kpniot | 0:a9259748d982 | 150 | else { //data is comming from the stream, this already returns -1 if there is no data. |
kpniot | 0:a9259748d982 | 151 | if(_streamCtx->format == SENML_RAW){ |
kpniot | 0:a9259748d982 | 152 | #ifdef __MBED__ |
kpniot | 0:a9259748d982 | 153 | res = _streamCtx->data.stream->getc(); |
kpniot | 0:a9259748d982 | 154 | #else |
kpniot | 0:a9259748d982 | 155 | res = _streamCtx->data.stream->available() ? _streamCtx->data.stream->read() : -1; //arduino stream, check if something is available, if not, we can't read anymore. |
kpniot | 0:a9259748d982 | 156 | #endif |
kpniot | 0:a9259748d982 | 157 | } |
kpniot | 0:a9259748d982 | 158 | else{ |
kpniot | 0:a9259748d982 | 159 | int resB; |
kpniot | 0:a9259748d982 | 160 | #ifdef __MBED__ |
kpniot | 0:a9259748d982 | 161 | res = _streamCtx->data.stream->getc(); |
kpniot | 0:a9259748d982 | 162 | resB = _streamCtx->data.stream->getc(); |
kpniot | 0:a9259748d982 | 163 | #else |
kpniot | 0:a9259748d982 | 164 | |
kpniot | 0:a9259748d982 | 165 | if(_streamCtx->data.stream->available()) |
kpniot | 0:a9259748d982 | 166 | res = _streamCtx->data.stream->read(); |
kpniot | 0:a9259748d982 | 167 | else |
kpniot | 0:a9259748d982 | 168 | return -1; |
kpniot | 0:a9259748d982 | 169 | if(_streamCtx->data.stream->available()) |
kpniot | 0:a9259748d982 | 170 | resB = _streamCtx->data.stream->read(); |
kpniot | 0:a9259748d982 | 171 | else |
kpniot | 0:a9259748d982 | 172 | return -1; |
kpniot | 0:a9259748d982 | 173 | #endif |
kpniot | 0:a9259748d982 | 174 | res = (res > '9')? (res &~ 0x20) - 'A' + 10: (res - '0'); |
kpniot | 0:a9259748d982 | 175 | resB = (resB > '9')? (resB &~ 0x20) - 'A' + 10: (resB - '0'); |
kpniot | 0:a9259748d982 | 176 | res = (res * 16) + resB; |
kpniot | 0:a9259748d982 | 177 | } |
kpniot | 0:a9259748d982 | 178 | } |
kpniot | 0:a9259748d982 | 179 | return res; |
kpniot | 0:a9259748d982 | 180 | }; |
kpniot | 0:a9259748d982 | 181 | |
kpniot | 0:a9259748d982 | 182 | int peekChar(){ |
kpniot | 0:a9259748d982 | 183 | if(peeked == true) //if already peeked, return the currently buffered value. |
kpniot | 0:a9259748d982 | 184 | return peekVal; |
kpniot | 0:a9259748d982 | 185 | peekVal = readChar(); |
kpniot | 0:a9259748d982 | 186 | if(peekVal != -1) //if no more data, don't try to buffer it, some new data might arrive later on |
kpniot | 0:a9259748d982 | 187 | peeked = true; |
kpniot | 0:a9259748d982 | 188 | return peekVal; |
kpniot | 0:a9259748d982 | 189 | }; |
kpniot | 0:a9259748d982 | 190 | |
kpniot | 0:a9259748d982 | 191 | void flush(){ |
kpniot | 0:a9259748d982 | 192 | peeked = false; |
kpniot | 0:a9259748d982 | 193 | } |
kpniot | 0:a9259748d982 | 194 | |
kpniot | 0:a9259748d982 | 195 | |
kpniot | 0:a9259748d982 | 196 |