Remove fragile JSON libraries from the firmware while retaining Meshtasticd JSON support (#10152)

This commit is contained in:
Thomas Göttgens
2026-06-03 16:47:30 +02:00
committed by GitHub
parent fe23dcfa3a
commit f86cb7781e
30 changed files with 724 additions and 2830 deletions
-245
View File
@@ -1,245 +0,0 @@
/*
* File JSON.cpp part of the SimpleJSON Library - http://mjpa.in/json
*
* Copyright (C) 2010 Mike Anchor
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "JSON.h"
/**
* Blocks off the public constructor
*
* @access private
*
*/
JSON::JSON() {}
/**
* Parses a complete JSON encoded string
*
* @access public
*
* @param char* data The JSON text
*
* @return JSONValue* Returns a JSON Value representing the root, or NULL on error
*/
JSONValue *JSON::Parse(const char *data)
{
// Skip any preceding whitespace, end of data = no JSON = fail
if (!SkipWhitespace(&data))
return NULL;
// We need the start of a value here now...
JSONValue *value = JSONValue::Parse(&data);
if (value == NULL)
return NULL;
// Can be white space now and should be at the end of the string then...
if (SkipWhitespace(&data)) {
delete value;
return NULL;
}
// We're now at the end of the string
return value;
}
/**
* Turns the passed in JSONValue into a JSON encode string
*
* @access public
*
* @param JSONValue* value The root value
*
* @return std::string Returns a JSON encoded string representation of the given value
*/
std::string JSON::Stringify(const JSONValue *value)
{
if (value != NULL)
return value->Stringify();
else
return "";
}
/**
* Skips over any whitespace characters (space, tab, \r or \n) defined by the JSON spec
*
* @access protected
*
* @param char** data Pointer to a char* that contains the JSON text
*
* @return bool Returns true if there is more data, or false if the end of the text was reached
*/
bool JSON::SkipWhitespace(const char **data)
{
while (**data != 0 && (**data == ' ' || **data == '\t' || **data == '\r' || **data == '\n'))
(*data)++;
return **data != 0;
}
/**
* Extracts a JSON String as defined by the spec - "<some chars>"
* Any escaped characters are swapped out for their unescaped values
*
* @access protected
*
* @param char** data Pointer to a char* that contains the JSON text
* @param std::string& str Reference to a std::string to receive the extracted string
*
* @return bool Returns true on success, false on failure
*/
bool JSON::ExtractString(const char **data, std::string &str)
{
str = "";
while (**data != 0) {
// Save the char so we can change it if need be
char next_char = **data;
// Escaping something?
if (next_char == '\\') {
// Move over the escape char
(*data)++;
// Deal with the escaped char
switch (**data) {
case '"':
next_char = '"';
break;
case '\\':
next_char = '\\';
break;
case '/':
next_char = '/';
break;
case 'b':
next_char = '\b';
break;
case 'f':
next_char = '\f';
break;
case 'n':
next_char = '\n';
break;
case 'r':
next_char = '\r';
break;
case 't':
next_char = '\t';
break;
case 'u': {
// We need 5 chars (4 hex + the 'u') or its not valid
if (!simplejson_csnlen(*data, 5))
return false;
// Deal with the chars
next_char = 0;
for (int i = 0; i < 4; i++) {
// Do it first to move off the 'u' and leave us on the
// final hex digit as we move on by one later on
(*data)++;
next_char <<= 4;
// Parse the hex digit
if (**data >= '0' && **data <= '9')
next_char |= (**data - '0');
else if (**data >= 'A' && **data <= 'F')
next_char |= (10 + (**data - 'A'));
else if (**data >= 'a' && **data <= 'f')
next_char |= (10 + (**data - 'a'));
else {
// Invalid hex digit = invalid JSON
return false;
}
}
break;
}
// By the spec, only the above cases are allowed
default:
return false;
}
}
// End of the string?
else if (next_char == '"') {
(*data)++;
str.shrink_to_fit(); // Remove unused capacity
return true;
}
// Disallowed char?
else if (next_char < ' ' && next_char != '\t') {
// SPEC Violation: Allow tabs due to real world cases
return false;
}
// Add the next char
str += next_char;
// Move on
(*data)++;
}
// If we're here, the string ended incorrectly
return false;
}
/**
* Parses some text as though it is an integer
*
* @access protected
*
* @param char** data Pointer to a char* that contains the JSON text
*
* @return double Returns the double value of the number found
*/
double JSON::ParseInt(const char **data)
{
double integer = 0;
while (**data != 0 && **data >= '0' && **data <= '9')
integer = integer * 10 + (*(*data)++ - '0');
return integer;
}
/**
* Parses some text as though it is a decimal
*
* @access protected
*
* @param char** data Pointer to a char* that contains the JSON text
*
* @return double Returns the double value of the decimal found
*/
double JSON::ParseDecimal(const char **data)
{
double decimal = 0.0;
double factor = 0.1;
while (**data != 0 && **data >= '0' && **data <= '9') {
int digit = (*(*data)++ - '0');
decimal = decimal + digit * factor;
factor *= 0.1;
}
return decimal;
}
-73
View File
@@ -1,73 +0,0 @@
/*
* File JSON.h part of the SimpleJSON Library - http://mjpa.in/json
*
* Copyright (C) 2010 Mike Anchor
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef _JSON_H_
#define _JSON_H_
#include <cstring>
#include <map>
#include <string>
#include <vector>
// Simple function to check a string 's' has at least 'n' characters
static inline bool simplejson_csnlen(const char *s, size_t n)
{
if (s == 0)
return false;
const char *save = s;
while (n-- > 0) {
if (*(save++) == 0)
return false;
}
return true;
}
// Custom types
class JSONValue;
typedef std::vector<JSONValue *> JSONArray;
typedef std::map<std::string, JSONValue *> JSONObject;
#include "JSONValue.h"
class JSON
{
friend class JSONValue;
public:
static JSONValue *Parse(const char *data);
static std::string Stringify(const JSONValue *value);
protected:
static bool SkipWhitespace(const char **data);
static bool ExtractString(const char **data, std::string &str);
static double ParseInt(const char **data);
static double ParseDecimal(const char **data);
private:
JSON();
};
#endif
-897
View File
@@ -1,897 +0,0 @@
/*
* File JSONValue.cpp part of the SimpleJSON Library - http://mjpa.in/json
*
* Copyright (C) 2010 Mike Anchor
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <math.h>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include "JSONValue.h"
// Macros to free an array/object
#define FREE_ARRAY(x) \
{ \
JSONArray::iterator iter; \
for (iter = x.begin(); iter != x.end(); ++iter) { \
delete *iter; \
} \
}
#define FREE_OBJECT(x) \
{ \
JSONObject::iterator iter; \
for (iter = x.begin(); iter != x.end(); ++iter) { \
delete (*iter).second; \
} \
}
/**
* Parses a JSON encoded value to a JSONValue object
*
* @access protected
*
* @param char** data Pointer to a char* that contains the data
*
* @return JSONValue* Returns a pointer to a JSONValue object on success, NULL on error
*/
JSONValue *JSONValue::Parse(const char **data)
{
// Is it a string?
if (**data == '"') {
std::string str;
if (!JSON::ExtractString(&(++(*data)), str))
return NULL;
else
return new JSONValue(str);
}
// Is it a boolean?
else if ((simplejson_csnlen(*data, 4) && strncasecmp(*data, "true", 4) == 0) ||
(simplejson_csnlen(*data, 5) && strncasecmp(*data, "false", 5) == 0)) {
bool value = strncasecmp(*data, "true", 4) == 0;
(*data) += value ? 4 : 5;
return new JSONValue(value);
}
// Is it a null?
else if (simplejson_csnlen(*data, 4) && strncasecmp(*data, "null", 4) == 0) {
(*data) += 4;
return new JSONValue();
}
// Is it a number?
else if (**data == '-' || (**data >= '0' && **data <= '9')) {
// Negative?
bool neg = **data == '-';
if (neg)
(*data)++;
double number = 0.0;
// Parse the whole part of the number - only if it wasn't 0
if (**data == '0')
(*data)++;
else if (**data >= '1' && **data <= '9')
number = JSON::ParseInt(data);
else
return NULL;
// Could be a decimal now...
if (**data == '.') {
(*data)++;
// Not get any digits?
if (!(**data >= '0' && **data <= '9'))
return NULL;
// Find the decimal and sort the decimal place out
// Use ParseDecimal as ParseInt won't work with decimals less than 0.1
// thanks to Javier Abadia for the report & fix
double decimal = JSON::ParseDecimal(data);
// Save the number
number += decimal;
}
// Could be an exponent now...
if (**data == 'E' || **data == 'e') {
(*data)++;
// Check signage of expo
bool neg_expo = false;
if (**data == '-' || **data == '+') {
neg_expo = **data == '-';
(*data)++;
}
// Not get any digits?
if (!(**data >= '0' && **data <= '9'))
return NULL;
// Sort the expo out
double expo = JSON::ParseInt(data);
for (double i = 0.0; i < expo; i++)
number = neg_expo ? (number / 10.0) : (number * 10.0);
}
// Was it neg?
if (neg)
number *= -1;
return new JSONValue(number);
}
// An object?
else if (**data == '{') {
JSONObject object;
(*data)++;
while (**data != 0) {
// Whitespace at the start?
if (!JSON::SkipWhitespace(data)) {
FREE_OBJECT(object);
return NULL;
}
// Special case - empty object
if (object.size() == 0 && **data == '}') {
(*data)++;
return new JSONValue(object);
}
// We want a string now...
std::string name;
if (!JSON::ExtractString(&(++(*data)), name)) {
FREE_OBJECT(object);
return NULL;
}
// More whitespace?
if (!JSON::SkipWhitespace(data)) {
FREE_OBJECT(object);
return NULL;
}
// Need a : now
if (*((*data)++) != ':') {
FREE_OBJECT(object);
return NULL;
}
// More whitespace?
if (!JSON::SkipWhitespace(data)) {
FREE_OBJECT(object);
return NULL;
}
// The value is here
JSONValue *value = Parse(data);
if (value == NULL) {
FREE_OBJECT(object);
return NULL;
}
// Add the name:value
if (object.find(name) != object.end())
delete object[name];
object[name] = value;
// More whitespace?
if (!JSON::SkipWhitespace(data)) {
FREE_OBJECT(object);
return NULL;
}
// End of object?
if (**data == '}') {
(*data)++;
return new JSONValue(object);
}
// Want a , now
if (**data != ',') {
FREE_OBJECT(object);
return NULL;
}
(*data)++;
}
// Only here if we ran out of data
FREE_OBJECT(object);
return NULL;
}
// An array?
else if (**data == '[') {
JSONArray array;
(*data)++;
while (**data != 0) {
// Whitespace at the start?
if (!JSON::SkipWhitespace(data)) {
FREE_ARRAY(array);
return NULL;
}
// Special case - empty array
if (array.size() == 0 && **data == ']') {
(*data)++;
return new JSONValue(array);
}
// Get the value
JSONValue *value = Parse(data);
if (value == NULL) {
FREE_ARRAY(array);
return NULL;
}
// Add the value
array.push_back(value);
// More whitespace?
if (!JSON::SkipWhitespace(data)) {
FREE_ARRAY(array);
return NULL;
}
// End of array?
if (**data == ']') {
(*data)++;
return new JSONValue(array);
}
// Want a , now
if (**data != ',') {
FREE_ARRAY(array);
return NULL;
}
(*data)++;
}
// Only here if we ran out of data
FREE_ARRAY(array);
return NULL;
}
// Ran out of possibilities, it's bad!
else {
return NULL;
}
}
/**
* Basic constructor for creating a JSON Value of type NULL
*
* @access public
*/
JSONValue::JSONValue(/*NULL*/)
{
type = JSONType_Null;
}
/**
* Basic constructor for creating a JSON Value of type String
*
* @access public
*
* @param char* m_char_value The string to use as the value
*/
JSONValue::JSONValue(const char *m_char_value)
{
type = JSONType_String;
string_value = new std::string(std::string(m_char_value));
}
/**
* Basic constructor for creating a JSON Value of type String
*
* @access public
*
* @param std::string m_string_value The string to use as the value
*/
JSONValue::JSONValue(const std::string &m_string_value)
{
type = JSONType_String;
string_value = new std::string(m_string_value);
}
/**
* Basic constructor for creating a JSON Value of type Bool
*
* @access public
*
* @param bool m_bool_value The bool to use as the value
*/
JSONValue::JSONValue(bool m_bool_value)
{
type = JSONType_Bool;
bool_value = m_bool_value;
}
/**
* Basic constructor for creating a JSON Value of type Number
*
* @access public
*
* @param double m_number_value The number to use as the value
*/
JSONValue::JSONValue(double m_number_value)
{
type = JSONType_Number;
number_value = m_number_value;
}
/**
* Basic constructor for creating a JSON Value of type Number
*
* @access public
*
* @param int m_integer_value The number to use as the value
*/
JSONValue::JSONValue(int m_integer_value)
{
type = JSONType_Number;
number_value = (double)m_integer_value;
}
/**
* Basic constructor for creating a JSON Value of type Number
*
* @access public
*
* @param unsigned int m_integer_value The number to use as the value
*/
JSONValue::JSONValue(unsigned int m_integer_value)
{
type = JSONType_Number;
number_value = (double)m_integer_value;
}
/**
* Basic constructor for creating a JSON Value of type Array
*
* @access public
*
* @param JSONArray m_array_value The JSONArray to use as the value
*/
JSONValue::JSONValue(const JSONArray &m_array_value)
{
type = JSONType_Array;
array_value = new JSONArray(m_array_value);
}
/**
* Basic constructor for creating a JSON Value of type Object
*
* @access public
*
* @param JSONObject m_object_value The JSONObject to use as the value
*/
JSONValue::JSONValue(const JSONObject &m_object_value)
{
type = JSONType_Object;
object_value = new JSONObject(m_object_value);
}
/**
* Copy constructor to perform a deep copy of array / object values
*
* @access public
*
* @param JSONValue m_source The source JSONValue that is being copied
*/
JSONValue::JSONValue(const JSONValue &m_source)
{
type = m_source.type;
switch (type) {
case JSONType_String:
string_value = new std::string(*m_source.string_value);
break;
case JSONType_Bool:
bool_value = m_source.bool_value;
break;
case JSONType_Number:
number_value = m_source.number_value;
break;
case JSONType_Array: {
JSONArray source_array = *m_source.array_value;
JSONArray::iterator iter;
array_value = new JSONArray();
for (iter = source_array.begin(); iter != source_array.end(); ++iter)
array_value->push_back(new JSONValue(**iter));
break;
}
case JSONType_Object: {
JSONObject source_object = *m_source.object_value;
object_value = new JSONObject();
JSONObject::iterator iter;
for (iter = source_object.begin(); iter != source_object.end(); ++iter) {
std::string name = (*iter).first;
(*object_value)[name] = new JSONValue(*((*iter).second));
}
break;
}
case JSONType_Null:
// Nothing to do.
break;
}
}
/**
* The destructor for the JSON Value object
* Handles deleting the objects in the array or the object value
*
* @access public
*/
JSONValue::~JSONValue()
{
if (type == JSONType_Array) {
JSONArray::iterator iter;
for (iter = array_value->begin(); iter != array_value->end(); ++iter)
delete *iter;
delete array_value;
} else if (type == JSONType_Object) {
JSONObject::iterator iter;
for (iter = object_value->begin(); iter != object_value->end(); ++iter) {
delete (*iter).second;
}
delete object_value;
} else if (type == JSONType_String) {
delete string_value;
}
}
/**
* Checks if the value is a NULL
*
* @access public
*
* @return bool Returns true if it is a NULL value, false otherwise
*/
bool JSONValue::IsNull() const
{
return type == JSONType_Null;
}
/**
* Checks if the value is a String
*
* @access public
*
* @return bool Returns true if it is a String value, false otherwise
*/
bool JSONValue::IsString() const
{
return type == JSONType_String;
}
/**
* Checks if the value is a Bool
*
* @access public
*
* @return bool Returns true if it is a Bool value, false otherwise
*/
bool JSONValue::IsBool() const
{
return type == JSONType_Bool;
}
/**
* Checks if the value is a Number
*
* @access public
*
* @return bool Returns true if it is a Number value, false otherwise
*/
bool JSONValue::IsNumber() const
{
return type == JSONType_Number;
}
/**
* Checks if the value is an Array
*
* @access public
*
* @return bool Returns true if it is an Array value, false otherwise
*/
bool JSONValue::IsArray() const
{
return type == JSONType_Array;
}
/**
* Checks if the value is an Object
*
* @access public
*
* @return bool Returns true if it is an Object value, false otherwise
*/
bool JSONValue::IsObject() const
{
return type == JSONType_Object;
}
/**
* Retrieves the String value of this JSONValue
* Use IsString() before using this method.
*
* @access public
*
* @return std::string Returns the string value
*/
const std::string &JSONValue::AsString() const
{
return (*string_value);
}
/**
* Retrieves the Bool value of this JSONValue
* Use IsBool() before using this method.
*
* @access public
*
* @return bool Returns the bool value
*/
bool JSONValue::AsBool() const
{
return bool_value;
}
/**
* Retrieves the Number value of this JSONValue
* Use IsNumber() before using this method.
*
* @access public
*
* @return double Returns the number value
*/
double JSONValue::AsNumber() const
{
return number_value;
}
/**
* Retrieves the Array value of this JSONValue
* Use IsArray() before using this method.
*
* @access public
*
* @return JSONArray Returns the array value
*/
const JSONArray &JSONValue::AsArray() const
{
return (*array_value);
}
/**
* Retrieves the Object value of this JSONValue
* Use IsObject() before using this method.
*
* @access public
*
* @return JSONObject Returns the object value
*/
const JSONObject &JSONValue::AsObject() const
{
return (*object_value);
}
/**
* Retrieves the number of children of this JSONValue.
* This number will be 0 or the actual number of children
* if IsArray() or IsObject().
*
* @access public
*
* @return The number of children.
*/
std::size_t JSONValue::CountChildren() const
{
switch (type) {
case JSONType_Array:
return array_value->size();
case JSONType_Object:
return object_value->size();
default:
return 0;
}
}
/**
* Checks if this JSONValue has a child at the given index.
* Use IsArray() before using this method.
*
* @access public
*
* @return bool Returns true if the array has a value at the given index.
*/
bool JSONValue::HasChild(std::size_t index) const
{
if (type == JSONType_Array) {
return index < array_value->size();
} else {
return false;
}
}
/**
* Retrieves the child of this JSONValue at the given index.
* Use IsArray() before using this method.
*
* @access public
*
* @return JSONValue* Returns JSONValue at the given index or NULL
* if it doesn't exist.
*/
JSONValue *JSONValue::Child(std::size_t index)
{
if (index < array_value->size()) {
return (*array_value)[index];
} else {
return NULL;
}
}
/**
* Checks if this JSONValue has a child at the given key.
* Use IsObject() before using this method.
*
* @access public
*
* @return bool Returns true if the object has a value at the given key.
*/
bool JSONValue::HasChild(const char *name) const
{
if (type == JSONType_Object) {
return object_value->find(name) != object_value->end();
} else {
return false;
}
}
/**
* Retrieves the child of this JSONValue at the given key.
* Use IsObject() before using this method.
*
* @access public
*
* @return JSONValue* Returns JSONValue for the given key in the object
* or NULL if it doesn't exist.
*/
JSONValue *JSONValue::Child(const char *name)
{
JSONObject::const_iterator it = object_value->find(name);
if (it != object_value->end()) {
return it->second;
} else {
return NULL;
}
}
/**
* Retrieves the keys of the JSON Object or an empty vector
* if this value is not an object.
*
* @access public
*
* @return std::vector<std::string> A vector containing the keys.
*/
std::vector<std::string> JSONValue::ObjectKeys() const
{
std::vector<std::string> keys;
if (type == JSONType_Object) {
JSONObject::const_iterator iter = object_value->begin();
while (iter != object_value->end()) {
keys.push_back(iter->first);
++iter;
}
}
return keys;
}
/**
* Creates a JSON encoded string for the value with all necessary characters escaped
*
* @access public
*
* @param bool prettyprint Enable prettyprint
*
* @return std::string Returns the JSON string
*/
std::string JSONValue::Stringify(bool const prettyprint) const
{
size_t const indentDepth = prettyprint ? 1 : 0;
return StringifyImpl(indentDepth);
}
/**
* Creates a JSON encoded string for the value with all necessary characters escaped
*
* @access private
*
* @param size_t indentDepth The prettyprint indentation depth (0 : no prettyprint)
*
* @return std::string Returns the JSON string
*/
std::string JSONValue::StringifyImpl(size_t const indentDepth) const
{
std::string ret_string;
size_t const indentDepth1 = indentDepth ? indentDepth + 1 : 0;
std::string const indentStr = Indent(indentDepth);
std::string const indentStr1 = Indent(indentDepth1);
switch (type) {
case JSONType_Null:
ret_string = "null";
break;
case JSONType_String:
ret_string = StringifyString(*string_value);
break;
case JSONType_Bool:
ret_string = bool_value ? "true" : "false";
break;
case JSONType_Number: {
if (isinf(number_value) || isnan(number_value))
ret_string = "null";
else {
std::stringstream ss;
ss.precision(15);
ss << number_value;
ret_string = ss.str();
}
break;
}
case JSONType_Array: {
ret_string = indentDepth ? "[\n" + indentStr1 : "[";
JSONArray::const_iterator iter = array_value->begin();
while (iter != array_value->end()) {
ret_string += (*iter)->StringifyImpl(indentDepth1);
// Not at the end - add a separator
if (++iter != array_value->end())
ret_string += ",";
}
ret_string += indentDepth ? "\n" + indentStr + "]" : "]";
break;
}
case JSONType_Object: {
ret_string = indentDepth ? "{\n" + indentStr1 : "{";
JSONObject::const_iterator iter = object_value->begin();
while (iter != object_value->end()) {
ret_string += StringifyString((*iter).first);
ret_string += ":";
ret_string += (*iter).second->StringifyImpl(indentDepth1);
// Not at the end - add a separator
if (++iter != object_value->end())
ret_string += ",";
}
ret_string += indentDepth ? "\n" + indentStr + "}" : "}";
break;
}
}
return ret_string;
}
/**
* Creates a JSON encoded string with all required fields escaped
* Works from http://www.ecma-internationl.org/publications/files/ECMA-ST/ECMA-262.pdf
* Section 15.12.3.
*
* @access private
*
* @param std::string str The string that needs to have the characters escaped
*
* @return std::string Returns the JSON string
*/
std::string JSONValue::StringifyString(const std::string &str)
{
std::string str_out = "\"";
std::string::const_iterator iter = str.begin();
while (iter != str.end()) {
char chr = *iter;
if (chr == '"' || chr == '\\' || chr == '/') {
str_out += '\\';
str_out += chr;
} else if (chr == '\b') {
str_out += "\\b";
} else if (chr == '\f') {
str_out += "\\f";
} else if (chr == '\n') {
str_out += "\\n";
} else if (chr == '\r') {
str_out += "\\r";
} else if (chr == '\t') {
str_out += "\\t";
} else if (chr < 0x20 || chr == 0x7F) {
char buf[7];
snprintf(buf, sizeof(buf), "\\u%04x", chr);
str_out += buf;
} else if (chr < 0x80) {
str_out += chr;
} else {
str_out += chr;
size_t remain = str.end() - iter - 1;
if ((chr & 0xE0) == 0xC0 && remain >= 1) {
++iter;
str_out += *iter;
} else if ((chr & 0xF0) == 0xE0 && remain >= 2) {
str_out += *(++iter);
str_out += *(++iter);
} else if ((chr & 0xF8) == 0xF0 && remain >= 3) {
str_out += *(++iter);
str_out += *(++iter);
str_out += *(++iter);
}
}
++iter;
}
str_out += "\"";
return str_out;
}
/**
* Creates the indentation string for the depth given
*
* @access private
*
* @param size_t indent The prettyprint indentation depth (0 : no indentation)
*
* @return std::string Returns the string
*/
std::string JSONValue::Indent(size_t depth)
{
const size_t indent_step = 2;
depth ? --depth : 0;
std::string indentStr(depth * indent_step, ' ');
return indentStr;
}
-95
View File
@@ -1,95 +0,0 @@
/*
* File JSONValue.h part of the SimpleJSON Library - http://mjpa.in/json
*
* Copyright (C) 2010 Mike Anchor
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef _JSONVALUE_H_
#define _JSONVALUE_H_
#include <string>
#include <vector>
#include "JSON.h"
class JSON;
enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_Array, JSONType_Object };
class JSONValue
{
friend class JSON;
public:
JSONValue(/*NULL*/);
explicit JSONValue(const char *m_char_value);
explicit JSONValue(const std::string &m_string_value);
explicit JSONValue(bool m_bool_value);
explicit JSONValue(double m_number_value);
explicit JSONValue(int m_integer_value);
explicit JSONValue(unsigned int m_integer_value);
explicit JSONValue(const JSONArray &m_array_value);
explicit JSONValue(const JSONObject &m_object_value);
explicit JSONValue(const JSONValue &m_source);
~JSONValue();
bool IsNull() const;
bool IsString() const;
bool IsBool() const;
bool IsNumber() const;
bool IsArray() const;
bool IsObject() const;
const std::string &AsString() const;
bool AsBool() const;
double AsNumber() const;
const JSONArray &AsArray() const;
const JSONObject &AsObject() const;
std::size_t CountChildren() const;
bool HasChild(std::size_t index) const;
JSONValue *Child(std::size_t index);
bool HasChild(const char *name) const;
JSONValue *Child(const char *name);
std::vector<std::string> ObjectKeys() const;
std::string Stringify(bool const prettyprint = false) const;
protected:
static JSONValue *Parse(const char **data);
private:
static std::string StringifyString(const std::string &str);
std::string StringifyImpl(size_t const indentDepth) const;
static std::string Indent(size_t depth);
JSONType type;
union {
bool bool_value;
double number_value;
std::string *string_value;
JSONArray *array_value;
JSONObject *object_value;
};
};
#endif
+166 -172
View File
@@ -1,11 +1,12 @@
#ifndef NRF52_USE_JSON
#if ARCH_PORTDUINO
#include "MeshPacketSerializer.h"
#include "JSON.h"
#include "NodeDB.h"
#include "mesh/generated/meshtastic/mqtt.pb.h"
#include "mesh/generated/meshtastic/telemetry.pb.h"
#include "modules/RoutingModule.h"
#include <DebugConfiguration.h>
#include <json/json.h>
#include <memory>
#include <mesh-pb-constants.h>
#if defined(ARCH_ESP32)
#include "../mesh/generated/meshtastic/paxcount.pb.h"
@@ -15,41 +16,49 @@
static const char *errStr = "Error decoding proto for %s message!";
static std::string writeCompact(const Json::Value &v)
{
Json::StreamWriterBuilder b;
b["indentation"] = "";
b["emitUTF8"] = true;
return Json::writeString(b, v);
}
static bool tryParseJson(const char *s, Json::Value &out)
{
Json::CharReaderBuilder b;
std::unique_ptr<Json::CharReader> reader(b.newCharReader());
std::string errs;
const char *end = s + strlen(s);
return reader->parse(s, end, &out, &errs);
}
std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog)
{
// the created jsonObj is immutable after creation, so
// we need to do the heavy lifting before assembling it.
std::string msgType;
JSONObject jsonObj;
Json::Value jsonObj(Json::objectValue);
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
JSONObject msgPayload;
Json::Value msgPayload(Json::objectValue);
switch (mp->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP: {
msgType = "text";
// convert bytes to string
if (shouldLog)
LOG_DEBUG("got text message of size %u", mp->decoded.payload.size);
char payloadStr[(mp->decoded.payload.size) + 1];
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
payloadStr[mp->decoded.payload.size] = 0; // null terminated string
// check if this is a JSON payload
JSONValue *json_value = JSON::Parse(payloadStr);
if (json_value != NULL) {
payloadStr[mp->decoded.payload.size] = 0;
Json::Value parsed;
if (tryParseJson(payloadStr, parsed)) {
if (shouldLog)
LOG_INFO("text message payload is of type json");
// if it is, then we can just use the json object
jsonObj["payload"] = json_value;
jsonObj["payload"] = parsed;
} else {
// if it isn't, then we need to create a json object
// with the string as the value
if (shouldLog)
LOG_INFO("text message payload is of type plaintext");
msgPayload["text"] = new JSONValue(payloadStr);
jsonObj["payload"] = new JSONValue(msgPayload);
msgPayload["text"] = payloadStr;
jsonObj["payload"] = msgPayload;
}
break;
}
@@ -61,133 +70,129 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
// If battery is present, encode the battery level value
// TODO - Add a condition to send a code for a non-present value
if (decoded->variant.device_metrics.has_battery_level) {
msgPayload["battery_level"] = new JSONValue((int)decoded->variant.device_metrics.battery_level);
msgPayload["battery_level"] = (int)decoded->variant.device_metrics.battery_level;
}
msgPayload["voltage"] = new JSONValue(decoded->variant.device_metrics.voltage);
msgPayload["channel_utilization"] = new JSONValue(decoded->variant.device_metrics.channel_utilization);
msgPayload["air_util_tx"] = new JSONValue(decoded->variant.device_metrics.air_util_tx);
msgPayload["uptime_seconds"] = new JSONValue((unsigned int)decoded->variant.device_metrics.uptime_seconds);
msgPayload["voltage"] = decoded->variant.device_metrics.voltage;
msgPayload["channel_utilization"] = decoded->variant.device_metrics.channel_utilization;
msgPayload["air_util_tx"] = decoded->variant.device_metrics.air_util_tx;
msgPayload["uptime_seconds"] = (Json::UInt)decoded->variant.device_metrics.uptime_seconds;
} else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
// Avoid sending 0s for sensors that could be 0
if (decoded->variant.environment_metrics.has_temperature) {
msgPayload["temperature"] = new JSONValue(decoded->variant.environment_metrics.temperature);
msgPayload["temperature"] = decoded->variant.environment_metrics.temperature;
}
if (decoded->variant.environment_metrics.has_relative_humidity) {
msgPayload["relative_humidity"] = new JSONValue(decoded->variant.environment_metrics.relative_humidity);
msgPayload["relative_humidity"] = decoded->variant.environment_metrics.relative_humidity;
}
if (decoded->variant.environment_metrics.has_barometric_pressure) {
msgPayload["barometric_pressure"] =
new JSONValue(decoded->variant.environment_metrics.barometric_pressure);
msgPayload["barometric_pressure"] = decoded->variant.environment_metrics.barometric_pressure;
}
if (decoded->variant.environment_metrics.has_gas_resistance) {
msgPayload["gas_resistance"] = new JSONValue(decoded->variant.environment_metrics.gas_resistance);
msgPayload["gas_resistance"] = decoded->variant.environment_metrics.gas_resistance;
}
if (decoded->variant.environment_metrics.has_voltage) {
msgPayload["voltage"] = new JSONValue(decoded->variant.environment_metrics.voltage);
msgPayload["voltage"] = decoded->variant.environment_metrics.voltage;
}
if (decoded->variant.environment_metrics.has_current) {
msgPayload["current"] = new JSONValue(decoded->variant.environment_metrics.current);
msgPayload["current"] = decoded->variant.environment_metrics.current;
}
if (decoded->variant.environment_metrics.has_lux) {
msgPayload["lux"] = new JSONValue(decoded->variant.environment_metrics.lux);
msgPayload["lux"] = decoded->variant.environment_metrics.lux;
}
if (decoded->variant.environment_metrics.has_white_lux) {
msgPayload["white_lux"] = new JSONValue(decoded->variant.environment_metrics.white_lux);
msgPayload["white_lux"] = decoded->variant.environment_metrics.white_lux;
}
if (decoded->variant.environment_metrics.has_iaq) {
msgPayload["iaq"] = new JSONValue((uint)decoded->variant.environment_metrics.iaq);
msgPayload["iaq"] = (Json::UInt)decoded->variant.environment_metrics.iaq;
}
if (decoded->variant.environment_metrics.has_distance) {
msgPayload["distance"] = new JSONValue(decoded->variant.environment_metrics.distance);
msgPayload["distance"] = decoded->variant.environment_metrics.distance;
}
if (decoded->variant.environment_metrics.has_wind_speed) {
msgPayload["wind_speed"] = new JSONValue(decoded->variant.environment_metrics.wind_speed);
msgPayload["wind_speed"] = decoded->variant.environment_metrics.wind_speed;
}
if (decoded->variant.environment_metrics.has_wind_direction) {
msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction);
msgPayload["wind_direction"] = (Json::UInt)decoded->variant.environment_metrics.wind_direction;
}
if (decoded->variant.environment_metrics.has_wind_gust) {
msgPayload["wind_gust"] = new JSONValue(decoded->variant.environment_metrics.wind_gust);
msgPayload["wind_gust"] = decoded->variant.environment_metrics.wind_gust;
}
if (decoded->variant.environment_metrics.has_wind_lull) {
msgPayload["wind_lull"] = new JSONValue(decoded->variant.environment_metrics.wind_lull);
msgPayload["wind_lull"] = decoded->variant.environment_metrics.wind_lull;
}
if (decoded->variant.environment_metrics.has_radiation) {
msgPayload["radiation"] = new JSONValue(decoded->variant.environment_metrics.radiation);
msgPayload["radiation"] = decoded->variant.environment_metrics.radiation;
}
if (decoded->variant.environment_metrics.has_ir_lux) {
msgPayload["ir_lux"] = new JSONValue(decoded->variant.environment_metrics.ir_lux);
msgPayload["ir_lux"] = decoded->variant.environment_metrics.ir_lux;
}
if (decoded->variant.environment_metrics.has_uv_lux) {
msgPayload["uv_lux"] = new JSONValue(decoded->variant.environment_metrics.uv_lux);
msgPayload["uv_lux"] = decoded->variant.environment_metrics.uv_lux;
}
if (decoded->variant.environment_metrics.has_weight) {
msgPayload["weight"] = new JSONValue(decoded->variant.environment_metrics.weight);
msgPayload["weight"] = decoded->variant.environment_metrics.weight;
}
if (decoded->variant.environment_metrics.has_rainfall_1h) {
msgPayload["rainfall_1h"] = new JSONValue(decoded->variant.environment_metrics.rainfall_1h);
msgPayload["rainfall_1h"] = decoded->variant.environment_metrics.rainfall_1h;
}
if (decoded->variant.environment_metrics.has_rainfall_24h) {
msgPayload["rainfall_24h"] = new JSONValue(decoded->variant.environment_metrics.rainfall_24h);
msgPayload["rainfall_24h"] = decoded->variant.environment_metrics.rainfall_24h;
}
if (decoded->variant.environment_metrics.has_soil_moisture) {
msgPayload["soil_moisture"] = new JSONValue((uint)decoded->variant.environment_metrics.soil_moisture);
msgPayload["soil_moisture"] = (Json::UInt)decoded->variant.environment_metrics.soil_moisture;
}
if (decoded->variant.environment_metrics.has_soil_temperature) {
msgPayload["soil_temperature"] = new JSONValue(decoded->variant.environment_metrics.soil_temperature);
msgPayload["soil_temperature"] = decoded->variant.environment_metrics.soil_temperature;
}
} else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
if (decoded->variant.air_quality_metrics.has_pm10_standard) {
msgPayload["pm10"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_standard);
msgPayload["pm10"] = (Json::UInt)decoded->variant.air_quality_metrics.pm10_standard;
}
if (decoded->variant.air_quality_metrics.has_pm25_standard) {
msgPayload["pm25"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm25_standard);
msgPayload["pm25"] = (Json::UInt)decoded->variant.air_quality_metrics.pm25_standard;
}
if (decoded->variant.air_quality_metrics.has_pm100_standard) {
msgPayload["pm100"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm100_standard);
msgPayload["pm100"] = (Json::UInt)decoded->variant.air_quality_metrics.pm100_standard;
}
if (decoded->variant.air_quality_metrics.has_co2) {
msgPayload["co2"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.co2);
msgPayload["co2"] = (Json::UInt)decoded->variant.air_quality_metrics.co2;
}
if (decoded->variant.air_quality_metrics.has_co2_temperature) {
msgPayload["co2_temperature"] = new JSONValue(decoded->variant.air_quality_metrics.co2_temperature);
msgPayload["co2_temperature"] = decoded->variant.air_quality_metrics.co2_temperature;
}
if (decoded->variant.air_quality_metrics.has_co2_humidity) {
msgPayload["co2_humidity"] = new JSONValue(decoded->variant.air_quality_metrics.co2_humidity);
msgPayload["co2_humidity"] = decoded->variant.air_quality_metrics.co2_humidity;
}
if (decoded->variant.air_quality_metrics.has_form_formaldehyde) {
msgPayload["form_formaldehyde"] = new JSONValue(decoded->variant.air_quality_metrics.form_formaldehyde);
msgPayload["form_formaldehyde"] = decoded->variant.air_quality_metrics.form_formaldehyde;
}
if (decoded->variant.air_quality_metrics.has_form_temperature) {
msgPayload["form_temperature"] = new JSONValue(decoded->variant.air_quality_metrics.form_temperature);
msgPayload["form_temperature"] = decoded->variant.air_quality_metrics.form_temperature;
}
if (decoded->variant.air_quality_metrics.has_form_humidity) {
msgPayload["form_humidity"] = new JSONValue(decoded->variant.air_quality_metrics.form_humidity);
msgPayload["form_humidity"] = decoded->variant.air_quality_metrics.form_humidity;
}
} else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
if (decoded->variant.power_metrics.has_ch1_voltage) {
msgPayload["voltage_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_voltage);
msgPayload["voltage_ch1"] = decoded->variant.power_metrics.ch1_voltage;
}
if (decoded->variant.power_metrics.has_ch1_current) {
msgPayload["current_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_current);
msgPayload["current_ch1"] = decoded->variant.power_metrics.ch1_current;
}
if (decoded->variant.power_metrics.has_ch2_voltage) {
msgPayload["voltage_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_voltage);
msgPayload["voltage_ch2"] = decoded->variant.power_metrics.ch2_voltage;
}
if (decoded->variant.power_metrics.has_ch2_current) {
msgPayload["current_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_current);
msgPayload["current_ch2"] = decoded->variant.power_metrics.ch2_current;
}
if (decoded->variant.power_metrics.has_ch3_voltage) {
msgPayload["voltage_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_voltage);
msgPayload["voltage_ch3"] = decoded->variant.power_metrics.ch3_voltage;
}
if (decoded->variant.power_metrics.has_ch3_current) {
msgPayload["current_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_current);
msgPayload["current_ch3"] = decoded->variant.power_metrics.ch3_current;
}
}
jsonObj["payload"] = new JSONValue(msgPayload);
jsonObj["payload"] = msgPayload;
} else if (shouldLog) {
LOG_ERROR(errStr, msgType.c_str());
}
@@ -200,12 +205,12 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) {
decoded = &scratch;
msgPayload["id"] = new JSONValue(decoded->id);
msgPayload["longname"] = new JSONValue(decoded->long_name);
msgPayload["shortname"] = new JSONValue(decoded->short_name);
msgPayload["hardware"] = new JSONValue(decoded->hw_model);
msgPayload["role"] = new JSONValue((int)decoded->role);
jsonObj["payload"] = new JSONValue(msgPayload);
msgPayload["id"] = decoded->id;
msgPayload["longname"] = decoded->long_name;
msgPayload["shortname"] = decoded->short_name;
msgPayload["hardware"] = (int)decoded->hw_model;
msgPayload["role"] = (int)decoded->role;
jsonObj["payload"] = msgPayload;
} else if (shouldLog) {
LOG_ERROR(errStr, msgType.c_str());
}
@@ -219,38 +224,38 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) {
decoded = &scratch;
if ((int)decoded->time) {
msgPayload["time"] = new JSONValue((unsigned int)decoded->time);
msgPayload["time"] = (Json::UInt)decoded->time;
}
if ((int)decoded->timestamp) {
msgPayload["timestamp"] = new JSONValue((unsigned int)decoded->timestamp);
msgPayload["timestamp"] = (Json::UInt)decoded->timestamp;
}
msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i);
msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i);
msgPayload["latitude_i"] = (int)decoded->latitude_i;
msgPayload["longitude_i"] = (int)decoded->longitude_i;
if ((int)decoded->altitude) {
msgPayload["altitude"] = new JSONValue((int)decoded->altitude);
msgPayload["altitude"] = (int)decoded->altitude;
}
if ((int)decoded->ground_speed) {
msgPayload["ground_speed"] = new JSONValue((unsigned int)decoded->ground_speed);
msgPayload["ground_speed"] = (Json::UInt)decoded->ground_speed;
}
if (int(decoded->ground_track)) {
msgPayload["ground_track"] = new JSONValue((unsigned int)decoded->ground_track);
msgPayload["ground_track"] = (Json::UInt)decoded->ground_track;
}
if (int(decoded->sats_in_view)) {
msgPayload["sats_in_view"] = new JSONValue((unsigned int)decoded->sats_in_view);
msgPayload["sats_in_view"] = (Json::UInt)decoded->sats_in_view;
}
if ((int)decoded->PDOP) {
msgPayload["PDOP"] = new JSONValue((int)decoded->PDOP);
msgPayload["PDOP"] = (int)decoded->PDOP;
}
if ((int)decoded->HDOP) {
msgPayload["HDOP"] = new JSONValue((int)decoded->HDOP);
msgPayload["HDOP"] = (int)decoded->HDOP;
}
if ((int)decoded->VDOP) {
msgPayload["VDOP"] = new JSONValue((int)decoded->VDOP);
msgPayload["VDOP"] = (int)decoded->VDOP;
}
if ((int)decoded->precision_bits) {
msgPayload["precision_bits"] = new JSONValue((int)decoded->precision_bits);
msgPayload["precision_bits"] = (int)decoded->precision_bits;
}
jsonObj["payload"] = new JSONValue(msgPayload);
jsonObj["payload"] = msgPayload;
} else if (shouldLog) {
LOG_ERROR(errStr, msgType.c_str());
}
@@ -263,14 +268,14 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
decoded = &scratch;
msgPayload["id"] = new JSONValue((unsigned int)decoded->id);
msgPayload["name"] = new JSONValue(decoded->name);
msgPayload["description"] = new JSONValue(decoded->description);
msgPayload["expire"] = new JSONValue((unsigned int)decoded->expire);
msgPayload["locked_to"] = new JSONValue((unsigned int)decoded->locked_to);
msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i);
msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i);
jsonObj["payload"] = new JSONValue(msgPayload);
msgPayload["id"] = (Json::UInt)decoded->id;
msgPayload["name"] = decoded->name;
msgPayload["description"] = decoded->description;
msgPayload["expire"] = (Json::UInt)decoded->expire;
msgPayload["locked_to"] = (Json::UInt)decoded->locked_to;
msgPayload["latitude_i"] = (int)decoded->latitude_i;
msgPayload["longitude_i"] = (int)decoded->longitude_i;
jsonObj["payload"] = msgPayload;
} else if (shouldLog) {
LOG_ERROR(errStr, msgType.c_str());
}
@@ -284,26 +289,26 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg,
&scratch)) {
decoded = &scratch;
msgPayload["node_id"] = new JSONValue((unsigned int)decoded->node_id);
msgPayload["node_broadcast_interval_secs"] = new JSONValue((unsigned int)decoded->node_broadcast_interval_secs);
msgPayload["last_sent_by_id"] = new JSONValue((unsigned int)decoded->last_sent_by_id);
msgPayload["neighbors_count"] = new JSONValue(decoded->neighbors_count);
JSONArray neighbors;
msgPayload["node_id"] = (Json::UInt)decoded->node_id;
msgPayload["node_broadcast_interval_secs"] = (Json::UInt)decoded->node_broadcast_interval_secs;
msgPayload["last_sent_by_id"] = (Json::UInt)decoded->last_sent_by_id;
msgPayload["neighbors_count"] = (Json::UInt)decoded->neighbors_count;
Json::Value neighbors(Json::arrayValue);
for (uint8_t i = 0; i < decoded->neighbors_count; i++) {
JSONObject neighborObj;
neighborObj["node_id"] = new JSONValue((unsigned int)decoded->neighbors[i].node_id);
neighborObj["snr"] = new JSONValue((int)decoded->neighbors[i].snr);
neighbors.push_back(new JSONValue(neighborObj));
Json::Value neighborObj(Json::objectValue);
neighborObj["node_id"] = (Json::UInt)decoded->neighbors[i].node_id;
neighborObj["snr"] = (int)decoded->neighbors[i].snr;
neighbors.append(neighborObj);
}
msgPayload["neighbors"] = new JSONValue(neighbors);
jsonObj["payload"] = new JSONValue(msgPayload);
msgPayload["neighbors"] = neighbors;
jsonObj["payload"] = msgPayload;
} else if (shouldLog) {
LOG_ERROR(errStr, msgType.c_str());
}
break;
}
case meshtastic_PortNum_TRACEROUTE_APP: {
if (mp->decoded.request_id) { // Only report the traceroute response
if (mp->decoded.request_id) {
msgType = "traceroute";
meshtastic_RouteDiscovery scratch;
meshtastic_RouteDiscovery *decoded = NULL;
@@ -311,13 +316,12 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg,
&scratch)) {
decoded = &scratch;
JSONArray route; // Route this message took
JSONArray routeBack; // Route this message took back
JSONArray snrTowards; // Snr for forward route
JSONArray snrBack; // Snr for reverse route
Json::Value route(Json::arrayValue);
Json::Value routeBack(Json::arrayValue);
Json::Value snrTowards(Json::arrayValue);
Json::Value snrBack(Json::arrayValue);
// Lambda function for adding a long name to the route
auto addToRoute = [](JSONArray *route, NodeNum num) {
auto addToRoute = [](Json::Value *r, NodeNum num) {
char long_name[40] = "Unknown";
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
bool name_known = nodeInfoLiteHasUser(node);
@@ -327,33 +331,32 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
memcpy(long_name, node->long_name, copy_len);
long_name[copy_len] = '\0';
}
route->push_back(new JSONValue(long_name));
r->append(long_name);
};
addToRoute(&route, mp->to); // Started at the original transmitter (destination of response)
addToRoute(&route, mp->to);
for (uint8_t i = 0; i < decoded->route_count; i++) {
addToRoute(&route, decoded->route[i]);
}
addToRoute(&route, mp->from); // Ended at the original destination (source of response)
addToRoute(&route, mp->from);
addToRoute(&routeBack, mp->from); // Started at the original destination (source of response)
addToRoute(&routeBack, mp->from);
for (uint8_t i = 0; i < decoded->route_back_count; i++) {
addToRoute(&routeBack, decoded->route_back[i]);
}
addToRoute(&routeBack, mp->to); // Ended at the original transmitter (destination of response)
addToRoute(&routeBack, mp->to);
for (uint8_t i = 0; i < decoded->snr_back_count; i++) {
snrBack.push_back(new JSONValue((float)decoded->snr_back[i] / 4));
snrBack.append((float)decoded->snr_back[i] / 4);
}
for (uint8_t i = 0; i < decoded->snr_towards_count; i++) {
snrTowards.push_back(new JSONValue((float)decoded->snr_towards[i] / 4));
snrTowards.append((float)decoded->snr_towards[i] / 4);
}
msgPayload["route"] = new JSONValue(route);
msgPayload["route_back"] = new JSONValue(routeBack);
msgPayload["snr_back"] = new JSONValue(snrBack);
msgPayload["snr_towards"] = new JSONValue(snrTowards);
jsonObj["payload"] = new JSONValue(msgPayload);
msgPayload["route"] = route;
msgPayload["route_back"] = routeBack;
msgPayload["snr_back"] = snrBack;
msgPayload["snr_towards"] = snrTowards;
jsonObj["payload"] = msgPayload;
} else if (shouldLog) {
LOG_ERROR(errStr, msgType.c_str());
}
@@ -364,9 +367,9 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
msgType = "detection";
char payloadStr[(mp->decoded.payload.size) + 1];
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
payloadStr[mp->decoded.payload.size] = 0; // null terminated string
msgPayload["text"] = new JSONValue(payloadStr);
jsonObj["payload"] = new JSONValue(msgPayload);
payloadStr[mp->decoded.payload.size] = 0;
msgPayload["text"] = payloadStr;
jsonObj["payload"] = msgPayload;
break;
}
#ifdef ARCH_ESP32
@@ -377,10 +380,10 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Paxcount_msg, &scratch)) {
decoded = &scratch;
msgPayload["wifi_count"] = new JSONValue((unsigned int)decoded->wifi);
msgPayload["ble_count"] = new JSONValue((unsigned int)decoded->ble);
msgPayload["uptime"] = new JSONValue((unsigned int)decoded->uptime);
jsonObj["payload"] = new JSONValue(msgPayload);
msgPayload["wifi_count"] = (Json::UInt)decoded->wifi;
msgPayload["ble_count"] = (Json::UInt)decoded->ble;
msgPayload["uptime"] = (Json::UInt)decoded->uptime;
jsonObj["payload"] = msgPayload;
} else if (shouldLog) {
LOG_ERROR(errStr, msgType.c_str());
}
@@ -396,20 +399,19 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
decoded = &scratch;
if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) {
msgType = "gpios_changed";
msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value);
jsonObj["payload"] = new JSONValue(msgPayload);
msgPayload["gpio_value"] = (Json::UInt)decoded->gpio_value;
jsonObj["payload"] = msgPayload;
} else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) {
msgType = "gpios_read_reply";
msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value);
msgPayload["gpio_mask"] = new JSONValue((unsigned int)decoded->gpio_mask);
jsonObj["payload"] = new JSONValue(msgPayload);
msgPayload["gpio_value"] = (Json::UInt)decoded->gpio_value;
msgPayload["gpio_mask"] = (Json::UInt)decoded->gpio_mask;
jsonObj["payload"] = msgPayload;
}
} else if (shouldLog) {
LOG_ERROR(errStr, "RemoteHardware");
}
break;
}
// add more packet types here if needed
default:
break;
}
@@ -417,64 +419,56 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON");
}
jsonObj["id"] = new JSONValue((unsigned int)mp->id);
jsonObj["timestamp"] = new JSONValue((unsigned int)mp->rx_time);
jsonObj["to"] = new JSONValue((unsigned int)mp->to);
jsonObj["from"] = new JSONValue((unsigned int)mp->from);
jsonObj["channel"] = new JSONValue((unsigned int)mp->channel);
jsonObj["type"] = new JSONValue(msgType.c_str());
jsonObj["sender"] = new JSONValue(nodeDB->getNodeId().c_str());
jsonObj["id"] = (Json::UInt)mp->id;
jsonObj["timestamp"] = (Json::UInt)mp->rx_time;
jsonObj["to"] = (Json::UInt)mp->to;
jsonObj["from"] = (Json::UInt)mp->from;
jsonObj["channel"] = (Json::UInt)mp->channel;
jsonObj["type"] = msgType;
jsonObj["sender"] = nodeDB->getNodeId();
if (mp->rx_rssi != 0)
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
jsonObj["rssi"] = (int)mp->rx_rssi;
if (mp->rx_snr != 0)
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
jsonObj["snr"] = (float)mp->rx_snr;
const int8_t hopsAway = getHopsAway(*mp);
if (hopsAway >= 0) {
jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway));
jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start));
jsonObj["hops_away"] = (Json::UInt)(hopsAway);
jsonObj["hop_start"] = (Json::UInt)(mp->hop_start);
}
// serialize and write it to the stream
JSONValue *value = new JSONValue(jsonObj);
std::string jsonStr = value->Stringify();
std::string jsonStr = writeCompact(jsonObj);
if (shouldLog)
LOG_INFO("serialized json message: %s", jsonStr.c_str());
delete value;
return jsonStr;
}
std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPacket *mp)
{
JSONObject jsonObj;
Json::Value jsonObj(Json::objectValue);
jsonObj["id"] = new JSONValue((unsigned int)mp->id);
jsonObj["time_ms"] = new JSONValue((double)millis());
jsonObj["timestamp"] = new JSONValue((unsigned int)mp->rx_time);
jsonObj["to"] = new JSONValue((unsigned int)mp->to);
jsonObj["from"] = new JSONValue((unsigned int)mp->from);
jsonObj["channel"] = new JSONValue((unsigned int)mp->channel);
jsonObj["want_ack"] = new JSONValue(mp->want_ack);
jsonObj["id"] = (Json::UInt)mp->id;
jsonObj["time_ms"] = (double)millis();
jsonObj["timestamp"] = (Json::UInt)mp->rx_time;
jsonObj["to"] = (Json::UInt)mp->to;
jsonObj["from"] = (Json::UInt)mp->from;
jsonObj["channel"] = (Json::UInt)mp->channel;
jsonObj["want_ack"] = mp->want_ack;
if (mp->rx_rssi != 0)
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
jsonObj["rssi"] = (int)mp->rx_rssi;
if (mp->rx_snr != 0)
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
jsonObj["snr"] = (float)mp->rx_snr;
const int8_t hopsAway = getHopsAway(*mp);
if (hopsAway >= 0) {
jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway));
jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start));
jsonObj["hops_away"] = (Json::UInt)(hopsAway);
jsonObj["hop_start"] = (Json::UInt)(mp->hop_start);
}
jsonObj["size"] = new JSONValue((unsigned int)mp->encrypted.size);
jsonObj["size"] = (Json::UInt)mp->encrypted.size;
auto encryptedStr = bytesToHex(mp->encrypted.bytes, mp->encrypted.size);
jsonObj["bytes"] = new JSONValue(encryptedStr.c_str());
jsonObj["bytes"] = encryptedStr;
// serialize and write it to the stream
JSONValue *value = new JSONValue(jsonObj);
std::string jsonStr = value->Stringify();
delete value;
return jsonStr;
return writeCompact(jsonObj);
}
#endif
@@ -1,425 +0,0 @@
#ifdef NRF52_USE_JSON
#warning 'Using nRF52 Serializer'
#include "ArduinoJson.h"
#include "MeshPacketSerializer.h"
#include "NodeDB.h"
#include "mesh/generated/meshtastic/mqtt.pb.h"
#include "mesh/generated/meshtastic/remote_hardware.pb.h"
#include "mesh/generated/meshtastic/telemetry.pb.h"
#include "modules/RoutingModule.h"
#include <DebugConfiguration.h>
#include <mesh-pb-constants.h>
StaticJsonDocument<1024> jsonObj;
StaticJsonDocument<1024> arrayObj;
std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog)
{
// the created jsonObj is immutable after creation, so
// we need to do the heavy lifting before assembling it.
std::string msgType;
jsonObj.clear();
arrayObj.clear();
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
switch (mp->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP: {
msgType = "text";
// convert bytes to string
if (shouldLog)
LOG_DEBUG("got text message of size %u", mp->decoded.payload.size);
char payloadStr[(mp->decoded.payload.size) + 1];
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
payloadStr[mp->decoded.payload.size] = 0; // null terminated string
// check if this is a JSON payload
StaticJsonDocument<512> text_doc;
DeserializationError error = deserializeJson(text_doc, payloadStr);
if (error) {
// if it isn't, then we need to create a json object
// with the string as the value
if (shouldLog)
LOG_INFO("text message payload is of type plaintext");
jsonObj["payload"]["text"] = payloadStr;
} else {
// if it is, then we can just use the json object
if (shouldLog)
LOG_INFO("text message payload is of type json");
jsonObj["payload"] = text_doc;
}
break;
}
case meshtastic_PortNum_TELEMETRY_APP: {
msgType = "telemetry";
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
// If battery is present, encode the battery level value
// TODO - Add a condition to send a code for a non-present value
if (decoded->variant.device_metrics.has_battery_level) {
jsonObj["payload"]["battery_level"] = (int)decoded->variant.device_metrics.battery_level;
}
jsonObj["payload"]["voltage"] = decoded->variant.device_metrics.voltage;
jsonObj["payload"]["channel_utilization"] = decoded->variant.device_metrics.channel_utilization;
jsonObj["payload"]["air_util_tx"] = decoded->variant.device_metrics.air_util_tx;
jsonObj["payload"]["uptime_seconds"] = (unsigned int)decoded->variant.device_metrics.uptime_seconds;
} else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
if (decoded->variant.environment_metrics.has_temperature) {
jsonObj["payload"]["temperature"] = decoded->variant.environment_metrics.temperature;
}
if (decoded->variant.environment_metrics.has_relative_humidity) {
jsonObj["payload"]["relative_humidity"] = decoded->variant.environment_metrics.relative_humidity;
}
if (decoded->variant.environment_metrics.has_barometric_pressure) {
jsonObj["payload"]["barometric_pressure"] = decoded->variant.environment_metrics.barometric_pressure;
}
if (decoded->variant.environment_metrics.has_gas_resistance) {
jsonObj["payload"]["gas_resistance"] = decoded->variant.environment_metrics.gas_resistance;
}
if (decoded->variant.environment_metrics.has_voltage) {
jsonObj["payload"]["voltage"] = decoded->variant.environment_metrics.voltage;
}
if (decoded->variant.environment_metrics.has_current) {
jsonObj["payload"]["current"] = decoded->variant.environment_metrics.current;
}
if (decoded->variant.environment_metrics.has_lux) {
jsonObj["payload"]["lux"] = decoded->variant.environment_metrics.lux;
}
if (decoded->variant.environment_metrics.has_white_lux) {
jsonObj["payload"]["white_lux"] = decoded->variant.environment_metrics.white_lux;
}
if (decoded->variant.environment_metrics.has_iaq) {
jsonObj["payload"]["iaq"] = (uint)decoded->variant.environment_metrics.iaq;
}
if (decoded->variant.environment_metrics.has_wind_speed) {
jsonObj["payload"]["wind_speed"] = decoded->variant.environment_metrics.wind_speed;
}
if (decoded->variant.environment_metrics.has_wind_direction) {
jsonObj["payload"]["wind_direction"] = (uint)decoded->variant.environment_metrics.wind_direction;
}
if (decoded->variant.environment_metrics.has_wind_gust) {
jsonObj["payload"]["wind_gust"] = decoded->variant.environment_metrics.wind_gust;
}
if (decoded->variant.environment_metrics.has_wind_lull) {
jsonObj["payload"]["wind_lull"] = decoded->variant.environment_metrics.wind_lull;
}
if (decoded->variant.environment_metrics.has_radiation) {
jsonObj["payload"]["radiation"] = decoded->variant.environment_metrics.radiation;
}
} else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
if (decoded->variant.air_quality_metrics.has_pm10_standard) {
jsonObj["payload"]["pm10"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_standard;
}
if (decoded->variant.air_quality_metrics.has_pm25_standard) {
jsonObj["payload"]["pm25"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_standard;
}
if (decoded->variant.air_quality_metrics.has_pm100_standard) {
jsonObj["payload"]["pm100"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_standard;
}
if (decoded->variant.air_quality_metrics.has_co2) {
jsonObj["payload"]["co2"] = (unsigned int)decoded->variant.air_quality_metrics.co2;
}
if (decoded->variant.air_quality_metrics.has_co2_temperature) {
jsonObj["payload"]["co2_temperature"] = decoded->variant.air_quality_metrics.co2_temperature;
}
if (decoded->variant.air_quality_metrics.has_co2_humidity) {
jsonObj["payload"]["co2_humidity"] = decoded->variant.air_quality_metrics.co2_humidity;
}
if (decoded->variant.air_quality_metrics.has_form_formaldehyde) {
jsonObj["payload"]["form_formaldehyde"] = decoded->variant.air_quality_metrics.form_formaldehyde;
}
if (decoded->variant.air_quality_metrics.has_form_temperature) {
jsonObj["payload"]["form_temperature"] = decoded->variant.air_quality_metrics.form_temperature;
}
if (decoded->variant.air_quality_metrics.has_form_humidity) {
jsonObj["payload"]["form_humidity"] = decoded->variant.air_quality_metrics.form_humidity;
}
} else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
if (decoded->variant.power_metrics.has_ch1_voltage) {
jsonObj["payload"]["voltage_ch1"] = decoded->variant.power_metrics.ch1_voltage;
}
if (decoded->variant.power_metrics.has_ch1_current) {
jsonObj["payload"]["current_ch1"] = decoded->variant.power_metrics.ch1_current;
}
if (decoded->variant.power_metrics.has_ch2_voltage) {
jsonObj["payload"]["voltage_ch2"] = decoded->variant.power_metrics.ch2_voltage;
}
if (decoded->variant.power_metrics.has_ch2_current) {
jsonObj["payload"]["current_ch2"] = decoded->variant.power_metrics.ch2_current;
}
if (decoded->variant.power_metrics.has_ch3_voltage) {
jsonObj["payload"]["voltage_ch3"] = decoded->variant.power_metrics.ch3_voltage;
}
if (decoded->variant.power_metrics.has_ch3_current) {
jsonObj["payload"]["current_ch3"] = decoded->variant.power_metrics.ch3_current;
}
}
} else if (shouldLog) {
LOG_ERROR("Error decoding proto for telemetry message!");
return "";
}
break;
}
case meshtastic_PortNum_NODEINFO_APP: {
msgType = "nodeinfo";
meshtastic_User scratch;
meshtastic_User *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) {
decoded = &scratch;
jsonObj["payload"]["id"] = decoded->id;
jsonObj["payload"]["longname"] = decoded->long_name;
jsonObj["payload"]["shortname"] = decoded->short_name;
jsonObj["payload"]["hardware"] = decoded->hw_model;
jsonObj["payload"]["role"] = (int)decoded->role;
} else if (shouldLog) {
LOG_ERROR("Error decoding proto for nodeinfo message!");
return "";
}
break;
}
case meshtastic_PortNum_POSITION_APP: {
msgType = "position";
meshtastic_Position scratch;
meshtastic_Position *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) {
decoded = &scratch;
if ((int)decoded->time) {
jsonObj["payload"]["time"] = (unsigned int)decoded->time;
}
if ((int)decoded->timestamp) {
jsonObj["payload"]["timestamp"] = (unsigned int)decoded->timestamp;
}
jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i;
jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i;
if ((int)decoded->altitude) {
jsonObj["payload"]["altitude"] = (int)decoded->altitude;
}
if ((int)decoded->ground_speed) {
jsonObj["payload"]["ground_speed"] = (unsigned int)decoded->ground_speed;
}
if (int(decoded->ground_track)) {
jsonObj["payload"]["ground_track"] = (unsigned int)decoded->ground_track;
}
if (int(decoded->sats_in_view)) {
jsonObj["payload"]["sats_in_view"] = (unsigned int)decoded->sats_in_view;
}
if ((int)decoded->PDOP) {
jsonObj["payload"]["PDOP"] = (int)decoded->PDOP;
}
if ((int)decoded->HDOP) {
jsonObj["payload"]["HDOP"] = (int)decoded->HDOP;
}
if ((int)decoded->VDOP) {
jsonObj["payload"]["VDOP"] = (int)decoded->VDOP;
}
if ((int)decoded->precision_bits) {
jsonObj["payload"]["precision_bits"] = (int)decoded->precision_bits;
}
} else if (shouldLog) {
LOG_ERROR("Error decoding proto for position message!");
return "";
}
break;
}
case meshtastic_PortNum_WAYPOINT_APP: {
msgType = "position";
meshtastic_Waypoint scratch;
meshtastic_Waypoint *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
decoded = &scratch;
jsonObj["payload"]["id"] = (unsigned int)decoded->id;
jsonObj["payload"]["name"] = decoded->name;
jsonObj["payload"]["description"] = decoded->description;
jsonObj["payload"]["expire"] = (unsigned int)decoded->expire;
jsonObj["payload"]["locked_to"] = (unsigned int)decoded->locked_to;
jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i;
jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i;
} else if (shouldLog) {
LOG_ERROR("Error decoding proto for position message!");
return "";
}
break;
}
case meshtastic_PortNum_NEIGHBORINFO_APP: {
msgType = "neighborinfo";
meshtastic_NeighborInfo scratch;
meshtastic_NeighborInfo *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg,
&scratch)) {
decoded = &scratch;
jsonObj["payload"]["node_id"] = (unsigned int)decoded->node_id;
jsonObj["payload"]["node_broadcast_interval_secs"] = (unsigned int)decoded->node_broadcast_interval_secs;
jsonObj["payload"]["last_sent_by_id"] = (unsigned int)decoded->last_sent_by_id;
jsonObj["payload"]["neighbors_count"] = decoded->neighbors_count;
JsonObject neighbors_obj = arrayObj.to<JsonObject>();
JsonArray neighbors = neighbors_obj.createNestedArray("neighbors");
JsonObject neighbors_0 = neighbors.createNestedObject();
for (uint8_t i = 0; i < decoded->neighbors_count; i++) {
neighbors_0["node_id"] = (unsigned int)decoded->neighbors[i].node_id;
neighbors_0["snr"] = (int)decoded->neighbors[i].snr;
neighbors[i + 1] = neighbors_0;
neighbors_0.clear();
}
neighbors.remove(0);
jsonObj["payload"]["neighbors"] = neighbors;
} else if (shouldLog) {
LOG_ERROR("Error decoding proto for neighborinfo message!");
return "";
}
break;
}
case meshtastic_PortNum_TRACEROUTE_APP: {
if (mp->decoded.request_id) { // Only report the traceroute response
msgType = "traceroute";
meshtastic_RouteDiscovery scratch;
meshtastic_RouteDiscovery *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg,
&scratch)) {
decoded = &scratch;
JsonArray route = arrayObj.createNestedArray("route");
auto addToRoute = [](JsonArray *route, NodeNum num) {
char long_name[40] = "Unknown";
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
bool name_known = nodeInfoLiteHasUser(node);
if (name_known) {
const size_t copy_len =
(sizeof(node->long_name) < sizeof(long_name)) ? sizeof(node->long_name) : sizeof(long_name) - 1;
memcpy(long_name, node->long_name, copy_len);
long_name[copy_len] = '\0';
}
route->add(long_name);
};
addToRoute(&route, mp->to); // route.add(mp->to);
for (uint8_t i = 0; i < decoded->route_count; i++) {
addToRoute(&route, decoded->route[i]); // route.add(decoded->route[i]);
}
addToRoute(&route,
mp->from); // route.add(mp->from); // Ended at the original destination (source of response)
jsonObj["payload"]["route"] = route;
} else if (shouldLog) {
LOG_ERROR("Error decoding proto for traceroute message!");
return "";
}
} else {
LOG_WARN("Traceroute response not reported");
return "";
}
break;
}
case meshtastic_PortNum_DETECTION_SENSOR_APP: {
msgType = "detection";
char payloadStr[(mp->decoded.payload.size) + 1];
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
payloadStr[mp->decoded.payload.size] = 0; // null terminated string
jsonObj["payload"]["text"] = payloadStr;
break;
}
case meshtastic_PortNum_REMOTE_HARDWARE_APP: {
meshtastic_HardwareMessage scratch;
meshtastic_HardwareMessage *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_HardwareMessage_msg,
&scratch)) {
decoded = &scratch;
if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) {
msgType = "gpios_changed";
jsonObj["payload"]["gpio_value"] = (unsigned int)decoded->gpio_value;
} else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) {
msgType = "gpios_read_reply";
jsonObj["payload"]["gpio_value"] = (unsigned int)decoded->gpio_value;
jsonObj["payload"]["gpio_mask"] = (unsigned int)decoded->gpio_mask;
}
} else if (shouldLog) {
LOG_ERROR("Error decoding proto for RemoteHardware message!");
return "";
}
break;
}
// add more packet types here if needed
default:
LOG_WARN("Unsupported packet type %d", mp->decoded.portnum);
return "";
break;
}
} else if (shouldLog) {
LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON");
return "";
}
jsonObj["id"] = (unsigned int)mp->id;
jsonObj["timestamp"] = (unsigned int)mp->rx_time;
jsonObj["to"] = (unsigned int)mp->to;
jsonObj["from"] = (unsigned int)mp->from;
jsonObj["channel"] = (unsigned int)mp->channel;
jsonObj["type"] = msgType.c_str();
jsonObj["sender"] = nodeDB->getNodeId().c_str();
if (mp->rx_rssi != 0)
jsonObj["rssi"] = (int)mp->rx_rssi;
if (mp->rx_snr != 0)
jsonObj["snr"] = (float)mp->rx_snr;
const int8_t hopsAway = getHopsAway(*mp);
if (hopsAway >= 0) {
jsonObj["hops_away"] = (unsigned int)(hopsAway);
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
}
// serialize and write it to the stream
// Serial.printf("serialized json message: \r");
// serializeJson(jsonObj, Serial);
// Serial.println("");
std::string jsonStr = "";
serializeJson(jsonObj, jsonStr);
if (shouldLog)
LOG_INFO("serialized json message: %s", jsonStr.c_str());
return jsonStr;
}
std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPacket *mp)
{
jsonObj.clear();
jsonObj["id"] = (unsigned int)mp->id;
jsonObj["time_ms"] = (double)millis();
jsonObj["timestamp"] = (unsigned int)mp->rx_time;
jsonObj["to"] = (unsigned int)mp->to;
jsonObj["from"] = (unsigned int)mp->from;
jsonObj["channel"] = (unsigned int)mp->channel;
jsonObj["want_ack"] = mp->want_ack;
if (mp->rx_rssi != 0)
jsonObj["rssi"] = (int)mp->rx_rssi;
if (mp->rx_snr != 0)
jsonObj["snr"] = (float)mp->rx_snr;
const int8_t hopsAway = getHopsAway(*mp);
if (hopsAway >= 0) {
jsonObj["hops_away"] = (unsigned int)(hopsAway);
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
}
jsonObj["size"] = (unsigned int)mp->encrypted.size;
auto encryptedStr = bytesToHex(mp->encrypted.bytes, mp->encrypted.size);
jsonObj["bytes"] = encryptedStr.c_str();
// serialize and write it to the stream
std::string jsonStr = "";
serializeJson(jsonObj, jsonStr);
return jsonStr;
}
#endif