You've already forked meshtastic-device-ui
mirror of
https://github.com/m5stack/meshtastic-device-ui.git
synced 2026-05-20 11:51:03 -07:00
1108 lines
51 KiB
C++
1108 lines
51 KiB
C++
#include "graphics/common/ViewController.h"
|
|
#include "assert.h"
|
|
#include "graphics/common/MeshtasticView.h"
|
|
#include "util/ILog.h"
|
|
#include "util/LogMessage.h"
|
|
#include <algorithm>
|
|
|
|
#if defined(ARCH_PORTDUINO)
|
|
#include "PortduinoFS.h"
|
|
fs::FS &persistentFS = PortduinoFS;
|
|
#else
|
|
#include "LittleFS.h"
|
|
fs::FS &persistentFS = LittleFS;
|
|
#endif
|
|
|
|
const size_t DATA_PAYLOAD_LEN = meshtastic_Constants_DATA_PAYLOAD_LEN;
|
|
constexpr const char *logDir = "/messages";
|
|
|
|
/**
|
|
* @brief mediate between GUI view and client interface
|
|
*
|
|
*/
|
|
ViewController::ViewController()
|
|
: view(nullptr), log(persistentFS, logDir, sizeof(LogMessage)), client(nullptr), sendId(1), myNodeNum(0), setupDone(false),
|
|
configCompleted(false), messagesRestored(false), requestConfigRequired(true)
|
|
{
|
|
}
|
|
|
|
void ViewController::init(MeshtasticView *gui, IClientBase *_client)
|
|
{
|
|
time(&lastrun10);
|
|
view = gui;
|
|
client = _client;
|
|
if (client) {
|
|
client->init();
|
|
client->connect();
|
|
}
|
|
log.init();
|
|
}
|
|
|
|
/**
|
|
* @brief runOnce need to be called periodically to process send/receive queues
|
|
*
|
|
*/
|
|
void ViewController::runOnce(void)
|
|
{
|
|
if (client) {
|
|
if (view->getState() == MeshtasticView::eEnterProgrammingMode ||
|
|
(view->getState() >= MeshtasticView::eBootScreenDone && requestConfigRequired))
|
|
requestConfig();
|
|
|
|
if (configCompleted && !messagesRestored)
|
|
restoreTextMessages();
|
|
else {
|
|
if (myNodeNum == 0 || view->getState() != MeshtasticView::eProgrammingMode)
|
|
receive();
|
|
}
|
|
|
|
// executed every 10s:
|
|
time_t curtime;
|
|
time(&curtime);
|
|
|
|
if (curtime - lastrun10 >= 10) {
|
|
lastrun10 = curtime;
|
|
if (!client->isConnected())
|
|
client->connect();
|
|
if (view->getState() == MeshtasticView::eBootScreenDone) {
|
|
requestConfigRequired = true;
|
|
requestConfig();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ViewController::sleep(int16_t pin)
|
|
{
|
|
if (client)
|
|
return client->sleep(pin);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void ViewController::processEvent(void) {}
|
|
|
|
uint32_t ViewController::requestDeviceUIConfig(void)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_get_ui_config_request_tag,
|
|
.get_ui_config_request = true},
|
|
myNodeNum);
|
|
}
|
|
|
|
uint32_t ViewController::requestDeviceConfig(uint32_t nodeId)
|
|
{
|
|
return requestConfig(meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG, nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
uint32_t ViewController::requestPositionConfig(uint32_t nodeId)
|
|
{
|
|
return requestConfig(meshtastic_AdminMessage_ConfigType_POSITION_CONFIG, nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
uint32_t ViewController::requestPowerConfig(uint32_t nodeId)
|
|
{
|
|
return requestConfig(meshtastic_AdminMessage_ConfigType_POWER_CONFIG, nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
uint32_t ViewController::requestNetworkConfig(uint32_t nodeId)
|
|
{
|
|
return requestConfig(meshtastic_AdminMessage_ConfigType_NETWORK_CONFIG, nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
uint32_t ViewController::requestDisplayConfig(uint32_t nodeId)
|
|
{
|
|
return requestConfig(meshtastic_AdminMessage_ConfigType_DISPLAY_CONFIG, nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
uint32_t ViewController::requestLoRaConfig(uint32_t nodeId)
|
|
{
|
|
return requestConfig(meshtastic_AdminMessage_ConfigType_LORA_CONFIG, nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
uint32_t ViewController::requestBluetoothConfig(uint32_t nodeId)
|
|
{
|
|
return requestConfig(meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG, nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
uint32_t ViewController::requestRingtone(uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_get_ringtone_request_tag,
|
|
.get_ringtone_request = true},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
/**
|
|
* Request specific device config block
|
|
*/
|
|
uint32_t ViewController::requestConfig(meshtastic_AdminMessage_ConfigType type, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_get_config_request_tag,
|
|
.get_config_request = type},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::requestReboot(int32_t seconds, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_reboot_seconds_tag, .reboot_seconds = seconds},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::requestRebootOTA(int32_t seconds, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_reboot_ota_seconds_tag,
|
|
.reboot_ota_seconds = seconds},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::requestShutdown(int32_t seconds, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_shutdown_seconds_tag,
|
|
.shutdown_seconds = seconds},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
/**
|
|
* @brief request factory reset or nodedb reset
|
|
*
|
|
* @param factoryReset set to false if only nodeDB reset is desired
|
|
* @param nodeId
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool ViewController::requestReset(bool factoryReset, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant =
|
|
(pb_size_t)(factoryReset ? meshtastic_AdminMessage_factory_reset_config_tag
|
|
: meshtastic_AdminMessage_nodedb_reset_tag)},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::storeUIConfig(const meshtastic_DeviceUIConfig &config)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_store_ui_config_tag, .store_ui_config = config},
|
|
myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(const meshtastic_User &user, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_owner_tag, .set_owner{user}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Config_DeviceConfig &&device, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_config_tag,
|
|
.set_config{.which_payload_variant = meshtastic_Config_device_tag,
|
|
.payload_variant{.device = device}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Config_PositionConfig &&position, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_config_tag,
|
|
.set_config{.which_payload_variant = meshtastic_Config_position_tag,
|
|
.payload_variant{.position = position}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Config_PowerConfig &&power, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_config_tag,
|
|
.set_config{.which_payload_variant = meshtastic_Config_power_tag,
|
|
.payload_variant{.power = power}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Config_NetworkConfig &&network, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_config_tag,
|
|
.set_config{.which_payload_variant = meshtastic_Config_network_tag,
|
|
.payload_variant{.network = network}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Config_DisplayConfig &&display, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_config_tag,
|
|
.set_config{.which_payload_variant = meshtastic_Config_display_tag,
|
|
.payload_variant{.display = display}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Config_LoRaConfig &&lora, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_config_tag,
|
|
.set_config{.which_payload_variant = meshtastic_Config_lora_tag, .payload_variant{.lora = lora}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Config_BluetoothConfig &&bluetooth, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_config_tag,
|
|
.set_config{.which_payload_variant = meshtastic_Config_bluetooth_tag,
|
|
.payload_variant{.bluetooth = bluetooth}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Config_SecurityConfig &&security, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_config_tag,
|
|
.set_config{.which_payload_variant = meshtastic_Config_security_tag,
|
|
.payload_variant{.security = security}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_Channel &channel, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_channel_tag, .set_channel{channel}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
// module config
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_MQTTConfig &&mqtt, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_mqtt_tag,
|
|
.payload_variant{.mqtt = mqtt}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_SerialConfig &&serial, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{
|
|
.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_serial_tag, .payload_variant{.serial = serial}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_ExternalNotificationConfig &&extNotif, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag,
|
|
.payload_variant{.external_notification = extNotif}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(const char ringtone[230], uint32_t nodeId)
|
|
{
|
|
meshtastic_AdminMessage adminMsg{.which_payload_variant = meshtastic_AdminMessage_set_ringtone_message_tag};
|
|
memcpy(adminMsg.set_ringtone_message, ringtone, std::min(strlen(ringtone), (size_t)230U));
|
|
|
|
return sendAdminMessage(adminMsg, nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_StoreForwardConfig &&storeForward, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag,
|
|
.payload_variant{.store_forward = storeForward}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_RangeTestConfig &&rangeTest, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_range_test_tag,
|
|
.payload_variant{.range_test = rangeTest}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_TelemetryConfig &&telemetry, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_telemetry_tag,
|
|
.payload_variant{.telemetry = telemetry}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_CannedMessageConfig &&cannedMessage, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_canned_message_tag,
|
|
.payload_variant{.canned_message = cannedMessage}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_AudioConfig &&audio, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_audio_tag,
|
|
.payload_variant{.audio = audio}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_RemoteHardwareConfig &&remoteHW, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag,
|
|
.payload_variant{.remote_hardware = remoteHW}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_NeighborInfoConfig &&neighborInfo, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_neighbor_info_tag,
|
|
.payload_variant{.neighbor_info = neighborInfo}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_AmbientLightingConfig &&ambientLighting, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag,
|
|
.payload_variant{.ambient_lighting = ambientLighting}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_DetectionSensorConfig &&detectionSensor, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag,
|
|
.payload_variant{.detection_sensor = detectionSensor}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
bool ViewController::sendConfig(meshtastic_ModuleConfig_PaxcounterConfig &&paxCounter, uint32_t nodeId)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_set_module_config_tag,
|
|
.set_module_config{.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag,
|
|
.payload_variant{.paxcounter = paxCounter}}},
|
|
nodeId ? nodeId : myNodeNum);
|
|
}
|
|
|
|
/**
|
|
* @brief Generic admin message (takes lvalue)
|
|
*
|
|
*/
|
|
bool ViewController::sendAdminMessage(meshtastic_AdminMessage &config, uint32_t nodeId)
|
|
{
|
|
meshtastic_Data_payload_t payload;
|
|
payload.size = pb_encode_to_bytes(payload.bytes, DATA_PAYLOAD_LEN, &meshtastic_AdminMessage_msg, &config);
|
|
return send(nodeId, meshtastic_PortNum_ADMIN_APP, payload, true);
|
|
}
|
|
|
|
/**
|
|
* @brief Generic admin message (takes rvalue)
|
|
*
|
|
*/
|
|
bool ViewController::sendAdminMessage(meshtastic_AdminMessage &&config, uint32_t nodeId)
|
|
{
|
|
meshtastic_Data_payload_t payload;
|
|
payload.size = pb_encode_to_bytes(payload.bytes, DATA_PAYLOAD_LEN, &meshtastic_AdminMessage_msg, &config);
|
|
return send(nodeId, meshtastic_PortNum_ADMIN_APP, payload, true);
|
|
}
|
|
|
|
void ViewController::sendHeartbeat(void)
|
|
{
|
|
if (client->isConnected()) {
|
|
client->send(meshtastic_ToRadio{.which_payload_variant = meshtastic_ToRadio_heartbeat_tag});
|
|
}
|
|
}
|
|
|
|
void ViewController::sendPing(void)
|
|
{
|
|
if (client->isConnected()) {
|
|
client->send(meshtastic_ToRadio{.which_payload_variant = meshtastic_ToRadio_heartbeat_tag,
|
|
.heartbeat{.dummy_field = 1}}); // tell packet server to send ping to all nodes
|
|
}
|
|
}
|
|
|
|
void ViewController::setConfigRequested(bool required)
|
|
{
|
|
requestConfigRequired = required;
|
|
}
|
|
|
|
void ViewController::sendTextMessage(uint32_t to, uint8_t ch, uint8_t hopLimit, uint32_t msgTime, uint32_t requestId, bool usePkc,
|
|
const char *textmsg)
|
|
{
|
|
size_t msgLen = strlen(textmsg);
|
|
assert(msgLen <= (size_t)DATA_PAYLOAD_LEN);
|
|
|
|
if (send(to, ch, hopLimit, requestId, meshtastic_PortNum_TEXT_MESSAGE_APP, false, usePkc, (const uint8_t *)textmsg, msgLen)) {
|
|
ILOG_DEBUG("storing msg to:0x%08x, ch:%d, time:%d, size:%d, '%s'", to, ch, msgTime, msgLen, textmsg);
|
|
log.write(LogMessageEnv(myNodeNum, to, ch, msgTime, LogMessage::eDefault, false, msgLen, (const uint8_t *)textmsg));
|
|
}
|
|
}
|
|
|
|
bool ViewController::requestPosition(uint32_t to, uint8_t ch, uint32_t requestId)
|
|
{
|
|
ILOG_DEBUG("sending position request");
|
|
meshtastic_Position position{};
|
|
meshtastic_Data_payload_t payload;
|
|
payload.size = pb_encode_to_bytes(payload.bytes, DATA_PAYLOAD_LEN, &meshtastic_Position_msg, &position);
|
|
|
|
return client->send(
|
|
meshtastic_ToRadio{.which_payload_variant = meshtastic_ToRadio_packet_tag,
|
|
.packet{.to = to,
|
|
.channel = ch,
|
|
.which_payload_variant = meshtastic_MeshPacket_decoded_tag,
|
|
.decoded{.portnum = meshtastic_PortNum_POSITION_APP, .payload{payload}, .want_response = true},
|
|
.id = requestId,
|
|
.hop_limit = 0,
|
|
.want_ack = false}});
|
|
}
|
|
|
|
void ViewController::traceRoute(uint32_t to, uint8_t ch, uint8_t hopLimit, uint32_t requestId)
|
|
{
|
|
meshtastic_RouteDiscovery request{};
|
|
meshtastic_Data_payload_t payload;
|
|
payload.size = pb_encode_to_bytes(payload.bytes, DATA_PAYLOAD_LEN, &meshtastic_RouteDiscovery_msg, &request);
|
|
send(to, ch, hopLimit, requestId, meshtastic_PortNum_TRACEROUTE_APP, true, false, payload.bytes, payload.size);
|
|
}
|
|
|
|
/**
|
|
* @brief generic send message to configure or request config data
|
|
*
|
|
* @param to destination
|
|
* @param portnum app
|
|
* @param payload
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool ViewController::send(uint32_t to, meshtastic_PortNum portnum, const meshtastic_Data_payload_t &payload, bool wantRsp)
|
|
{
|
|
ILOG_DEBUG("sending meshpacket to radio id=0x%08x, to=0x%08x(%u), portnum=%u, len=%u, wantRsp=%d", 0, to, to, portnum,
|
|
payload.size, wantRsp);
|
|
return client->send(meshtastic_ToRadio{.which_payload_variant = meshtastic_ToRadio_packet_tag,
|
|
.packet{.to = to,
|
|
.which_payload_variant = meshtastic_MeshPacket_decoded_tag,
|
|
.decoded{.portnum = portnum, .payload{payload}, .want_response = wantRsp},
|
|
.want_ack = (to != 0)}});
|
|
}
|
|
|
|
/**
|
|
* generic send method for sending meshpackets with encoded payload
|
|
*/
|
|
bool ViewController::send(uint32_t to, uint8_t ch, uint8_t hopLimit, uint32_t requestId, meshtastic_PortNum portnum, bool wantRsp,
|
|
bool usePkc, const unsigned char bytes[meshtastic_Constants_DATA_PAYLOAD_LEN], size_t len)
|
|
{
|
|
ILOG_DEBUG("sending meshpacket to radio id=0x%x, to=0x%08x(%u), ch=%u, portnum=%u, len=%u, wantRsp=%d", requestId, to, to,
|
|
(unsigned int)ch, portnum, len, wantRsp);
|
|
// send requires movable lvalue, i.e. a temporary object
|
|
return client->send(meshtastic_ToRadio{
|
|
.which_payload_variant = meshtastic_ToRadio_packet_tag,
|
|
.packet{
|
|
.to = to,
|
|
.channel = ch,
|
|
.which_payload_variant = meshtastic_MeshPacket_decoded_tag,
|
|
.decoded{
|
|
.portnum = portnum,
|
|
.payload{.size = (unsigned short)len,
|
|
.bytes = {bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
|
|
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
|
|
bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23],
|
|
bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31],
|
|
bytes[32], bytes[33], bytes[34], bytes[35], bytes[36], bytes[37], bytes[38], bytes[39],
|
|
bytes[40], bytes[41], bytes[42], bytes[43], bytes[44], bytes[45], bytes[46], bytes[47],
|
|
bytes[48], bytes[49], bytes[50], bytes[51], bytes[52], bytes[53], bytes[54], bytes[55],
|
|
bytes[56], bytes[57], bytes[58], bytes[59], bytes[60], bytes[61], bytes[62], bytes[63],
|
|
bytes[64], bytes[65], bytes[66], bytes[67], bytes[68], bytes[69], bytes[70], bytes[71],
|
|
bytes[72], bytes[73], bytes[74], bytes[75], bytes[76], bytes[77], bytes[78], bytes[79],
|
|
bytes[80], bytes[81], bytes[82], bytes[83], bytes[84], bytes[85], bytes[86], bytes[87],
|
|
bytes[88], bytes[89], bytes[90], bytes[91], bytes[92], bytes[93], bytes[94], bytes[95],
|
|
bytes[96], bytes[97], bytes[98], bytes[99], bytes[100], bytes[101], bytes[102], bytes[103],
|
|
bytes[104], bytes[105], bytes[106], bytes[107], bytes[108], bytes[109], bytes[110], bytes[111],
|
|
bytes[112], bytes[113], bytes[114], bytes[115], bytes[116], bytes[117], bytes[118], bytes[119],
|
|
bytes[120], bytes[121], bytes[122], bytes[123], bytes[124], bytes[125], bytes[126], bytes[127],
|
|
bytes[128], bytes[129], bytes[130], bytes[131], bytes[132], bytes[133], bytes[134], bytes[135],
|
|
bytes[136], bytes[137], bytes[138], bytes[139], bytes[140], bytes[141], bytes[142], bytes[143],
|
|
bytes[144], bytes[145], bytes[146], bytes[147], bytes[148], bytes[149], bytes[150], bytes[151],
|
|
bytes[152], bytes[153], bytes[154], bytes[155], bytes[156], bytes[157], bytes[158], bytes[159],
|
|
bytes[160], bytes[161], bytes[162], bytes[163], bytes[164], bytes[165], bytes[166], bytes[167],
|
|
bytes[168], bytes[169], bytes[170], bytes[171], bytes[172], bytes[173], bytes[174], bytes[175],
|
|
bytes[176], bytes[177], bytes[178], bytes[179], bytes[180], bytes[181], bytes[182], bytes[183],
|
|
bytes[184], bytes[185], bytes[186], bytes[187], bytes[188], bytes[189], bytes[190], bytes[191],
|
|
bytes[192], bytes[193], bytes[194], bytes[195], bytes[196], bytes[197], bytes[198], bytes[199],
|
|
bytes[200], bytes[201], bytes[202], bytes[203], bytes[204], bytes[205], bytes[206], bytes[207],
|
|
bytes[208], bytes[209], bytes[210], bytes[211], bytes[212], bytes[213], bytes[214], bytes[215],
|
|
bytes[216], bytes[217], bytes[218], bytes[219], bytes[220], bytes[221], bytes[222], bytes[223],
|
|
bytes[224], bytes[225], bytes[226], bytes[227], bytes[228], bytes[229], bytes[230], bytes[231],
|
|
bytes[232]}},
|
|
.want_response = wantRsp}, // FIXME: traceRoute, requestPosition, remote config: true
|
|
.id = requestId,
|
|
.hop_limit = hopLimit,
|
|
.want_ack = (to != 0),
|
|
.pki_encrypted = usePkc}});
|
|
}
|
|
|
|
bool ViewController::receive(void)
|
|
{
|
|
bool gotPacket = false;
|
|
if (client->isConnected()) {
|
|
do {
|
|
meshtastic_FromRadio from = client->receive();
|
|
if (from.which_payload_variant) {
|
|
handleFromRadio(from);
|
|
}
|
|
gotPacket = from.which_payload_variant != 0;
|
|
} while (gotPacket);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* request full upload of all config data from the radio
|
|
*/
|
|
void ViewController::requestConfig(void)
|
|
{
|
|
static uint32_t configId = 1;
|
|
if (client->isConnected() && requestConfigRequired) {
|
|
client->send(
|
|
meshtastic_ToRadio{.which_payload_variant = meshtastic_ToRadio_want_config_id_tag, .want_config_id = configId++});
|
|
requestConfigRequired = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief request additional config after initial startup finished
|
|
*
|
|
*/
|
|
void ViewController::requestAdditionalConfig(void)
|
|
{
|
|
requestRingtone(myNodeNum);
|
|
}
|
|
|
|
/**
|
|
* recover all messages from persistent log (could take a while!)
|
|
*/
|
|
void ViewController::beginRestoreTextMessages(void)
|
|
{
|
|
configCompleted = true;
|
|
restoreTimer = millis();
|
|
ILOG_INFO("loading persistent messages...");
|
|
}
|
|
|
|
/**
|
|
* incrementally recover messages from persistent log (could take a while!)
|
|
*/
|
|
void ViewController::restoreTextMessages(void)
|
|
{
|
|
static uint32_t msgCounter = 0;
|
|
static uint32_t msgTotalSize = 0;
|
|
LogMessageEnv msg;
|
|
|
|
if (log.readNext(msg)) {
|
|
msgCounter++;
|
|
msgTotalSize += msg.size();
|
|
view->restoreMessage(msg);
|
|
view->notifyRestoreMessages(msgTotalSize * 100 / log.size());
|
|
} else {
|
|
ILOG_INFO("restoring %d messages completed in %dms.", msgCounter, millis() - restoreTimer);
|
|
msgCounter = 0;
|
|
msgTotalSize = 0;
|
|
messagesRestored = true;
|
|
view->notifyMessagesRestored();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* write a flag into message log that chat has been deleted
|
|
* The call removeTextMessages(0,0,0) removes all logs.
|
|
*/
|
|
void ViewController::removeTextMessages(uint32_t from, uint32_t to, uint8_t ch)
|
|
{
|
|
if (!from && !to && !ch) {
|
|
log.clear();
|
|
} else {
|
|
log.write(LogMessageEnv(from, to, ch, 0L, LogMessage::eDefault, true, 0, nullptr));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* request connection status of WLAN/BT/MQTT
|
|
*/
|
|
bool ViewController::requestDeviceConnectionStatus(void)
|
|
{
|
|
return sendAdminMessage(
|
|
meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_request_tag,
|
|
.get_device_connection_status_request = true},
|
|
myNodeNum);
|
|
}
|
|
|
|
bool ViewController::handleFromRadio(const meshtastic_FromRadio &from)
|
|
{
|
|
if (view->getState() == MeshtasticView::eProgrammingMode)
|
|
return false;
|
|
|
|
ILOG_DEBUG("handleFromRadio variant %u, id=%d", from.which_payload_variant, from.id);
|
|
if (from.which_payload_variant == meshtastic_FromRadio_deviceuiConfig_tag) {
|
|
setupDone = view->setupUIConfig(from.deviceuiConfig);
|
|
} else if (from.which_payload_variant == meshtastic_FromRadio_my_info_tag) {
|
|
const meshtastic_MyNodeInfo &info = from.my_info;
|
|
view->setMyInfo(info.my_node_num);
|
|
myNodeNum = info.my_node_num;
|
|
} else {
|
|
if (setupDone || (from.which_payload_variant == meshtastic_FromRadio_config_tag &&
|
|
from.config.which_payload_variant == meshtastic_Config_bluetooth_tag)) {
|
|
switch (from.which_payload_variant) {
|
|
case meshtastic_FromRadio_packet_tag: {
|
|
const meshtastic_MeshPacket &p = from.packet;
|
|
if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
|
packetReceived(p);
|
|
} else {
|
|
// FIXME: needs implementation when not using PacketClient interface
|
|
ILOG_WARN("dropping encoded meshpacket id=%u from radio!", from.id);
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_node_info_tag: {
|
|
const meshtastic_NodeInfo &node = from.node_info;
|
|
if (node.has_user) {
|
|
view->addOrUpdateNode(node.num, node.channel, node.user.short_name, node.user.long_name, node.last_heard,
|
|
(MeshtasticView::eRole)node.user.role, node.user.public_key.size != 0, node.via_mqtt);
|
|
} else {
|
|
view->addOrUpdateNode(node.num, node.channel, node.last_heard, (MeshtasticView::eRole)node.user.role, false,
|
|
node.via_mqtt);
|
|
}
|
|
if (node.has_position) {
|
|
view->updatePosition(node.num, node.position.latitude_i, node.position.longitude_i, node.position.altitude, 0,
|
|
node.position.precision_bits);
|
|
}
|
|
if (node.has_device_metrics) {
|
|
view->updateMetrics(node.num, node.device_metrics.battery_level, node.device_metrics.voltage,
|
|
node.device_metrics.channel_utilization, node.device_metrics.air_util_tx);
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_config_tag: {
|
|
const meshtastic_Config &config = from.config;
|
|
switch (config.which_payload_variant) {
|
|
case meshtastic_Config_device_tag: {
|
|
const meshtastic_Config_DeviceConfig &cfg = config.payload_variant.device;
|
|
view->updateDeviceConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_position_tag: {
|
|
const meshtastic_Config_PositionConfig &cfg = config.payload_variant.position;
|
|
view->updatePositionConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_power_tag: {
|
|
const meshtastic_Config_PowerConfig &cfg = config.payload_variant.power;
|
|
view->updatePowerConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_network_tag: {
|
|
const meshtastic_Config_NetworkConfig &cfg = config.payload_variant.network;
|
|
view->updateNetworkConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_display_tag: {
|
|
const meshtastic_Config_DisplayConfig &cfg = config.payload_variant.display;
|
|
view->updateDisplayConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_lora_tag: {
|
|
const meshtastic_Config_LoRaConfig &cfg = config.payload_variant.lora;
|
|
view->updateLoRaConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_bluetooth_tag: {
|
|
const meshtastic_Config_BluetoothConfig &cfg = config.payload_variant.bluetooth;
|
|
view->updateBluetoothConfig(cfg, from.id); // PacketAPI provides our nodeId here
|
|
break;
|
|
}
|
|
case meshtastic_Config_security_tag: {
|
|
const meshtastic_Config_SecurityConfig &cfg = config.payload_variant.security;
|
|
view->updateSecurityConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_sessionkey_tag: {
|
|
const meshtastic_Config_SessionkeyConfig &cfg = config.payload_variant.sessionkey;
|
|
view->updateSessionKeyConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_device_ui_tag: {
|
|
ILOG_DEBUG("skipping meshtastic_Config_device_ui_tag");
|
|
break;
|
|
}
|
|
default:
|
|
ILOG_ERROR("unsupported device config variant: %u", config.which_payload_variant);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_moduleConfig_tag: {
|
|
const meshtastic_ModuleConfig &module = from.moduleConfig;
|
|
switch (module.which_payload_variant) {
|
|
case meshtastic_ModuleConfig_mqtt_tag: {
|
|
const meshtastic_ModuleConfig_MQTTConfig &cfg = module.payload_variant.mqtt;
|
|
view->updateMQTTModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_serial_tag: {
|
|
const meshtastic_ModuleConfig_SerialConfig &cfg = module.payload_variant.serial;
|
|
view->updateSerialModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_external_notification_tag: {
|
|
const meshtastic_ModuleConfig_ExternalNotificationConfig &cfg = module.payload_variant.external_notification;
|
|
view->updateExtNotificationModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_store_forward_tag: {
|
|
const meshtastic_ModuleConfig_StoreForwardConfig &cfg = module.payload_variant.store_forward;
|
|
view->updateStoreForwardModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_range_test_tag: {
|
|
const meshtastic_ModuleConfig_RangeTestConfig &cfg = module.payload_variant.range_test;
|
|
view->updateRangeTestModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_telemetry_tag: {
|
|
const meshtastic_ModuleConfig_TelemetryConfig &cfg = module.payload_variant.telemetry;
|
|
view->updateTelemetryModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_canned_message_tag: {
|
|
const meshtastic_ModuleConfig_CannedMessageConfig &cfg = module.payload_variant.canned_message;
|
|
view->updateCannedMessageModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_audio_tag: {
|
|
const meshtastic_ModuleConfig_AudioConfig &cfg = module.payload_variant.audio;
|
|
view->updateAudioModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_remote_hardware_tag: {
|
|
const meshtastic_ModuleConfig_RemoteHardwareConfig &cfg = module.payload_variant.remote_hardware;
|
|
view->updateRemoteHardwareModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_neighbor_info_tag: {
|
|
const meshtastic_ModuleConfig_NeighborInfoConfig &cfg = module.payload_variant.neighbor_info;
|
|
view->updateNeighborInfoModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_ambient_lighting_tag: {
|
|
const meshtastic_ModuleConfig_AmbientLightingConfig &cfg = module.payload_variant.ambient_lighting;
|
|
view->updateAmbientLightingModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_detection_sensor_tag: {
|
|
const meshtastic_ModuleConfig_DetectionSensorConfig &cfg = module.payload_variant.detection_sensor;
|
|
view->updateDetectionSensorModule(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_ModuleConfig_paxcounter_tag: {
|
|
const meshtastic_ModuleConfig_PaxcounterConfig &cfg = module.payload_variant.paxcounter;
|
|
view->updatePaxCounterModule(cfg);
|
|
break;
|
|
}
|
|
default:
|
|
ILOG_ERROR("unsupported module config variant: %u", module.which_payload_variant);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_fileInfo_tag: {
|
|
const meshtastic_FileInfo &fileinfo = from.fileInfo;
|
|
view->updateFileinfo(fileinfo);
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_channel_tag: {
|
|
const meshtastic_Channel &ch = from.channel;
|
|
if (ch.has_settings) {
|
|
view->updateChannelConfig(ch);
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_metadata_tag: {
|
|
const meshtastic_DeviceMetadata &meta = from.metadata;
|
|
view->setDeviceMetaData(meta.hw_model, meta.firmware_version, meta.hasBluetooth, meta.hasWifi, meta.hasEthernet,
|
|
meta.canShutdown);
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_config_complete_id_tag: {
|
|
beginRestoreTextMessages();
|
|
view->configCompleted();
|
|
requestAdditionalConfig();
|
|
view->notifyResync(false);
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_queueStatus_tag: {
|
|
const meshtastic_QueueStatus &q = from.queueStatus;
|
|
if (q.free == 0) {
|
|
ILOG_CRIT("meshqueue full!?");
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_FromRadio_rebooted_tag: {
|
|
view->notifyResync(true);
|
|
setConfigRequested(true);
|
|
break;
|
|
}
|
|
default: {
|
|
ILOG_ERROR("unhandled fromRadio packet variant: %u", from.which_payload_variant);
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
ILOG_WARN("skipping packet while setup not finished: %u", from.which_payload_variant);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ViewController::packetReceived(const meshtastic_MeshPacket &p)
|
|
{
|
|
ILOG_DEBUG("received packet from 0x%08x, id=0x%08x, portnum=%u", p.from, p.id, p.decoded.portnum);
|
|
view->packetReceived(p);
|
|
|
|
// only for direct neighbors print rssi/snr
|
|
if (p.hop_limit == p.hop_start && p.hop_limit > 0) {
|
|
view->updateSignalStrength(p.from, p.rx_rssi, p.rx_snr);
|
|
} else if (p.hop_start > 0) {
|
|
view->updateHopsAway(p.from, p.hop_start - p.hop_limit);
|
|
}
|
|
view->updateLastHeard(p.from);
|
|
|
|
switch (p.decoded.portnum) {
|
|
case meshtastic_PortNum_ALERT_APP:
|
|
case meshtastic_PortNum_TEXT_MESSAGE_APP: {
|
|
ILOG_INFO("received text message '%s'", (const char *)p.decoded.payload.bytes);
|
|
if (!messagesRestored && log.count() > 0) {
|
|
// houston we have a problem! Haven't finished restoring messages incrementally while new ones come in
|
|
if (!configCompleted) {
|
|
ILOG_ERROR("cannot handle received message NOW");
|
|
return false; // the only way out
|
|
} else {
|
|
// let's hope for the best...
|
|
ILOG_ERROR("WTF!? how did we get here??");
|
|
}
|
|
}
|
|
uint32_t time = p.rx_time;
|
|
view->newMessage(p.from, p.to, p.channel, (const char *)p.decoded.payload.bytes, time);
|
|
log.write(LogMessageEnv(p.from, p.to, p.channel, time, LogMessage::eDefault, false, p.decoded.payload.size,
|
|
(const uint8_t *)p.decoded.payload.bytes));
|
|
break;
|
|
}
|
|
case meshtastic_PortNum_POSITION_APP: {
|
|
meshtastic_Position position;
|
|
if (pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Position_msg, &position)) {
|
|
// if "to" is our node then this is a reply to a requestPosition request
|
|
if (p.to == myNodeNum) {
|
|
view->handlePositionResponse(p.from, p.decoded.request_id, p.rx_rssi, p.rx_snr, p.hop_limit == p.hop_start);
|
|
} else {
|
|
view->updatePosition(p.from, position.latitude_i, position.longitude_i, position.altitude, position.sats_in_view,
|
|
position.precision_bits);
|
|
}
|
|
view->updateTime(position.time);
|
|
} else {
|
|
ILOG_ERROR("Error decoding protobuf meshtastic_Position!");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_PortNum_NODEINFO_APP: {
|
|
meshtastic_User user;
|
|
if (pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_User_msg, &user)) {
|
|
view->updateNode(p.from, -1, user.short_name, user.long_name, 0, (MeshtasticView::eRole)user.role,
|
|
user.public_key.size != 0, false); // TODO viaMqtt?
|
|
} else {
|
|
ILOG_ERROR("Error decoding protobuf meshtastic_User (nodeinfo)!");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_PortNum_TELEMETRY_APP: {
|
|
meshtastic_Telemetry telemetry;
|
|
if (pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Telemetry_msg, &telemetry)) {
|
|
switch (telemetry.which_variant) {
|
|
case meshtastic_Telemetry_device_metrics_tag: {
|
|
meshtastic_DeviceMetrics &metrics = telemetry.variant.device_metrics;
|
|
view->updateMetrics(p.from, metrics.battery_level, metrics.voltage, metrics.channel_utilization,
|
|
metrics.air_util_tx);
|
|
break;
|
|
}
|
|
case meshtastic_Telemetry_environment_metrics_tag: {
|
|
view->updateEnvironmentMetrics(p.from, telemetry.variant.environment_metrics);
|
|
break;
|
|
}
|
|
case meshtastic_Telemetry_air_quality_metrics_tag: {
|
|
view->updateAirQualityMetrics(p.from, telemetry.variant.air_quality_metrics);
|
|
return false;
|
|
}
|
|
case meshtastic_Telemetry_power_metrics_tag: {
|
|
view->updatePowerMetrics(p.from, telemetry.variant.power_metrics);
|
|
return false;
|
|
}
|
|
case meshtastic_Telemetry_local_stats_tag: {
|
|
ILOG_DEBUG("meshtastic_Telemetry_local_stats_tag not implemented!");
|
|
return false;
|
|
}
|
|
default:
|
|
ILOG_ERROR("unhandled telemetry variant: %u", telemetry.which_variant);
|
|
return false;
|
|
}
|
|
} else {
|
|
ILOG_ERROR("Error decoding protobuf meshtastic_Telemetry!");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_PortNum_TRACEROUTE_APP: {
|
|
ILOG_DEBUG("PortNum_TRACEROUTE_APP");
|
|
meshtastic_RouteDiscovery route;
|
|
if (pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_RouteDiscovery_msg, &route)) {
|
|
view->handleResponse(p.from, p.decoded.request_id, route);
|
|
} else {
|
|
ILOG_ERROR("Error decoding protobuf meshtastic_RouteDiscovery!");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_PortNum_ROUTING_APP: {
|
|
meshtastic_Routing routing;
|
|
ILOG_DEBUG("PortNum_ROUTING_APP: id:%08x, from:%08x, to:%08x, dest:%08x, source:%08x, requestId:%08x, replyId:%08x", p.id,
|
|
p.from, p.to, p.decoded.dest, p.decoded.source, p.decoded.request_id, p.decoded.reply_id);
|
|
if (pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Routing_msg, &routing)) {
|
|
if (routing.which_variant == meshtastic_Routing_error_reason_tag) {
|
|
switch (routing.error_reason) {
|
|
case meshtastic_Routing_Error_NONE:
|
|
case meshtastic_Routing_Error_MAX_RETRANSMIT:
|
|
view->handleResponse(p.from, p.decoded.request_id, routing, p);
|
|
break;
|
|
case meshtastic_Routing_Error_NO_RESPONSE:
|
|
ILOG_DEBUG("Routing error: no response");
|
|
// this response is sent by the other node when position is not availble
|
|
// however, it contains valid rssi/snr, so use these
|
|
view->handlePositionResponse(p.from, p.decoded.request_id, p.rx_rssi, p.rx_snr, p.hop_limit == p.hop_start);
|
|
break;
|
|
case meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY:
|
|
case meshtastic_Routing_Error_NO_CHANNEL:
|
|
// this response is sent by the other node when encryption keys differ (outdated)
|
|
view->handleResponse(p.from, p.decoded.request_id, routing, p);
|
|
break;
|
|
default:
|
|
ILOG_WARN("got unhandled Routing_Error: %d", routing.error_reason);
|
|
break;
|
|
}
|
|
} else {
|
|
view->handleResponse(p.from, p.decoded.request_id, routing, p);
|
|
}
|
|
} else {
|
|
ILOG_ERROR("Error decoding protobuf meshtastic_Routing!");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_PortNum_ADMIN_APP: {
|
|
meshtastic_AdminMessage admin;
|
|
if (pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_AdminMessage_msg, &admin)) {
|
|
switch (admin.which_payload_variant) {
|
|
case meshtastic_AdminMessage_get_device_connection_status_response_tag: {
|
|
meshtastic_DeviceConnectionStatus &status = admin.get_device_connection_status_response;
|
|
view->updateConnectionStatus(status);
|
|
break;
|
|
}
|
|
case meshtastic_AdminMessage_get_config_response_tag: {
|
|
meshtastic_Config &config = admin.get_config_response;
|
|
switch (config.which_payload_variant) {
|
|
case meshtastic_Config_device_tag: {
|
|
const meshtastic_Config_DeviceConfig &cfg = config.payload_variant.device;
|
|
view->updateDeviceConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_position_tag: {
|
|
const meshtastic_Config_PositionConfig &cfg = config.payload_variant.position;
|
|
view->updatePositionConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_power_tag: {
|
|
const meshtastic_Config_PowerConfig &cfg = config.payload_variant.power;
|
|
view->updatePowerConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_network_tag: {
|
|
const meshtastic_Config_NetworkConfig &cfg = config.payload_variant.network;
|
|
view->updateNetworkConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_display_tag: {
|
|
const meshtastic_Config_DisplayConfig &cfg = config.payload_variant.display;
|
|
view->updateDisplayConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_lora_tag: {
|
|
const meshtastic_Config_LoRaConfig &cfg = config.payload_variant.lora;
|
|
view->updateLoRaConfig(cfg);
|
|
break;
|
|
}
|
|
case meshtastic_Config_bluetooth_tag: {
|
|
const meshtastic_Config_BluetoothConfig &cfg = config.payload_variant.bluetooth;
|
|
view->updateBluetoothConfig(cfg);
|
|
break;
|
|
}
|
|
default:
|
|
ILOG_ERROR("unhandled meshtastic_Config variant: %u", config.which_payload_variant);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_AdminMessage_get_module_config_response_tag: {
|
|
meshtastic_ModuleConfig &config = admin.get_module_config_response;
|
|
switch (config.which_payload_variant) {
|
|
case meshtastic_ModuleConfig_mqtt_tag: {
|
|
const meshtastic_ModuleConfig_MQTTConfig &cfg = config.payload_variant.mqtt;
|
|
view->updateMQTTModule(cfg);
|
|
break;
|
|
}
|
|
default:
|
|
ILOG_ERROR("unhandled meshtastic_ModuleConfig variant: %u", config.which_payload_variant);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_AdminMessage_get_ringtone_response_tag: {
|
|
view->updateRingtone(admin.get_ringtone_response);
|
|
break;
|
|
}
|
|
case meshtastic_AdminMessage_set_channel_tag: {
|
|
// TODO
|
|
ILOG_WARN("meshtastic_AdminMessage_set_channel_tag not implemented");
|
|
return false;
|
|
}
|
|
default:
|
|
ILOG_ERROR("unhandled AdminMessage variant: %u", admin.which_payload_variant);
|
|
return false;
|
|
}
|
|
} else {
|
|
ILOG_ERROR("Error decoding protobuf meshtastic_AdminMessage!");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case meshtastic_PortNum_SIMULATOR_APP:
|
|
break;
|
|
default:
|
|
ILOG_ERROR("unhandled meshpacket portnum: %u", p.decoded.portnum);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ViewController::~ViewController() {}
|