mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-23 09:06:02 +00:00
Fix formatting
This commit is contained in:
parent
a388e78842
commit
ca8d2204ba
@ -96,7 +96,7 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
}
|
||||
#if defined(RAK_4631)
|
||||
#if !defined (MESHTASTIC_EXCLUDE_SCREEN)
|
||||
} else if (acceleremoter_type == ScanI2C::DeviceType::BMX160) {
|
||||
} else if (acceleremoter_type == ScanI2C::DeviceType::BMX160) {
|
||||
sBmx160SensorData_t magAccel;
|
||||
sBmx160SensorData_t gAccel;
|
||||
|
||||
@ -232,7 +232,7 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
bmaSensor.enableWakeupIRQ();
|
||||
#ifdef RAK_4631
|
||||
#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
|
||||
} else if (acceleremoter_type == ScanI2C::DeviceType::BMX160 && bmx160.begin()) {
|
||||
} else if (acceleremoter_type == ScanI2C::DeviceType::BMX160 && bmx160.begin()) {
|
||||
bmx160.ODR_Config(BMX160_ACCEL_ODR_100HZ, BMX160_GYRO_ODR_100HZ); // set output data rate
|
||||
#endif
|
||||
#endif
|
||||
@ -268,7 +268,7 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
SensorBMA423 bmaSensor;
|
||||
bool BMA_IRQ = false;
|
||||
#if defined(RAK_4631) && !defined(MESHTASTIC_EXCLUDE_SCREEN)
|
||||
bool showingScreen = false;
|
||||
bool showingScreen = false;
|
||||
RAK_BMX160 bmx160;
|
||||
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
||||
|
||||
|
1103
src/mqtt/MQTT.cpp
1103
src/mqtt/MQTT.cpp
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ static BLEBas blebas; // BAS (Battery Service) helper class instance
|
||||
#ifndef BLE_DFU_SECURE
|
||||
static BLEDfu bledfu; // DFU software update helper service
|
||||
#else
|
||||
static BLEDfuSecure bledfusecure; // DFU software update helper service
|
||||
static BLEDfuSecure bledfusecure; // DFU software update helper service
|
||||
#endif
|
||||
|
||||
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
|
||||
@ -32,23 +32,23 @@ static uint16_t connectionHandle;
|
||||
|
||||
class BluetoothPhoneAPI : public PhoneAPI
|
||||
{
|
||||
/**
|
||||
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
||||
*/
|
||||
virtual void onNowHasData(uint32_t fromRadioNum) override
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
/**
|
||||
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
||||
*/
|
||||
virtual void onNowHasData(uint32_t fromRadioNum) override
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
LOG_INFO("BLE notify fromNum\n");
|
||||
fromNum.notify32(fromRadioNum);
|
||||
}
|
||||
LOG_INFO("BLE notify fromNum\n");
|
||||
fromNum.notify32(fromRadioNum);
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override
|
||||
{
|
||||
BLEConnection *connection = Bluefruit.Connection(connectionHandle);
|
||||
return connection->connected();
|
||||
}
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override
|
||||
{
|
||||
BLEConnection *connection = Bluefruit.Connection(connectionHandle);
|
||||
return connection->connected();
|
||||
}
|
||||
};
|
||||
|
||||
static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
@ -56,12 +56,12 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
void onConnect(uint16_t conn_handle)
|
||||
{
|
||||
|
||||
// Get the reference to current connection
|
||||
BLEConnection *connection = Bluefruit.Connection(conn_handle);
|
||||
connectionHandle = conn_handle;
|
||||
char central_name[32] = {0};
|
||||
connection->getPeerName(central_name, sizeof(central_name));
|
||||
LOG_INFO("BLE Connected to %s\n", central_name);
|
||||
// Get the reference to current connection
|
||||
BLEConnection *connection = Bluefruit.Connection(conn_handle);
|
||||
connectionHandle = conn_handle;
|
||||
char central_name[32] = {0};
|
||||
connection->getPeerName(central_name, sizeof(central_name));
|
||||
LOG_INFO("BLE Connected to %s\n", central_name);
|
||||
}
|
||||
/**
|
||||
* Callback invoked when a connection is dropped
|
||||
@ -70,246 +70,258 @@ void onConnect(uint16_t conn_handle)
|
||||
*/
|
||||
void onDisconnect(uint16_t conn_handle, uint8_t reason)
|
||||
{
|
||||
// FIXME - we currently assume only one active connection
|
||||
LOG_INFO("BLE Disconnected, reason = 0x%x\n", reason);
|
||||
// FIXME - we currently assume only one active connection
|
||||
LOG_INFO("BLE Disconnected, reason = 0x%x\n", reason);
|
||||
}
|
||||
void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
|
||||
{
|
||||
// Display the raw request packet
|
||||
LOG_INFO("CCCD Updated: %u\n", cccd_value);
|
||||
// Check the characteristic this CCCD update is associated with in case
|
||||
// this handler is used for multiple CCCD records.
|
||||
// Display the raw request packet
|
||||
LOG_INFO("CCCD Updated: %u\n", cccd_value);
|
||||
// Check the characteristic this CCCD update is associated with in case
|
||||
// this handler is used for multiple CCCD records.
|
||||
|
||||
// According to the GATT spec: cccd value = 0x0001 means notifications are enabled
|
||||
// and cccd value = 0x0002 means indications are enabled
|
||||
// According to the GATT spec: cccd value = 0x0001 means notifications are enabled
|
||||
// and cccd value = 0x0002 means indications are enabled
|
||||
|
||||
if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) {
|
||||
auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl) : chr->notifyEnabled(conn_hdl);
|
||||
if (result) {
|
||||
LOG_INFO("Notify/Indicate enabled\n");
|
||||
} else {
|
||||
LOG_INFO("Notify/Indicate disabled\n");
|
||||
}
|
||||
}
|
||||
if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid)
|
||||
{
|
||||
auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl) : chr->notifyEnabled(conn_hdl);
|
||||
if (result)
|
||||
{
|
||||
LOG_INFO("Notify/Indicate enabled\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO("Notify/Indicate disabled\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
void startAdv(void)
|
||||
{
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
// IncludeService UUID
|
||||
// Bluefruit.ScanResponse.addService(meshBleService);
|
||||
Bluefruit.ScanResponse.addTxPower();
|
||||
Bluefruit.ScanResponse.addName();
|
||||
// Include Name
|
||||
// Bluefruit.Advertising.addName();
|
||||
Bluefruit.Advertising.addService(meshBleService);
|
||||
/* Start Advertising
|
||||
* - Enable auto advertising if disconnected
|
||||
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
|
||||
* - Timeout for fast mode is 30 seconds
|
||||
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
*
|
||||
* For recommended advertising interval
|
||||
* https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
// IncludeService UUID
|
||||
// Bluefruit.ScanResponse.addService(meshBleService);
|
||||
Bluefruit.ScanResponse.addTxPower();
|
||||
Bluefruit.ScanResponse.addName();
|
||||
// Include Name
|
||||
// Bluefruit.Advertising.addName();
|
||||
Bluefruit.Advertising.addService(meshBleService);
|
||||
/* Start Advertising
|
||||
* - Enable auto advertising if disconnected
|
||||
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
|
||||
* - Timeout for fast mode is 30 seconds
|
||||
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
*
|
||||
* For recommended advertising interval
|
||||
* https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
|
||||
}
|
||||
// Just ack that the caller is allowed to read
|
||||
static void authorizeRead(uint16_t conn_hdl)
|
||||
{
|
||||
ble_gatts_rw_authorize_reply_params_t reply = {.type = BLE_GATTS_AUTHORIZE_TYPE_READ};
|
||||
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
|
||||
ble_gatts_rw_authorize_reply_params_t reply = {.type = BLE_GATTS_AUTHORIZE_TYPE_READ};
|
||||
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
|
||||
}
|
||||
/**
|
||||
* client is starting read, pull the bytes from our API class
|
||||
*/
|
||||
void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
|
||||
{
|
||||
if (request->offset == 0) {
|
||||
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
||||
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
|
||||
// or make empty if the queue is empty
|
||||
fromRadio.write(fromRadioBytes, numBytes);
|
||||
} else {
|
||||
// LOG_INFO("Ignoring successor read\n");
|
||||
}
|
||||
authorizeRead(conn_hdl);
|
||||
if (request->offset == 0)
|
||||
{
|
||||
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
||||
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
|
||||
// or make empty if the queue is empty
|
||||
fromRadio.write(fromRadioBytes, numBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG_INFO("Ignoring successor read\n");
|
||||
}
|
||||
authorizeRead(conn_hdl);
|
||||
}
|
||||
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
|
||||
{
|
||||
LOG_INFO("toRadioWriteCb data %p, len %u\n", data, len);
|
||||
bluetoothPhoneAPI->handleToRadio(data, len);
|
||||
LOG_INFO("toRadioWriteCb data %p, len %u\n", data, len);
|
||||
bluetoothPhoneAPI->handleToRadio(data, len);
|
||||
}
|
||||
|
||||
void setupMeshService(void)
|
||||
{
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
meshBleService.begin();
|
||||
// Note: You must call .begin() on the BLEService before calling .begin() on
|
||||
// any characteristic(s) within that service definition.. Calling .begin() on
|
||||
// a BLECharacteristic will cause it to be added to the last BLEService that
|
||||
// was 'begin()'ed!
|
||||
auto secMode =
|
||||
config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM;
|
||||
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
fromNum.setPermission(secMode, SECMODE_NO_ACCESS); // FIXME, secure this!!!
|
||||
fromNum.setFixedLen(
|
||||
0); // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty
|
||||
fromNum.setMaxLen(4);
|
||||
fromNum.setCccdWriteCallback(onCccd); // Optionally capture CCCD updates
|
||||
// We don't yet need to hook the fromNum auth callback
|
||||
// fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb);
|
||||
fromNum.write32(0); // Provide default fromNum of 0
|
||||
fromNum.begin();
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
meshBleService.begin();
|
||||
// Note: You must call .begin() on the BLEService before calling .begin() on
|
||||
// any characteristic(s) within that service definition.. Calling .begin() on
|
||||
// a BLECharacteristic will cause it to be added to the last BLEService that
|
||||
// was 'begin()'ed!
|
||||
auto secMode =
|
||||
config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM;
|
||||
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
fromNum.setPermission(secMode, SECMODE_NO_ACCESS); // FIXME, secure this!!!
|
||||
fromNum.setFixedLen(
|
||||
0); // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty
|
||||
fromNum.setMaxLen(4);
|
||||
fromNum.setCccdWriteCallback(onCccd); // Optionally capture CCCD updates
|
||||
// We don't yet need to hook the fromNum auth callback
|
||||
// fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb);
|
||||
fromNum.write32(0); // Provide default fromNum of 0
|
||||
fromNum.begin();
|
||||
|
||||
fromRadio.setProperties(CHR_PROPS_READ);
|
||||
fromRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
fromRadio.setMaxLen(sizeof(fromRadioBytes));
|
||||
fromRadio.setReadAuthorizeCallback(
|
||||
onFromRadioAuthorize,
|
||||
false); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
fromRadio.setBuffer(fromRadioBytes, sizeof(fromRadioBytes)); // we preallocate our fromradio buffer so we won't waste space
|
||||
// for two copies
|
||||
fromRadio.begin();
|
||||
fromRadio.setProperties(CHR_PROPS_READ);
|
||||
fromRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
fromRadio.setMaxLen(sizeof(fromRadioBytes));
|
||||
fromRadio.setReadAuthorizeCallback(
|
||||
onFromRadioAuthorize,
|
||||
false); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
fromRadio.setBuffer(fromRadioBytes, sizeof(fromRadioBytes)); // we preallocate our fromradio buffer so we won't waste space
|
||||
// for two copies
|
||||
fromRadio.begin();
|
||||
|
||||
toRadio.setProperties(CHR_PROPS_WRITE);
|
||||
toRadio.setPermission(secMode, secMode); // FIXME secure this!
|
||||
toRadio.setFixedLen(0);
|
||||
toRadio.setMaxLen(512);
|
||||
toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes));
|
||||
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
toRadio.setWriteCallback(onToRadioWrite, false);
|
||||
toRadio.begin();
|
||||
toRadio.setProperties(CHR_PROPS_WRITE);
|
||||
toRadio.setPermission(secMode, secMode); // FIXME secure this!
|
||||
toRadio.setFixedLen(0);
|
||||
toRadio.setMaxLen(512);
|
||||
toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes));
|
||||
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
toRadio.setWriteCallback(onToRadioWrite, false);
|
||||
toRadio.begin();
|
||||
|
||||
logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
logRadio.setMaxLen(512);
|
||||
logRadio.setCccdWriteCallback(onCccd);
|
||||
logRadio.write32(0);
|
||||
logRadio.begin();
|
||||
logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
logRadio.setMaxLen(512);
|
||||
logRadio.setCccdWriteCallback(onCccd);
|
||||
logRadio.write32(0);
|
||||
logRadio.begin();
|
||||
}
|
||||
static uint32_t configuredPasskey;
|
||||
void NRF52Bluetooth::shutdown()
|
||||
{
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
LOG_INFO("Disable NRF52 bluetooth\n");
|
||||
uint8_t connection_num = Bluefruit.connected();
|
||||
if (connection_num) {
|
||||
for (uint8_t i = 0; i < connection_num; i++) {
|
||||
LOG_INFO("NRF52 bluetooth disconnecting handle %d\n", i);
|
||||
Bluefruit.disconnect(i);
|
||||
}
|
||||
delay(100); // wait for ondisconnect;
|
||||
}
|
||||
Bluefruit.Advertising.stop();
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
LOG_INFO("Disable NRF52 bluetooth\n");
|
||||
uint8_t connection_num = Bluefruit.connected();
|
||||
if (connection_num)
|
||||
{
|
||||
for (uint8_t i = 0; i < connection_num; i++)
|
||||
{
|
||||
LOG_INFO("NRF52 bluetooth disconnecting handle %d\n", i);
|
||||
Bluefruit.disconnect(i);
|
||||
}
|
||||
delay(100); // wait for ondisconnect;
|
||||
}
|
||||
Bluefruit.Advertising.stop();
|
||||
}
|
||||
void NRF52Bluetooth::startDisabled()
|
||||
{
|
||||
// Setup Bluetooth
|
||||
nrf52Bluetooth->setup();
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.setTxPower(-40); // Minimum power
|
||||
LOG_INFO("Disabling NRF52 Bluetooth. (Workaround: tx power min, advertising stopped)\n");
|
||||
// Setup Bluetooth
|
||||
nrf52Bluetooth->setup();
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.setTxPower(-40); // Minimum power
|
||||
LOG_INFO("Disabling NRF52 Bluetooth. (Workaround: tx power min, advertising stopped)\n");
|
||||
}
|
||||
bool NRF52Bluetooth::isConnected()
|
||||
{
|
||||
return Bluefruit.connected(connectionHandle);
|
||||
return Bluefruit.connected(connectionHandle);
|
||||
}
|
||||
int NRF52Bluetooth::getRssi()
|
||||
{
|
||||
return 0; // FIXME figure out where to source this
|
||||
return 0; // FIXME figure out where to source this
|
||||
}
|
||||
void NRF52Bluetooth::setup()
|
||||
{
|
||||
// Initialise the Bluefruit module
|
||||
LOG_INFO("Initialize the Bluefruit nRF52 module\n");
|
||||
Bluefruit.autoConnLed(false);
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.begin();
|
||||
// Clear existing data.
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.Advertising.clearData();
|
||||
Bluefruit.ScanResponse.clearData();
|
||||
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
configuredPasskey = config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN
|
||||
? config.bluetooth.fixed_pin
|
||||
: random(100000, 999999);
|
||||
auto pinString = std::to_string(configuredPasskey);
|
||||
LOG_INFO("Bluetooth pin set to '%i'\n", configuredPasskey);
|
||||
Bluefruit.Security.setPIN(pinString.c_str());
|
||||
Bluefruit.Security.setIOCaps(true, false, false);
|
||||
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onPairingPasskey);
|
||||
Bluefruit.Security.setPairCompleteCallback(NRF52Bluetooth::onPairingCompleted);
|
||||
Bluefruit.Security.setSecuredCallback(NRF52Bluetooth::onConnectionSecured);
|
||||
meshBleService.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
} else {
|
||||
Bluefruit.Security.setIOCaps(false, false, false);
|
||||
meshBleService.setPermission(SECMODE_OPEN, SECMODE_OPEN);
|
||||
}
|
||||
// Set the advertised device name (keep it short!)
|
||||
Bluefruit.setName(getDeviceName());
|
||||
// Set the connect/disconnect callback handlers
|
||||
Bluefruit.Periph.setConnectCallback(onConnect);
|
||||
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
|
||||
// Initialise the Bluefruit module
|
||||
LOG_INFO("Initialize the Bluefruit nRF52 module\n");
|
||||
Bluefruit.autoConnLed(false);
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.begin();
|
||||
// Clear existing data.
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.Advertising.clearData();
|
||||
Bluefruit.ScanResponse.clearData();
|
||||
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN)
|
||||
{
|
||||
configuredPasskey = config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN
|
||||
? config.bluetooth.fixed_pin
|
||||
: random(100000, 999999);
|
||||
auto pinString = std::to_string(configuredPasskey);
|
||||
LOG_INFO("Bluetooth pin set to '%i'\n", configuredPasskey);
|
||||
Bluefruit.Security.setPIN(pinString.c_str());
|
||||
Bluefruit.Security.setIOCaps(true, false, false);
|
||||
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onPairingPasskey);
|
||||
Bluefruit.Security.setPairCompleteCallback(NRF52Bluetooth::onPairingCompleted);
|
||||
Bluefruit.Security.setSecuredCallback(NRF52Bluetooth::onConnectionSecured);
|
||||
meshBleService.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
}
|
||||
else
|
||||
{
|
||||
Bluefruit.Security.setIOCaps(false, false, false);
|
||||
meshBleService.setPermission(SECMODE_OPEN, SECMODE_OPEN);
|
||||
}
|
||||
// Set the advertised device name (keep it short!)
|
||||
Bluefruit.setName(getDeviceName());
|
||||
// Set the connect/disconnect callback handlers
|
||||
Bluefruit.Periph.setConnectCallback(onConnect);
|
||||
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
|
||||
#ifndef BLE_DFU_SECURE
|
||||
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
bledfu.begin(); // Install the DFU helper
|
||||
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
bledfu.begin(); // Install the DFU helper
|
||||
#else
|
||||
bledfusecure.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // add by WayenWeng
|
||||
bledfusecure.begin(); // Install the DFU helper
|
||||
bledfusecure.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // add by WayenWeng
|
||||
bledfusecure.begin(); // Install the DFU helper
|
||||
#endif
|
||||
// Configure and Start the Device Information Service
|
||||
LOG_INFO("Configuring the Device Information Service\n");
|
||||
bledis.setModel(optstr(HW_VERSION));
|
||||
bledis.setFirmwareRev(optstr(APP_VERSION));
|
||||
bledis.begin();
|
||||
// Start the BLE Battery Service and set it to 100%
|
||||
LOG_INFO("Configuring the Battery Service\n");
|
||||
blebas.begin();
|
||||
blebas.write(0); // Unknown battery level for now
|
||||
// Setup the Heart Rate Monitor service using
|
||||
// BLEService and BLECharacteristic classes
|
||||
LOG_INFO("Configuring the Mesh bluetooth service\n");
|
||||
setupMeshService();
|
||||
// Setup the advertising packet(s)
|
||||
LOG_INFO("Setting up the advertising payload(s)\n");
|
||||
startAdv();
|
||||
LOG_INFO("Advertising\n");
|
||||
// Configure and Start the Device Information Service
|
||||
LOG_INFO("Configuring the Device Information Service\n");
|
||||
bledis.setModel(optstr(HW_VERSION));
|
||||
bledis.setFirmwareRev(optstr(APP_VERSION));
|
||||
bledis.begin();
|
||||
// Start the BLE Battery Service and set it to 100%
|
||||
LOG_INFO("Configuring the Battery Service\n");
|
||||
blebas.begin();
|
||||
blebas.write(0); // Unknown battery level for now
|
||||
// Setup the Heart Rate Monitor service using
|
||||
// BLEService and BLECharacteristic classes
|
||||
LOG_INFO("Configuring the Mesh bluetooth service\n");
|
||||
setupMeshService();
|
||||
// Setup the advertising packet(s)
|
||||
LOG_INFO("Setting up the advertising payload(s)\n");
|
||||
startAdv();
|
||||
LOG_INFO("Advertising\n");
|
||||
}
|
||||
void NRF52Bluetooth::resumeAdvertising()
|
||||
{
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0);
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0);
|
||||
}
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
blebas.write(level);
|
||||
blebas.write(level);
|
||||
}
|
||||
void NRF52Bluetooth::clearBonds()
|
||||
{
|
||||
LOG_INFO("Clearing bluetooth bonds!\n");
|
||||
bond_print_list(BLE_GAP_ROLE_PERIPH);
|
||||
bond_print_list(BLE_GAP_ROLE_CENTRAL);
|
||||
Bluefruit.Periph.clearBonds();
|
||||
Bluefruit.Central.clearBonds();
|
||||
LOG_INFO("Clearing bluetooth bonds!\n");
|
||||
bond_print_list(BLE_GAP_ROLE_PERIPH);
|
||||
bond_print_list(BLE_GAP_ROLE_CENTRAL);
|
||||
Bluefruit.Periph.clearBonds();
|
||||
Bluefruit.Central.clearBonds();
|
||||
}
|
||||
void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle)
|
||||
{
|
||||
LOG_INFO("BLE connection secured\n");
|
||||
LOG_INFO("BLE connection secured\n");
|
||||
}
|
||||
bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request)
|
||||
{
|
||||
LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3);
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3);
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
|
||||
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void
|
||||
{
|
||||
@ -337,31 +349,33 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, deviceName); });
|
||||
#endif
|
||||
if (match_request) {
|
||||
uint32_t start_time = millis();
|
||||
while (millis() < start_time + 30000) {
|
||||
if (!Bluefruit.connected(conn_handle))
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG_INFO("BLE passkey pairing: match_request=%i\n", match_request);
|
||||
return true;
|
||||
if (match_request)
|
||||
{
|
||||
uint32_t start_time = millis();
|
||||
while (millis() < start_time + 30000)
|
||||
{
|
||||
if (!Bluefruit.connected(conn_handle))
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG_INFO("BLE passkey pairing: match_request=%i\n", match_request);
|
||||
return true;
|
||||
}
|
||||
void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status)
|
||||
{
|
||||
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
|
||||
LOG_INFO("BLE pairing success\n");
|
||||
else
|
||||
LOG_INFO("BLE pairing failed\n");
|
||||
screen->endAlert();
|
||||
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
|
||||
LOG_INFO("BLE pairing success\n");
|
||||
else
|
||||
LOG_INFO("BLE pairing failed\n");
|
||||
screen->endAlert();
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
{
|
||||
if (!isConnected() || length > 512)
|
||||
return;
|
||||
if (logRadio.indicateEnabled())
|
||||
logRadio.indicate(logMessage, (uint16_t)length);
|
||||
else
|
||||
logRadio.notify(logMessage, (uint16_t)length);
|
||||
if (!isConnected() || length > 512)
|
||||
return;
|
||||
if (logRadio.indicateEnabled())
|
||||
logRadio.indicate(logMessage, (uint16_t)length);
|
||||
else
|
||||
logRadio.notify(logMessage, (uint16_t)length);
|
||||
}
|
@ -16,296 +16,360 @@ 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;
|
||||
// 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\n", mp->decoded.payload.size);
|
||||
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\n", 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
|
||||
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 (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\n");
|
||||
// with the string as the value
|
||||
if (shouldLog)
|
||||
LOG_INFO("text message payload is of type plaintext\n");
|
||||
jsonObj["payload"]["text"] = payloadStr;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it is, then we can just use the json object
|
||||
if (shouldLog)
|
||||
LOG_INFO("text message payload is of type json\n");
|
||||
if (shouldLog)
|
||||
LOG_INFO("text message payload is of type json\n");
|
||||
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) {
|
||||
jsonObj["payload"]["battery_level"] = (unsigned 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) {
|
||||
jsonObj["payload"]["temperature"] = decoded->variant.environment_metrics.temperature;
|
||||
jsonObj["payload"]["relative_humidity"] = decoded->variant.environment_metrics.relative_humidity;
|
||||
jsonObj["payload"]["barometric_pressure"] = decoded->variant.environment_metrics.barometric_pressure;
|
||||
jsonObj["payload"]["gas_resistance"] = decoded->variant.environment_metrics.gas_resistance;
|
||||
jsonObj["payload"]["voltage"] = decoded->variant.environment_metrics.voltage;
|
||||
jsonObj["payload"]["current"] = decoded->variant.environment_metrics.current;
|
||||
jsonObj["payload"]["lux"] = decoded->variant.environment_metrics.lux;
|
||||
jsonObj["payload"]["white_lux"] = decoded->variant.environment_metrics.white_lux;
|
||||
jsonObj["payload"]["iaq"] = (uint)decoded->variant.environment_metrics.iaq;
|
||||
jsonObj["payload"]["wind_speed"] = decoded->variant.environment_metrics.wind_speed;
|
||||
jsonObj["payload"]["wind_direction"] = (uint)decoded->variant.environment_metrics.wind_direction;
|
||||
jsonObj["payload"]["wind_gust"] = decoded->variant.environment_metrics.wind_gust;
|
||||
jsonObj["payload"]["wind_lull"] = decoded->variant.environment_metrics.wind_lull;
|
||||
} else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
|
||||
jsonObj["payload"]["pm10"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_standard;
|
||||
jsonObj["payload"]["pm25"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_standard;
|
||||
jsonObj["payload"]["pm100"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_standard;
|
||||
jsonObj["payload"]["pm10_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_environmental;
|
||||
jsonObj["payload"]["pm25_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_environmental;
|
||||
jsonObj["payload"]["pm100_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_environmental;
|
||||
} else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
|
||||
jsonObj["payload"]["voltage_ch1"] = decoded->variant.power_metrics.ch1_voltage;
|
||||
jsonObj["payload"]["current_ch1"] = decoded->variant.power_metrics.ch1_current;
|
||||
jsonObj["payload"]["voltage_ch2"] = decoded->variant.power_metrics.ch2_voltage;
|
||||
jsonObj["payload"]["current_ch2"] = decoded->variant.power_metrics.ch2_current;
|
||||
jsonObj["payload"]["voltage_ch3"] = decoded->variant.power_metrics.ch3_voltage;
|
||||
jsonObj["payload"]["current_ch3"] = decoded->variant.power_metrics.ch3_current;
|
||||
}
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR("Error decoding protobuf for telemetry message!\n");
|
||||
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 protobuf for nodeinfo message!\n");
|
||||
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 protobuf for position message!\n");
|
||||
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 protobuf for position message!\n");
|
||||
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;
|
||||
|
||||
}
|
||||
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)
|
||||
{
|
||||
jsonObj["payload"]["battery_level"] = (unsigned 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)
|
||||
{
|
||||
jsonObj["payload"]["temperature"] = decoded->variant.environment_metrics.temperature;
|
||||
jsonObj["payload"]["relative_humidity"] = decoded->variant.environment_metrics.relative_humidity;
|
||||
jsonObj["payload"]["barometric_pressure"] = decoded->variant.environment_metrics.barometric_pressure;
|
||||
jsonObj["payload"]["gas_resistance"] = decoded->variant.environment_metrics.gas_resistance;
|
||||
jsonObj["payload"]["voltage"] = decoded->variant.environment_metrics.voltage;
|
||||
jsonObj["payload"]["current"] = decoded->variant.environment_metrics.current;
|
||||
jsonObj["payload"]["lux"] = decoded->variant.environment_metrics.lux;
|
||||
jsonObj["payload"]["white_lux"] = decoded->variant.environment_metrics.white_lux;
|
||||
jsonObj["payload"]["iaq"] = (uint)decoded->variant.environment_metrics.iaq;
|
||||
jsonObj["payload"]["wind_speed"] = decoded->variant.environment_metrics.wind_speed;
|
||||
jsonObj["payload"]["wind_direction"] = (uint)decoded->variant.environment_metrics.wind_direction;
|
||||
jsonObj["payload"]["wind_gust"] = decoded->variant.environment_metrics.wind_gust;
|
||||
jsonObj["payload"]["wind_lull"] = decoded->variant.environment_metrics.wind_lull;
|
||||
}
|
||||
else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag)
|
||||
{
|
||||
jsonObj["payload"]["pm10"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_standard;
|
||||
jsonObj["payload"]["pm25"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_standard;
|
||||
jsonObj["payload"]["pm100"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_standard;
|
||||
jsonObj["payload"]["pm10_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_environmental;
|
||||
jsonObj["payload"]["pm25_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_environmental;
|
||||
jsonObj["payload"]["pm100_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_environmental;
|
||||
}
|
||||
else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag)
|
||||
{
|
||||
jsonObj["payload"]["voltage_ch1"] = decoded->variant.power_metrics.ch1_voltage;
|
||||
jsonObj["payload"]["current_ch1"] = decoded->variant.power_metrics.ch1_current;
|
||||
jsonObj["payload"]["voltage_ch2"] = decoded->variant.power_metrics.ch2_voltage;
|
||||
jsonObj["payload"]["current_ch2"] = decoded->variant.power_metrics.ch2_current;
|
||||
jsonObj["payload"]["voltage_ch3"] = decoded->variant.power_metrics.ch3_voltage;
|
||||
jsonObj["payload"]["current_ch3"] = decoded->variant.power_metrics.ch3_current;
|
||||
}
|
||||
}
|
||||
else if (shouldLog)
|
||||
{
|
||||
LOG_ERROR("Error decoding protobuf for telemetry message!\n");
|
||||
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 protobuf for nodeinfo message!\n");
|
||||
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 protobuf for position message!\n");
|
||||
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 protobuf for position message!\n");
|
||||
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();
|
||||
}
|
||||
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 protobuf for neighborinfo message!\n");
|
||||
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";
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
|
||||
bool name_known = node ? node->has_user : false;
|
||||
if (name_known)
|
||||
memcpy(long_name, node->user.long_name, sizeof(long_name));
|
||||
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 protobuf for traceroute message!\n");
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("Traceroute response not reported");
|
||||
}
|
||||
else if (shouldLog)
|
||||
{
|
||||
LOG_ERROR("Error decoding protobuf for neighborinfo message!\n");
|
||||
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 protobuf for RemoteHardware message!\n");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
// add more packet types here if needed
|
||||
default:
|
||||
LOG_WARN("Unsupported packet type %d\n",mp->decoded.portnum);
|
||||
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";
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
|
||||
bool name_known = node ? node->has_user : false;
|
||||
if (name_known)
|
||||
memcpy(long_name, node->user.long_name, sizeof(long_name));
|
||||
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 protobuf for traceroute message!\n");
|
||||
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 protobuf for RemoteHardware message!\n");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
// add more packet types here if needed
|
||||
default:
|
||||
LOG_WARN("Unsupported packet type %d\n", mp->decoded.portnum);
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
} else if (shouldLog) {
|
||||
LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (shouldLog)
|
||||
{
|
||||
LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n");
|
||||
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"] = owner.id;
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
|
||||
jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit);
|
||||
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
|
||||
}
|
||||
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"] = owner.id;
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start)
|
||||
{
|
||||
jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit);
|
||||
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
|
||||
}
|
||||
|
||||
// serialize and write it to the stream
|
||||
// serialize and write it to the stream
|
||||
|
||||
// Serial.printf("serialized json message: \r\n");
|
||||
// serializeJson(jsonObj, Serial);
|
||||
@ -314,39 +378,40 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
std::string jsonStr = "";
|
||||
serializeJson(jsonObj, jsonStr);
|
||||
|
||||
if (shouldLog)
|
||||
LOG_INFO("serialized json message: %s\n", jsonStr.c_str());
|
||||
if (shouldLog)
|
||||
LOG_INFO("serialized json message: %s\n", jsonStr.c_str());
|
||||
|
||||
return jsonStr;
|
||||
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;
|
||||
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;
|
||||
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
|
||||
jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit);
|
||||
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();
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start)
|
||||
{
|
||||
jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit);
|
||||
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
|
||||
// serialize and write it to the stream
|
||||
std::string jsonStr = "";
|
||||
serializeJson(jsonObj, jsonStr);
|
||||
|
||||
return jsonStr;
|
||||
return jsonStr;
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user