From c4c85777d035402140d96270e280051185e0a218 Mon Sep 17 00:00:00 2001 From: beegee-tokyo Date: Thu, 12 Sep 2024 13:20:09 +0800 Subject: [PATCH] Another try to get the code format correct. --- src/mqtt/MQTT.cpp | 1097 +++++++++++-------------- src/platform/nrf52/NRF52Bluetooth.cpp | 456 +++++----- 2 files changed, 719 insertions(+), 834 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 3e0e692b4..a4921e684 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -33,194 +33,161 @@ Allocator &mqttPool = staticMqttPool; void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) { - mqtt->onReceive(topic, payload, length); + mqtt->onReceive(topic, payload, length); } void MQTT::onClientProxyReceive(meshtastic_MqttClientProxyMessage msg) { - onReceive(msg.topic, msg.payload_variant.data.bytes, msg.payload_variant.data.size); + onReceive(msg.topic, msg.payload_variant.data.bytes, msg.payload_variant.data.size); } void MQTT::onReceive(char *topic, byte *payload, size_t length) { - meshtastic_ServiceEnvelope e = meshtastic_ServiceEnvelope_init_default; + meshtastic_ServiceEnvelope e = meshtastic_ServiceEnvelope_init_default; - if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) - { - // check if this is a json payload message by comparing the topic start - char payloadStr[length + 1]; - memcpy(payloadStr, payload, length); - payloadStr[length] = 0; // null terminated string - JSONValue *json_value = JSON::Parse(payloadStr); - if (json_value != NULL) - { - // check if it is a valid envelope - JSONObject json; - json = json_value->AsObject(); + if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) { + // check if this is a json payload message by comparing the topic start + char payloadStr[length + 1]; + memcpy(payloadStr, payload, length); + payloadStr[length] = 0; // null terminated string + JSONValue *json_value = JSON::Parse(payloadStr); + if (json_value != NULL) { + // check if it is a valid envelope + JSONObject json; + json = json_value->AsObject(); - // parse the channel name from the topic string - // the topic has been checked above for having jsonTopic prefix, so just move past it - char *ptr = topic + jsonTopic.length(); - ptr = strtok(ptr, "/") ? strtok(ptr, "/") : ptr; // if another "/" was added, parse string up to that character - meshtastic_Channel sendChannel = channels.getByName(ptr); - // We allow downlink JSON packets only on a channel named "mqtt" - if (strncasecmp(channels.getGlobalId(sendChannel.index), Channels::mqttChannel, strlen(Channels::mqttChannel)) == 0 && - sendChannel.settings.downlink_enabled) - { - if (isValidJsonEnvelope(json)) - { - // this is a valid envelope - if (json["type"]->AsString().compare("sendtext") == 0 && json["payload"]->IsString()) - { - std::string jsonPayloadStr = json["payload"]->AsString(); - LOG_INFO("JSON payload %s, length %u\n", jsonPayloadStr.c_str(), jsonPayloadStr.length()); + // parse the channel name from the topic string + // the topic has been checked above for having jsonTopic prefix, so just move past it + char *ptr = topic + jsonTopic.length(); + ptr = strtok(ptr, "/") ? strtok(ptr, "/") : ptr; // if another "/" was added, parse string up to that character + meshtastic_Channel sendChannel = channels.getByName(ptr); + // We allow downlink JSON packets only on a channel named "mqtt" + if (strncasecmp(channels.getGlobalId(sendChannel.index), Channels::mqttChannel, strlen(Channels::mqttChannel)) == 0 && + sendChannel.settings.downlink_enabled) { + if (isValidJsonEnvelope(json)) { + // this is a valid envelope + if (json["type"]->AsString().compare("sendtext") == 0 && json["payload"]->IsString()) { + std::string jsonPayloadStr = json["payload"]->AsString(); + LOG_INFO("JSON payload %s, length %u\n", jsonPayloadStr.c_str(), jsonPayloadStr.length()); - // construct protobuf data packet using TEXT_MESSAGE, send it to the mesh - meshtastic_MeshPacket *p = router->allocForSending(); - p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; - if (json.find("channel") != json.end() && json["channel"]->IsNumber() && - (json["channel"]->AsNumber() < channels.getNumChannels())) - p->channel = json["channel"]->AsNumber(); - if (json.find("to") != json.end() && json["to"]->IsNumber()) - p->to = json["to"]->AsNumber(); - if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber()) - p->hop_limit = json["hopLimit"]->AsNumber(); - if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) - { - memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length()); - p->decoded.payload.size = jsonPayloadStr.length(); - service->sendToMesh(p, RX_SRC_LOCAL); - } - else - { - LOG_WARN("Received MQTT json payload too long, dropping\n"); - } - } - else if (json["type"]->AsString().compare("sendposition") == 0 && json["payload"]->IsObject()) - { - // invent the "sendposition" type for a valid envelope - JSONObject posit; - posit = json["payload"]->AsObject(); // get nested JSON Position - meshtastic_Position pos = meshtastic_Position_init_default; - if (posit.find("latitude_i") != posit.end() && posit["latitude_i"]->IsNumber()) - pos.latitude_i = posit["latitude_i"]->AsNumber(); - if (posit.find("longitude_i") != posit.end() && posit["longitude_i"]->IsNumber()) - pos.longitude_i = posit["longitude_i"]->AsNumber(); - if (posit.find("altitude") != posit.end() && posit["altitude"]->IsNumber()) - pos.altitude = posit["altitude"]->AsNumber(); - if (posit.find("time") != posit.end() && posit["time"]->IsNumber()) - pos.time = posit["time"]->AsNumber(); + // construct protobuf data packet using TEXT_MESSAGE, send it to the mesh + meshtastic_MeshPacket *p = router->allocForSending(); + p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; + if (json.find("channel") != json.end() && json["channel"]->IsNumber() && + (json["channel"]->AsNumber() < channels.getNumChannels())) + p->channel = json["channel"]->AsNumber(); + if (json.find("to") != json.end() && json["to"]->IsNumber()) + p->to = json["to"]->AsNumber(); + if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber()) + p->hop_limit = json["hopLimit"]->AsNumber(); + if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) { + memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length()); + p->decoded.payload.size = jsonPayloadStr.length(); + service->sendToMesh(p, RX_SRC_LOCAL); + } else { + LOG_WARN("Received MQTT json payload too long, dropping\n"); + } + } else if (json["type"]->AsString().compare("sendposition") == 0 && json["payload"]->IsObject()) { + // invent the "sendposition" type for a valid envelope + JSONObject posit; + posit = json["payload"]->AsObject(); // get nested JSON Position + meshtastic_Position pos = meshtastic_Position_init_default; + if (posit.find("latitude_i") != posit.end() && posit["latitude_i"]->IsNumber()) + pos.latitude_i = posit["latitude_i"]->AsNumber(); + if (posit.find("longitude_i") != posit.end() && posit["longitude_i"]->IsNumber()) + pos.longitude_i = posit["longitude_i"]->AsNumber(); + if (posit.find("altitude") != posit.end() && posit["altitude"]->IsNumber()) + pos.altitude = posit["altitude"]->AsNumber(); + if (posit.find("time") != posit.end() && posit["time"]->IsNumber()) + pos.time = posit["time"]->AsNumber(); - // construct protobuf data packet using POSITION, send it to the mesh - meshtastic_MeshPacket *p = router->allocForSending(); - p->decoded.portnum = meshtastic_PortNum_POSITION_APP; - if (json.find("channel") != json.end() && json["channel"]->IsNumber() && - (json["channel"]->AsNumber() < channels.getNumChannels())) - p->channel = json["channel"]->AsNumber(); - if (json.find("to") != json.end() && json["to"]->IsNumber()) - p->to = json["to"]->AsNumber(); - if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber()) - p->hop_limit = json["hopLimit"]->AsNumber(); - p->decoded.payload.size = - pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), - &meshtastic_Position_msg, &pos); // make the Data protobuf from position - service->sendToMesh(p, RX_SRC_LOCAL); - } - else - { - LOG_DEBUG("JSON Ignoring downlink message with unsupported type.\n"); - } - } - else - { - LOG_ERROR("JSON Received payload on MQTT but not a valid envelope.\n"); - } - } - else - { - LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled.\n"); - } - } - else - { - // no json, this is an invalid payload - LOG_ERROR("JSON Received payload on MQTT but not a valid JSON\n"); - } - delete json_value; - } - else - { - if (length == 0) - { - LOG_WARN("Empty MQTT payload received, topic %s!\n", topic); - return; - } - else if (!pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, &e)) - { - LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); - return; - } - else - { - if (e.channel_id == NULL || e.gateway_id == NULL) - { - LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); - return; - } - meshtastic_Channel ch = channels.getByName(e.channel_id); - if (strcmp(e.gateway_id, owner.id) == 0) - { - // Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message. - // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node - // receives it when we get our own packet back. Then we'll stop our retransmissions. - if (e.packet && getFrom(e.packet) == nodeDB->getNodeNum()) - routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index); - else - LOG_INFO("Ignoring downlink message we originally sent.\n"); - } - else - { - // Find channel by channel_id and check downlink_enabled - if ((strcmp(e.channel_id, "PKI") == 0 && e.packet) || - (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) - { - LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length); - meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet); - p->via_mqtt = true; // Mark that the packet was received via MQTT + // construct protobuf data packet using POSITION, send it to the mesh + meshtastic_MeshPacket *p = router->allocForSending(); + p->decoded.portnum = meshtastic_PortNum_POSITION_APP; + if (json.find("channel") != json.end() && json["channel"]->IsNumber() && + (json["channel"]->AsNumber() < channels.getNumChannels())) + p->channel = json["channel"]->AsNumber(); + if (json.find("to") != json.end() && json["to"]->IsNumber()) + p->to = json["to"]->AsNumber(); + if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber()) + p->hop_limit = json["hopLimit"]->AsNumber(); + p->decoded.payload.size = + pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), + &meshtastic_Position_msg, &pos); // make the Data protobuf from position + service->sendToMesh(p, RX_SRC_LOCAL); + } else { + LOG_DEBUG("JSON Ignoring downlink message with unsupported type.\n"); + } + } else { + LOG_ERROR("JSON Received payload on MQTT but not a valid envelope.\n"); + } + } else { + LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled.\n"); + } + } else { + // no json, this is an invalid payload + LOG_ERROR("JSON Received payload on MQTT but not a valid JSON\n"); + } + delete json_value; + } else { + if (length == 0) { + LOG_WARN("Empty MQTT payload received, topic %s!\n", topic); + return; + } else if (!pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, &e)) { + LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); + return; + } else { + if (e.channel_id == NULL || e.gateway_id == NULL) { + LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); + return; + } + meshtastic_Channel ch = channels.getByName(e.channel_id); + if (strcmp(e.gateway_id, owner.id) == 0) { + // Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message. + // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node + // receives it when we get our own packet back. Then we'll stop our retransmissions. + if (e.packet && getFrom(e.packet) == nodeDB->getNodeNum()) + routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index); + else + LOG_INFO("Ignoring downlink message we originally sent.\n"); + } else { + // Find channel by channel_id and check downlink_enabled + if ((strcmp(e.channel_id, "PKI") == 0 && e.packet) || + (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) { + LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length); + meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet); + p->via_mqtt = true; // Mark that the packet was received via MQTT - if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) - { - p->channel = ch.index; - } + if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + p->channel = ch.index; + } - // PKI messages get accepted even if we can't decrypt - if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && - strcmp(e.channel_id, "PKI") == 0) - { - meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p)); - meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to); - // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's - // likely they discovered each other via a channel we have downlink enabled for - if (p->to == nodeDB->getNodeNum() || (tx && tx->has_user && rx && rx->has_user)) - router->enqueueReceivedMessage(p); - } - else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key - router->enqueueReceivedMessage(p); - else - packetPool.release(p); - } - } - } - // make sure to free both strings and the MeshPacket (passing in NULL is acceptable) - free(e.channel_id); - free(e.gateway_id); - free(e.packet); - } + // PKI messages get accepted even if we can't decrypt + if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && + strcmp(e.channel_id, "PKI") == 0) { + meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p)); + meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to); + // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's + // likely they discovered each other via a channel we have downlink enabled for + if (p->to == nodeDB->getNodeNum() || (tx && tx->has_user && rx && rx->has_user)) + router->enqueueReceivedMessage(p); + } else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key + router->enqueueReceivedMessage(p); + else + packetPool.release(p); + } + } + } + // make sure to free both strings and the MeshPacket (passing in NULL is acceptable) + free(e.channel_id); + free(e.gateway_id); + free(e.packet); + } } void mqttInit() { - new MQTT(); + new MQTT(); } #if HAS_NETWORKING @@ -229,551 +196,483 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient), mqttQueue(MAX_ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) #endif { - if (moduleConfig.mqtt.enabled) - { - LOG_DEBUG("Initializing MQTT\n"); + if (moduleConfig.mqtt.enabled) { + LOG_DEBUG("Initializing MQTT\n"); - assert(!mqtt); - mqtt = this; + assert(!mqtt); + mqtt = this; - if (*moduleConfig.mqtt.root) - { - cryptTopic = moduleConfig.mqtt.root + cryptTopic; - jsonTopic = moduleConfig.mqtt.root + jsonTopic; - mapTopic = moduleConfig.mqtt.root + mapTopic; - } - else - { - cryptTopic = "msh" + cryptTopic; - jsonTopic = "msh" + jsonTopic; - mapTopic = "msh" + mapTopic; - } + if (*moduleConfig.mqtt.root) { + cryptTopic = moduleConfig.mqtt.root + cryptTopic; + jsonTopic = moduleConfig.mqtt.root + jsonTopic; + mapTopic = moduleConfig.mqtt.root + mapTopic; + } else { + cryptTopic = "msh" + cryptTopic; + jsonTopic = "msh" + jsonTopic; + mapTopic = "msh" + mapTopic; + } - if (moduleConfig.mqtt.map_reporting_enabled && moduleConfig.mqtt.has_map_report_settings) - { - map_position_precision = Default::getConfiguredOrDefault(moduleConfig.mqtt.map_report_settings.position_precision, - default_map_position_precision); - map_publish_interval_msecs = Default::getConfiguredOrDefaultMs( - moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs); - } + if (moduleConfig.mqtt.map_reporting_enabled && moduleConfig.mqtt.has_map_report_settings) { + map_position_precision = Default::getConfiguredOrDefault(moduleConfig.mqtt.map_report_settings.position_precision, + default_map_position_precision); + map_publish_interval_msecs = Default::getConfiguredOrDefaultMs( + moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs); + } #if HAS_NETWORKING - if (!moduleConfig.mqtt.proxy_to_client_enabled) - pubSub.setCallback(mqttCallback); + if (!moduleConfig.mqtt.proxy_to_client_enabled) + pubSub.setCallback(mqttCallback); #endif - if (moduleConfig.mqtt.proxy_to_client_enabled) - { - LOG_INFO("MQTT configured to use client proxy...\n"); - enabled = true; - runASAP = true; - reconnectCount = 0; - publishNodeInfo(); - } - // preflightSleepObserver.observe(&preflightSleep); - } - else - { - disable(); - } + if (moduleConfig.mqtt.proxy_to_client_enabled) { + LOG_INFO("MQTT configured to use client proxy...\n"); + enabled = true; + runASAP = true; + reconnectCount = 0; + publishNodeInfo(); + } + // preflightSleepObserver.observe(&preflightSleep); + } else { + disable(); + } } bool MQTT::isConnectedDirectly() { #if HAS_NETWORKING - return pubSub.connected(); + return pubSub.connected(); #else - return false; + return false; #endif } bool MQTT::publish(const char *topic, const char *payload, bool retained) { - if (moduleConfig.mqtt.proxy_to_client_enabled) - { - meshtastic_MqttClientProxyMessage *msg = mqttClientProxyMessagePool.allocZeroed(); - msg->which_payload_variant = meshtastic_MqttClientProxyMessage_text_tag; - strcpy(msg->topic, topic); - strcpy(msg->payload_variant.text, payload); - msg->retained = retained; - service->sendMqttMessageToClientProxy(msg); - return true; - } + if (moduleConfig.mqtt.proxy_to_client_enabled) { + meshtastic_MqttClientProxyMessage *msg = mqttClientProxyMessagePool.allocZeroed(); + msg->which_payload_variant = meshtastic_MqttClientProxyMessage_text_tag; + strcpy(msg->topic, topic); + strcpy(msg->payload_variant.text, payload); + msg->retained = retained; + service->sendMqttMessageToClientProxy(msg); + return true; + } #if HAS_NETWORKING - else if (isConnectedDirectly()) - { - return pubSub.publish(topic, payload, retained); - } + else if (isConnectedDirectly()) { + return pubSub.publish(topic, payload, retained); + } #endif - return false; + return false; } bool MQTT::publish(const char *topic, const uint8_t *payload, size_t length, bool retained) { - if (moduleConfig.mqtt.proxy_to_client_enabled) - { - meshtastic_MqttClientProxyMessage *msg = mqttClientProxyMessagePool.allocZeroed(); - msg->which_payload_variant = meshtastic_MqttClientProxyMessage_data_tag; - strcpy(msg->topic, topic); - msg->payload_variant.data.size = length; - memcpy(msg->payload_variant.data.bytes, payload, length); - msg->retained = retained; - service->sendMqttMessageToClientProxy(msg); - return true; - } + if (moduleConfig.mqtt.proxy_to_client_enabled) { + meshtastic_MqttClientProxyMessage *msg = mqttClientProxyMessagePool.allocZeroed(); + msg->which_payload_variant = meshtastic_MqttClientProxyMessage_data_tag; + strcpy(msg->topic, topic); + msg->payload_variant.data.size = length; + memcpy(msg->payload_variant.data.bytes, payload, length); + msg->retained = retained; + service->sendMqttMessageToClientProxy(msg); + return true; + } #if HAS_NETWORKING - else if (isConnectedDirectly()) - { - return pubSub.publish(topic, payload, length, retained); - } + else if (isConnectedDirectly()) { + return pubSub.publish(topic, payload, length, retained); + } #endif - return false; + return false; } void MQTT::reconnect() { - if (wantsLink()) - { - if (moduleConfig.mqtt.proxy_to_client_enabled) - { - LOG_INFO("MQTT connecting via client proxy instead...\n"); - enabled = true; - runASAP = true; - reconnectCount = 0; + if (wantsLink()) { + if (moduleConfig.mqtt.proxy_to_client_enabled) { + LOG_INFO("MQTT connecting via client proxy instead...\n"); + enabled = true; + runASAP = true; + reconnectCount = 0; - publishNodeInfo(); - return; // Don't try to connect directly to the server - } + publishNodeInfo(); + return; // Don't try to connect directly to the server + } #if HAS_NETWORKING - // Defaults - int serverPort = 1883; - const char *serverAddr = default_mqtt_address; - const char *mqttUsername = default_mqtt_username; - const char *mqttPassword = default_mqtt_password; + // Defaults + int serverPort = 1883; + const char *serverAddr = default_mqtt_address; + const char *mqttUsername = default_mqtt_username; + const char *mqttPassword = default_mqtt_password; - if (*moduleConfig.mqtt.address) - { - serverAddr = moduleConfig.mqtt.address; - mqttUsername = moduleConfig.mqtt.username; - mqttPassword = moduleConfig.mqtt.password; - } + if (*moduleConfig.mqtt.address) { + serverAddr = moduleConfig.mqtt.address; + mqttUsername = moduleConfig.mqtt.username; + mqttPassword = moduleConfig.mqtt.password; + } #if HAS_WIFI && !defined(ARCH_PORTDUINO) - if (moduleConfig.mqtt.tls_enabled) - { - // change default for encrypted to 8883 - try - { - serverPort = 8883; - wifiSecureClient.setInsecure(); + if (moduleConfig.mqtt.tls_enabled) { + // change default for encrypted to 8883 + try { + serverPort = 8883; + wifiSecureClient.setInsecure(); - pubSub.setClient(wifiSecureClient); - LOG_INFO("Using TLS-encrypted session\n"); - } - catch (const std::exception &e) - { - LOG_ERROR("MQTT ERROR: %s\n", e.what()); - } - } - else - { - LOG_INFO("Using non-TLS-encrypted session\n"); - pubSub.setClient(mqttClient); - } + pubSub.setClient(wifiSecureClient); + LOG_INFO("Using TLS-encrypted session\n"); + } catch (const std::exception &e) { + LOG_ERROR("MQTT ERROR: %s\n", e.what()); + } + } else { + LOG_INFO("Using non-TLS-encrypted session\n"); + pubSub.setClient(mqttClient); + } #elif HAS_NETWORKING - pubSub.setClient(mqttClient); + pubSub.setClient(mqttClient); #endif - String server = String(serverAddr); - int delimIndex = server.indexOf(':'); - if (delimIndex > 0) - { - String port = server.substring(delimIndex + 1, server.length()); - server[delimIndex] = 0; - serverPort = port.toInt(); - serverAddr = server.c_str(); - } - pubSub.setServer(serverAddr, serverPort); - pubSub.setBufferSize(512); + String server = String(serverAddr); + int delimIndex = server.indexOf(':'); + if (delimIndex > 0) { + String port = server.substring(delimIndex + 1, server.length()); + server[delimIndex] = 0; + serverPort = port.toInt(); + serverAddr = server.c_str(); + } + pubSub.setServer(serverAddr, serverPort); + pubSub.setBufferSize(512); - LOG_INFO("Attempting to connect directly to MQTT server %s, port: %d, username: %s, password: %s\n", serverAddr, - serverPort, mqttUsername, mqttPassword); + LOG_INFO("Attempting to connect directly to MQTT server %s, port: %d, username: %s, password: %s\n", serverAddr, + serverPort, mqttUsername, mqttPassword); - bool connected = pubSub.connect(owner.id, mqttUsername, mqttPassword); - if (connected) - { - LOG_INFO("MQTT connected\n"); - enabled = true; // Start running background process again - runASAP = true; - reconnectCount = 0; + bool connected = pubSub.connect(owner.id, mqttUsername, mqttPassword); + if (connected) { + LOG_INFO("MQTT connected\n"); + enabled = true; // Start running background process again + runASAP = true; + reconnectCount = 0; - publishNodeInfo(); - sendSubscriptions(); - } - else - { + publishNodeInfo(); + sendSubscriptions(); + } else { #if HAS_WIFI && !defined(ARCH_PORTDUINO) - reconnectCount++; - LOG_ERROR("Failed to contact MQTT server directly (%d/%d)...\n", reconnectCount, reconnectMax); - if (reconnectCount >= reconnectMax) - { - needReconnect = true; - wifiReconnect->setIntervalFromNow(0); - reconnectCount = 0; - } + reconnectCount++; + LOG_ERROR("Failed to contact MQTT server directly (%d/%d)...\n", reconnectCount, reconnectMax); + if (reconnectCount >= reconnectMax) { + needReconnect = true; + wifiReconnect->setIntervalFromNow(0); + reconnectCount = 0; + } #endif - } + } #endif - } + } } void MQTT::sendSubscriptions() { #if HAS_NETWORKING - bool hasDownlink = false; - size_t numChan = channels.getNumChannels(); - for (size_t i = 0; i < numChan; i++) - { - const auto &ch = channels.getByIndex(i); - if (ch.settings.downlink_enabled) - { - hasDownlink = true; - std::string topic = cryptTopic + channels.getGlobalId(i) + "/+"; - LOG_INFO("Subscribing to %s\n", topic.c_str()); - pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right? + bool hasDownlink = false; + size_t numChan = channels.getNumChannels(); + for (size_t i = 0; i < numChan; i++) { + const auto &ch = channels.getByIndex(i); + if (ch.settings.downlink_enabled) { + hasDownlink = true; + std::string topic = cryptTopic + channels.getGlobalId(i) + "/+"; + LOG_INFO("Subscribing to %s\n", topic.c_str()); + pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right? #if !defined(ARCH_NRF52) || defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJSON ### - if (moduleConfig.mqtt.json_enabled == true) - { - std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+"; - LOG_INFO("Subscribing to %s\n", topicDecoded.c_str()); - pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right? - } + if (moduleConfig.mqtt.json_enabled == true) { + std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+"; + LOG_INFO("Subscribing to %s\n", topicDecoded.c_str()); + pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right? + } #endif // ARCH_NRF52 NRF52_USE_JSON - } - } + } + } #if !MESHTASTIC_EXCLUDE_PKI - if (hasDownlink) - { - std::string topic = cryptTopic + "PKI/+"; - LOG_INFO("Subscribing to %s\n", topic.c_str()); - pubSub.subscribe(topic.c_str(), 1); - } + if (hasDownlink) { + std::string topic = cryptTopic + "PKI/+"; + LOG_INFO("Subscribing to %s\n", topic.c_str()); + pubSub.subscribe(topic.c_str(), 1); + } #endif #endif } bool MQTT::wantsLink() const { - bool hasChannelorMapReport = - moduleConfig.mqtt.enabled && (moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()); + bool hasChannelorMapReport = + moduleConfig.mqtt.enabled && (moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()); - if (hasChannelorMapReport && moduleConfig.mqtt.proxy_to_client_enabled) - return true; + if (hasChannelorMapReport && moduleConfig.mqtt.proxy_to_client_enabled) + return true; #if HAS_WIFI - return hasChannelorMapReport && WiFi.isConnected(); + return hasChannelorMapReport && WiFi.isConnected(); #endif #if HAS_ETHERNET - return hasChannelorMapReport && Ethernet.linkStatus() == LinkON; + return hasChannelorMapReport && Ethernet.linkStatus() == LinkON; #endif - return false; + return false; } int32_t MQTT::runOnce() { #if HAS_NETWORKING - if (!moduleConfig.mqtt.enabled || !(moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled())) - return disable(); + if (!moduleConfig.mqtt.enabled || !(moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled())) + return disable(); - bool wantConnection = wantsLink(); + bool wantConnection = wantsLink(); - perhapsReportToMap(); + perhapsReportToMap(); - // If connected poll rapidly, otherwise only occasionally check for a wifi connection change and ability to contact server - if (moduleConfig.mqtt.proxy_to_client_enabled) - { - publishQueuedMessages(); - return 200; - } - else if (!pubSub.loop()) - { - if (!wantConnection) - return 5000; // If we don't want connection now, check again in 5 secs - else - { - reconnect(); - // If we succeeded, empty the queue one by one and start reading rapidly, else try again in 30 seconds (TCP - // connections are EXPENSIVE so try rarely) - if (isConnectedDirectly()) - { - publishQueuedMessages(); - return 200; - } - else - return 30000; - } - } - else - { - // we are connected to server, check often for new requests on the TCP port - if (!wantConnection) - { - LOG_INFO("MQTT link not needed, dropping\n"); - pubSub.disconnect(); - } + // If connected poll rapidly, otherwise only occasionally check for a wifi connection change and ability to contact server + if (moduleConfig.mqtt.proxy_to_client_enabled) { + publishQueuedMessages(); + return 200; + } - powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // Suppress entering light sleep (because that would turn off bluetooth) - return 20; - } + else if (!pubSub.loop()) { + if (!wantConnection) + return 5000; // If we don't want connection now, check again in 5 secs + else { + reconnect(); + // If we succeeded, empty the queue one by one and start reading rapidly, else try again in 30 seconds (TCP + // connections are EXPENSIVE so try rarely) + if (isConnectedDirectly()) { + publishQueuedMessages(); + return 200; + } else + return 30000; + } + } else { + // we are connected to server, check often for new requests on the TCP port + if (!wantConnection) { + LOG_INFO("MQTT link not needed, dropping\n"); + pubSub.disconnect(); + } + + powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // Suppress entering light sleep (because that would turn off bluetooth) + return 20; + } #endif - return 30000; + return 30000; } void MQTT::publishNodeInfo() { - // TODO: NodeInfo broadcast over MQTT only (NODENUM_BROADCAST_NO_LORA) + // TODO: NodeInfo broadcast over MQTT only (NODENUM_BROADCAST_NO_LORA) } void MQTT::publishQueuedMessages() { - if (!mqttQueue.isEmpty()) - { - LOG_DEBUG("Publishing enqueued MQTT message\n"); - // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets - meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0); - static uint8_t bytes[meshtastic_MeshPacket_size + 64]; - size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); - std::string topic; - if (env->packet->pki_encrypted) - { - topic = cryptTopic + "PKI/" + owner.id; - } - else - { - topic = cryptTopic + env->channel_id + "/" + owner.id; - } - LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes); + if (!mqttQueue.isEmpty()) { + LOG_DEBUG("Publishing enqueued MQTT message\n"); + // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets + meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0); + static uint8_t bytes[meshtastic_MeshPacket_size + 64]; + size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); + std::string topic; + if (env->packet->pki_encrypted) { + topic = cryptTopic + "PKI/" + owner.id; + } else { + topic = cryptTopic + env->channel_id + "/" + owner.id; + } + LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes); - publish(topic.c_str(), bytes, numBytes, false); + publish(topic.c_str(), bytes, numBytes, false); #if !defined(ARCH_NRF52) || defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### - if (moduleConfig.mqtt.json_enabled) - { - // handle json topic - auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet); - if (jsonString.length() != 0) - { - std::string topicJson; - if (env->packet->pki_encrypted) - { - topicJson = jsonTopic + "PKI/" + owner.id; - } - else - { - topicJson = jsonTopic + env->channel_id + "/" + owner.id; - } - LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), - jsonString.c_str()); - publish(topicJson.c_str(), jsonString.c_str(), false); - } - } + if (moduleConfig.mqtt.json_enabled) { + // handle json topic + auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet); + if (jsonString.length() != 0) { + std::string topicJson; + if (env->packet->pki_encrypted) { + topicJson = jsonTopic + "PKI/" + owner.id; + } else { + topicJson = jsonTopic + env->channel_id + "/" + owner.id; + } + LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), + jsonString.c_str()); + publish(topicJson.c_str(), jsonString.c_str(), false); + } + } #endif // ARCH_NRF52 NRF52_USE_JSON - mqttPool.release(env); - } + mqttPool.release(env); + } } void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &mp_decoded, ChannelIndex chIndex) { - if (mp.via_mqtt) - return; // Don't send messages that came from MQTT back into MQTT - bool uplinkEnabled = false; - for (int i = 0; i <= 7; i++) - { - if (channels.getByIndex(i).settings.uplink_enabled) - uplinkEnabled = true; - } - if (!uplinkEnabled) - return; // no channels have an uplink enabled - auto &ch = channels.getByIndex(chIndex); + if (mp.via_mqtt) + return; // Don't send messages that came from MQTT back into MQTT + bool uplinkEnabled = false; + for (int i = 0; i <= 7; i++) { + if (channels.getByIndex(i).settings.uplink_enabled) + uplinkEnabled = true; + } + if (!uplinkEnabled) + return; // no channels have an uplink enabled + auto &ch = channels.getByIndex(chIndex); - if (!mp.pki_encrypted) - { - if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) - { - LOG_CRIT("MQTT::onSend(): mp_decoded isn't actually decoded\n"); - return; - } + if (!mp.pki_encrypted) { + if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) { + LOG_CRIT("MQTT::onSend(): mp_decoded isn't actually decoded\n"); + return; + } - // check for the lowest bit of the data bitfield set false, and the use of one of the default keys. - if (mp_decoded.from != nodeDB->getNodeNum() && mp_decoded.decoded.has_bitfield && - !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK) && - (ch.settings.psk.size < 2 || (ch.settings.psk.size == 16 && memcmp(ch.settings.psk.bytes, defaultpsk, 16)) || - (ch.settings.psk.size == 32 && memcmp(ch.settings.psk.bytes, eventpsk, 32)))) - { - LOG_INFO("MQTT onSend - Not forwarding packet due to DontMqttMeBro flag\n"); - return; - } + // check for the lowest bit of the data bitfield set false, and the use of one of the default keys. + if (mp_decoded.from != nodeDB->getNodeNum() && mp_decoded.decoded.has_bitfield && + !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK) && + (ch.settings.psk.size < 2 || (ch.settings.psk.size == 16 && memcmp(ch.settings.psk.bytes, defaultpsk, 16)) || + (ch.settings.psk.size == 32 && memcmp(ch.settings.psk.bytes, eventpsk, 32)))) { + LOG_INFO("MQTT onSend - Not forwarding packet due to DontMqttMeBro flag\n"); + return; + } - if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 && - (mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP || - mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) - { - LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt\n"); - return; - } - } - if (mp.pki_encrypted || ch.settings.uplink_enabled) - { - const char *channelId = mp.pki_encrypted ? "PKI" : channels.getGlobalId(chIndex); + if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 && + (mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP || + mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) { + LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt\n"); + return; + } + } + if (mp.pki_encrypted || ch.settings.uplink_enabled) { + const char *channelId = mp.pki_encrypted ? "PKI" : channels.getGlobalId(chIndex); - meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed(); - env->channel_id = (char *)channelId; - env->gateway_id = owner.id; + meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed(); + env->channel_id = (char *)channelId; + env->gateway_id = owner.id; - LOG_DEBUG("MQTT onSend - Publishing "); - if (moduleConfig.mqtt.encryption_enabled) - { - env->packet = (meshtastic_MeshPacket *)∓ - LOG_DEBUG("encrypted message\n"); - } - else if (mp_decoded.which_payload_variant == - meshtastic_MeshPacket_decoded_tag) - { // Don't upload a still-encrypted PKI packet - env->packet = (meshtastic_MeshPacket *)&mp_decoded; - LOG_DEBUG("portnum %i message\n", env->packet->decoded.portnum); - } + LOG_DEBUG("MQTT onSend - Publishing "); + if (moduleConfig.mqtt.encryption_enabled) { + env->packet = (meshtastic_MeshPacket *)∓ + LOG_DEBUG("encrypted message\n"); + } else if (mp_decoded.which_payload_variant == + meshtastic_MeshPacket_decoded_tag) { // Don't upload a still-encrypted PKI packet + env->packet = (meshtastic_MeshPacket *)&mp_decoded; + LOG_DEBUG("portnum %i message\n", env->packet->decoded.portnum); + } - if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) - { - // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets - static uint8_t bytes[meshtastic_MeshPacket_size + 64]; - size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); - std::string topic = cryptTopic + channelId + "/" + owner.id; - LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes); + if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) { + // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets + static uint8_t bytes[meshtastic_MeshPacket_size + 64]; + size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); + std::string topic = cryptTopic + channelId + "/" + owner.id; + LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes); - publish(topic.c_str(), bytes, numBytes, false); + publish(topic.c_str(), bytes, numBytes, false); #if !defined(ARCH_NRF52) || defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### - if (moduleConfig.mqtt.json_enabled) - { - // handle json topic - auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); - if (jsonString.length() != 0) - { - std::string topicJson = jsonTopic + channelId + "/" + owner.id; - LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), - jsonString.c_str()); - publish(topicJson.c_str(), jsonString.c_str(), false); - } - } + if (moduleConfig.mqtt.json_enabled) { + // handle json topic + auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); + if (jsonString.length() != 0) { + std::string topicJson = jsonTopic + channelId + "/" + owner.id; + LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), + jsonString.c_str()); + publish(topicJson.c_str(), jsonString.c_str(), false); + } + } #endif // ARCH_NRF52 NRF52_USE_JSON - } - else - { - LOG_INFO("MQTT not connected, queueing packet\n"); - if (mqttQueue.numFree() == 0) - { - LOG_WARN("NOTE: MQTT queue is full, discarding oldest\n"); - meshtastic_ServiceEnvelope *d = mqttQueue.dequeuePtr(0); - if (d) - mqttPool.release(d); - } - // make a copy of serviceEnvelope and queue it - meshtastic_ServiceEnvelope *copied = mqttPool.allocCopy(*env); - assert(mqttQueue.enqueue(copied, 0)); - } - mqttPool.release(env); - } + } else { + LOG_INFO("MQTT not connected, queueing packet\n"); + if (mqttQueue.numFree() == 0) { + LOG_WARN("NOTE: MQTT queue is full, discarding oldest\n"); + meshtastic_ServiceEnvelope *d = mqttQueue.dequeuePtr(0); + if (d) + mqttPool.release(d); + } + // make a copy of serviceEnvelope and queue it + meshtastic_ServiceEnvelope *copied = mqttPool.allocCopy(*env); + assert(mqttQueue.enqueue(copied, 0)); + } + mqttPool.release(env); + } } void MQTT::perhapsReportToMap() { - if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly())) - return; + if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly())) + return; - if (millis() - last_report_to_map < map_publish_interval_msecs) - { - return; - } - else - { - if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) - { - last_report_to_map = millis(); - if (map_position_precision == 0) - LOG_WARN("MQTT Map reporting is enabled, but precision is 0\n"); - if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) - LOG_WARN("MQTT Map reporting is enabled, but no position available.\n"); - return; - } + if (millis() - last_report_to_map < map_publish_interval_msecs) { + return; + } else { + if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { + last_report_to_map = millis(); + if (map_position_precision == 0) + LOG_WARN("MQTT Map reporting is enabled, but precision is 0\n"); + if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) + LOG_WARN("MQTT Map reporting is enabled, but no position available.\n"); + return; + } - // Allocate ServiceEnvelope and fill it - meshtastic_ServiceEnvelope *se = mqttPool.allocZeroed(); - se->channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()); // Use primary channel as the channel_id - se->gateway_id = owner.id; + // Allocate ServiceEnvelope and fill it + meshtastic_ServiceEnvelope *se = mqttPool.allocZeroed(); + se->channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()); // Use primary channel as the channel_id + se->gateway_id = owner.id; - // Allocate MeshPacket and fill it - meshtastic_MeshPacket *mp = packetPool.allocZeroed(); - mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag; - mp->from = nodeDB->getNodeNum(); - mp->to = NODENUM_BROADCAST; - mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP; + // Allocate MeshPacket and fill it + meshtastic_MeshPacket *mp = packetPool.allocZeroed(); + mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag; + mp->from = nodeDB->getNodeNum(); + mp->to = NODENUM_BROADCAST; + mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP; - // Fill MapReport message - meshtastic_MapReport mapReport = meshtastic_MapReport_init_default; - memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name)); - memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name)); - mapReport.role = config.device.role; - mapReport.hw_model = owner.hw_model; - strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version)); - mapReport.region = config.lora.region; - mapReport.modem_preset = config.lora.modem_preset; - mapReport.has_default_channel = channels.hasDefaultChannel(); + // Fill MapReport message + meshtastic_MapReport mapReport = meshtastic_MapReport_init_default; + memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name)); + memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name)); + mapReport.role = config.device.role; + mapReport.hw_model = owner.hw_model; + strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version)); + mapReport.region = config.lora.region; + mapReport.modem_preset = config.lora.modem_preset; + mapReport.has_default_channel = channels.hasDefaultChannel(); - // Set position with precision (same as in PositionModule) - if (map_position_precision < 32 && map_position_precision > 0) - { - mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision)); - mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision)); - mapReport.latitude_i += (1 << (31 - map_position_precision)); - mapReport.longitude_i += (1 << (31 - map_position_precision)); - } - else - { - mapReport.latitude_i = localPosition.latitude_i; - mapReport.longitude_i = localPosition.longitude_i; - } - mapReport.altitude = localPosition.altitude; - mapReport.position_precision = map_position_precision; + // Set position with precision (same as in PositionModule) + if (map_position_precision < 32 && map_position_precision > 0) { + mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision)); + mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision)); + mapReport.latitude_i += (1 << (31 - map_position_precision)); + mapReport.longitude_i += (1 << (31 - map_position_precision)); + } else { + mapReport.latitude_i = localPosition.latitude_i; + mapReport.longitude_i = localPosition.longitude_i; + } + mapReport.altitude = localPosition.altitude; + mapReport.position_precision = map_position_precision; - mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true); + mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true); - // Encode MapReport message and set it to MeshPacket in ServiceEnvelope - mp->decoded.payload.size = pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), - &meshtastic_MapReport_msg, &mapReport); - se->packet = mp; + // Encode MapReport message and set it to MeshPacket in ServiceEnvelope + mp->decoded.payload.size = pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), + &meshtastic_MapReport_msg, &mapReport); + se->packet = mp; - // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets - static uint8_t bytes[meshtastic_MeshPacket_size + 64]; - size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se); + // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets + static uint8_t bytes[meshtastic_MeshPacket_size + 64]; + size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se); - LOG_INFO("MQTT Publish map report to %s\n", mapTopic.c_str()); - publish(mapTopic.c_str(), bytes, numBytes, false); + LOG_INFO("MQTT Publish map report to %s\n", mapTopic.c_str()); + publish(mapTopic.c_str(), bytes, numBytes, false); - // Release the allocated memory for ServiceEnvelope and MeshPacket - mqttPool.release(se); - packetPool.release(mp); + // Release the allocated memory for ServiceEnvelope and MeshPacket + mqttPool.release(se); + packetPool.release(mp); - // Update the last report time - last_report_to_map = millis(); - } + // Update the last report time + last_report_to_map = millis(); + } } bool MQTT::isValidJsonEnvelope(JSONObject &json) { - // if "sender" is provided, avoid processing packets we uplinked - return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(owner.id) != 0) : true) && - (json.find("hopLimit") != json.end() ? json["hopLimit"]->IsNumber() : true) && // hop limit should be a number - (json.find("from") != json.end()) && json["from"]->IsNumber() && - (json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us - (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type - (json.find("payload") != json.end()); // should have a payload + // if "sender" is provided, avoid processing packets we uplinked + return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(owner.id) != 0) : true) && + (json.find("hopLimit") != json.end() ? json["hopLimit"]->IsNumber() : true) && // hop limit should be a number + (json.find("from") != json.end()) && json["from"]->IsNumber() && + (json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us + (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type + (json.find("payload") != json.end()); // should have a payload } diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 9ed2a0d6a..817583821 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -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,261 +70,248 @@ 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 - { + screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { char btPIN[16] = "888888"; snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey); int x_offset = display->width() / 2; @@ -347,35 +334,34 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke String deviceName = "Name: "; deviceName.concat(getDeviceName()); 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); }); + 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); } \ No newline at end of file