-
-
Save tube0013/9dae3a83bc2118153830aa21559f6fbc to your computer and use it in GitHub Desktop.
Stream server (serial-to-wifi bridge) for ESPHome
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| esphome: | |
| # ... | |
| includes: | |
| - stream_server.h | |
| - stream_server.cpp | |
| uart: | |
| id: uart_bus | |
| # ... | |
| custom_component: | |
| - lambda: |- | |
| auto stream_server = new StreamServerComponent(id(uart_bus)); | |
| return {stream_server}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Copyright (C) 2020 Oxan van Leeuwen | |
| * | |
| * This program is free software: you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation, either version 3 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| */ | |
| #include "stream_server.h" | |
| #include "esphome/core/log.h" | |
| #include "esphome/core/util.h" | |
| static const char *TAG = "streamserver"; | |
| using namespace esphome; | |
| void StreamServerComponent::setup() { | |
| ESP_LOGCONFIG(TAG, "Setting up stream server..."); | |
| this->recv_buf_.reserve(128); | |
| this->server_ = AsyncServer(this->port_); | |
| this->server_.begin(); | |
| this->server_.onClient([this](void *h, AsyncClient *tcpClient) { | |
| if(tcpClient == nullptr) | |
| return; | |
| this->clients_.push_back(std::unique_ptr<Client>(new Client(tcpClient, this->recv_buf_))); | |
| }, this); | |
| } | |
| void StreamServerComponent::loop() { | |
| this->cleanup(); | |
| this->read(); | |
| this->write(); | |
| } | |
| void StreamServerComponent::cleanup() { | |
| auto discriminator = [](std::unique_ptr<Client> &client) { return !client->disconnected; }; | |
| auto last_client = std::partition(this->clients_.begin(), this->clients_.end(), discriminator); | |
| for (auto it = last_client; it != this->clients_.end(); it++) | |
| ESP_LOGD(TAG, "Client %s disconnected", (*it)->identifier.c_str()); | |
| this->clients_.erase(last_client, this->clients_.end()); | |
| } | |
| void StreamServerComponent::read() { | |
| int len; | |
| while ((len = this->stream_->available()) > 0) { | |
| char buf[128]; | |
| size_t read = this->stream_->readBytes(buf, min(len, 128)); | |
| for (auto const& client : this->clients_) | |
| client->tcp_client->write(buf, read); | |
| } | |
| } | |
| void StreamServerComponent::write() { | |
| size_t len; | |
| while ((len = this->recv_buf_.size()) > 0) { | |
| this->stream_->write(this->recv_buf_.data(), len); | |
| this->recv_buf_.erase(this->recv_buf_.begin(), this->recv_buf_.begin() + len); | |
| } | |
| } | |
| void StreamServerComponent::dump_config() { | |
| ESP_LOGCONFIG(TAG, "Stream Server:"); | |
| ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_); | |
| } | |
| void StreamServerComponent::on_shutdown() { | |
| for (auto &client : this->clients_) | |
| client->tcp_client->close(true); | |
| } | |
| StreamServerComponent::Client::Client(AsyncClient *client, std::vector<uint8_t> &recv_buf) : | |
| tcp_client{client}, identifier{client->remoteIP().toString().c_str()}, disconnected{false} { | |
| ESP_LOGD(TAG, "New client connected from %s", this->identifier.c_str()); | |
| this->tcp_client->onError( [this](void *h, AsyncClient *client, int8_t error) { this->disconnected = true; }); | |
| this->tcp_client->onDisconnect([this](void *h, AsyncClient *client) { this->disconnected = true; }); | |
| this->tcp_client->onTimeout( [this](void *h, AsyncClient *client, uint32_t time) { this->disconnected = true; }); | |
| this->tcp_client->onData([&](void *h, AsyncClient *client, void *data, size_t len) { | |
| if (len == 0 || data == nullptr) | |
| return; | |
| auto buf = static_cast<uint8_t *>(data); | |
| recv_buf.insert(recv_buf.end(), buf, buf + len); | |
| }, nullptr); | |
| } | |
| StreamServerComponent::Client::~Client() { | |
| delete this->tcp_client; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Copyright (C) 2020 Oxan van Leeuwen | |
| * | |
| * This program is free software: you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation, either version 3 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| */ | |
| #pragma once | |
| #include "esphome/core/component.h" | |
| #include <memory> | |
| #include <string> | |
| #include <vector> | |
| #include <Stream.h> | |
| #ifdef ARDUINO_ARCH_ESP8266 | |
| #include <ESPAsyncTCP.h> | |
| #else | |
| #include <AsyncTCP.h> | |
| #endif | |
| class StreamServerComponent : public esphome::Component { | |
| public: | |
| explicit StreamServerComponent(Stream *stream) : stream_{stream} {} | |
| void setup() override; | |
| void loop() override; | |
| void dump_config() override; | |
| void on_shutdown() override; | |
| float get_setup_priority() const override { return esphome::setup_priority::AFTER_WIFI; } | |
| void set_port(uint16_t port) { this->port_ = port; } | |
| protected: | |
| void cleanup(); | |
| void read(); | |
| void write(); | |
| struct Client { | |
| Client(AsyncClient *client, std::vector<uint8_t> &recv_buf); | |
| ~Client(); | |
| AsyncClient *tcp_client{nullptr}; | |
| std::string identifier{}; | |
| bool disconnected{false}; | |
| }; | |
| Stream *stream_{nullptr}; | |
| AsyncServer server_{0}; | |
| uint16_t port_{6638}; | |
| std::vector<uint8_t> recv_buf_{}; | |
| std::vector<std::unique_ptr<Client>> clients_{}; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment