| | |
| | | .callback = [](void* arg) { |
| | | MqttProtocol* protocol = (MqttProtocol*)arg; |
| | | auto& app = Application::GetInstance(); |
| | | if (app.GetDeviceState() == kDeviceStateIdle) { |
| | | if (app.GetDeviceState() == kDeviceStateIdle && Board::GetInstance().IsNetworkReady()) { |
| | | ESP_LOGI(TAG, "Reconnecting to MQTT server"); |
| | | auto alive = protocol->alive_; // Capture alive flag |
| | | app.Schedule([protocol, alive]() { |
| | | if (*alive) { |
| | | if (*alive && Board::GetInstance().IsNetworkReady()) { |
| | | protocol->StartMqttClient(false); |
| | | } |
| | | }); |
| | |
| | | return StartMqttClient(false); |
| | | } |
| | | |
| | | void MqttProtocol::PrepareForNetworkStandby() { |
| | | network_standby_preparing_ = true; |
| | | if (reconnect_timer_ != nullptr) { |
| | | esp_timer_stop(reconnect_timer_); |
| | | } |
| | | audio_channel_opened_ = false; |
| | | udp_.reset(); |
| | | if (mqtt_ != nullptr) { |
| | | mqtt_->Disconnect(); |
| | | mqtt_.reset(); |
| | | } |
| | | network_standby_preparing_ = false; |
| | | } |
| | | |
| | | bool MqttProtocol::StartMqttClient(bool report_error) { |
| | | if (mqtt_ != nullptr) { |
| | | ESP_LOGW(TAG, "Mqtt client already started"); |
| | | audio_channel_opened_ = false; |
| | | udp_.reset(); |
| | | mqtt_.reset(); |
| | | } |
| | | |
| | |
| | | mqtt_->SetKeepAlive(keepalive_interval); |
| | | |
| | | mqtt_->OnDisconnected([this]() { |
| | | if (network_standby_preparing_ || !Board::GetInstance().IsNetworkReady()) { |
| | | return; |
| | | } |
| | | if (on_disconnected_ != nullptr) { |
| | | on_disconnected_(); |
| | | } |
| | |
| | | } |
| | | if (!mqtt_->Connect(broker_address, broker_port, client_id, username, password)) { |
| | | ESP_LOGE(TAG, "Failed to connect to endpoint, code=%d", mqtt_->GetLastError()); |
| | | SetError(Lang::Strings::SERVER_NOT_CONNECTED); |
| | | if (report_error) { |
| | | SetError(Lang::Strings::SERVER_NOT_CONNECTED); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | bool MqttProtocol::SendAudio(std::unique_ptr<AudioStreamPacket> packet) { |
| | | std::lock_guard<std::mutex> lock(channel_mutex_); |
| | | if (udp_ == nullptr) { |
| | | return false; |
| | | bool notify_channel_closed = false; |
| | | |
| | | { |
| | | std::lock_guard<std::mutex> lock(channel_mutex_); |
| | | if (udp_ == nullptr || !audio_channel_opened_) { |
| | | return false; |
| | | } |
| | | if (!Board::GetInstance().IsNetworkReady()) { |
| | | ESP_LOGW(TAG, "Network is not ready, drop UDP audio and close channel"); |
| | | audio_channel_opened_ = false; |
| | | udp_.reset(); |
| | | notify_channel_closed = true; |
| | | } else { |
| | | std::string nonce(aes_nonce_); |
| | | *(uint16_t*)&nonce[2] = htons(packet->payload.size()); |
| | | *(uint32_t*)&nonce[8] = htonl(packet->timestamp); |
| | | *(uint32_t*)&nonce[12] = htonl(++local_sequence_); |
| | | |
| | | std::string encrypted; |
| | | encrypted.resize(aes_nonce_.size() + packet->payload.size()); |
| | | memcpy(encrypted.data(), nonce.data(), nonce.size()); |
| | | |
| | | size_t nc_off = 0; |
| | | uint8_t stream_block[16] = {0}; |
| | | if (mbedtls_aes_crypt_ctr(&aes_ctx_, packet->payload.size(), &nc_off, (uint8_t*)nonce.c_str(), stream_block, |
| | | (uint8_t*)packet->payload.data(), (uint8_t*)&encrypted[nonce.size()]) != 0) { |
| | | ESP_LOGE(TAG, "Failed to encrypt audio data"); |
| | | return false; |
| | | } |
| | | |
| | | int ret = udp_->Send(encrypted); |
| | | if (ret <= 0) { |
| | | ESP_LOGW(TAG, "UDP audio send failed, close audio channel"); |
| | | audio_channel_opened_ = false; |
| | | udp_.reset(); |
| | | notify_channel_closed = true; |
| | | } else { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | std::string nonce(aes_nonce_); |
| | | *(uint16_t*)&nonce[2] = htons(packet->payload.size()); |
| | | *(uint32_t*)&nonce[8] = htonl(packet->timestamp); |
| | | *(uint32_t*)&nonce[12] = htonl(++local_sequence_); |
| | | |
| | | std::string encrypted; |
| | | encrypted.resize(aes_nonce_.size() + packet->payload.size()); |
| | | memcpy(encrypted.data(), nonce.data(), nonce.size()); |
| | | |
| | | size_t nc_off = 0; |
| | | uint8_t stream_block[16] = {0}; |
| | | if (mbedtls_aes_crypt_ctr(&aes_ctx_, packet->payload.size(), &nc_off, (uint8_t*)nonce.c_str(), stream_block, |
| | | (uint8_t*)packet->payload.data(), (uint8_t*)&encrypted[nonce.size()]) != 0) { |
| | | ESP_LOGE(TAG, "Failed to encrypt audio data"); |
| | | return false; |
| | | if (notify_channel_closed && on_audio_channel_closed_ != nullptr) { |
| | | on_audio_channel_closed_(); |
| | | } |
| | | |
| | | return udp_->Send(encrypted) > 0; |
| | | return false; |
| | | } |
| | | |
| | | void MqttProtocol::CloseAudioChannel(bool send_goodbye) { |
| | | if (!audio_channel_opened_) { |
| | | return; |
| | | } |
| | | audio_channel_opened_ = false; |
| | | |
| | | { |
| | | std::lock_guard<std::mutex> lock(channel_mutex_); |
| | | udp_.reset(); |
| | |
| | | }); |
| | | |
| | | udp_->Connect(udp_server_, udp_port_); |
| | | audio_channel_opened_ = true; |
| | | |
| | | if (on_audio_channel_opened_ != nullptr) { |
| | | on_audio_channel_opened_(); |
| | |
| | | } |
| | | |
| | | bool MqttProtocol::IsAudioChannelOpened() const { |
| | | return udp_ != nullptr && !error_occurred_ && !IsTimeout(); |
| | | return audio_channel_opened_ && udp_ != nullptr && !error_occurred_ && !IsTimeout(); |
| | | } |