попытки сделать crsf, не взлетела часть с пультом

This commit is contained in:
2025-11-24 16:24:59 +03:00
parent 0f14fd0155
commit 3eaea1b966
20 changed files with 769 additions and 535 deletions

View File

@@ -0,0 +1,2 @@

54
lib/port/poller.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef SDRPI_FPV_CONTROL_GROUND_PORT_POLLER_H
#define SDRPI_FPV_CONTROL_GROUND_PORT_POLLER_H
#include <memory>
#include <vector>
namespace poller {
/**
* PollObject - базовый объект для мониторинга I/O.
* Платформенно-специфичные дескрипторы хранятся в защищённых полях.
*/
class PollObject {
public:
bool isPollIn() const;
bool isPollOut() const;
bool isPollHup() const;
virtual ~PollObject();
protected:
#ifdef _WIN32
// UART HANDLE или UDP событие
HANDLE hCom{INVALID_HANDLE_VALUE};
SOCKET sock{INVALID_SOCKET};
WSAEVENT winHandle{nullptr};
short revents{0}; // событие произошло
#else
int fd{0};
short events{0};
short revents{0};
#endif
friend class PollWrapper;
};
/**
* Класс-обертка для мониторинга файловых дискрипторов. Использует стек для хранения массива структур мониторинга.
*/
class PollWrapper {
public:
PollWrapper();
std::vector<std::shared_ptr<PollObject>> objects;
/**
* Функция, которую нужно вызывать в бесконечном цикле. Вызывает `poll`, после чего выполняет обработчики событий, если нужно.
* @param timeoutMs
*/
void loop(int timeoutMs = -1);
~PollWrapper();
};
} // namespace poller
#endif //SDRPI_FPV_CONTROL_GROUND_PORT_POLLER_H

25
lib/port/uart.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef SDRPI_FPV_CONTROL_GROUND_PORT_UART_H
#define SDRPI_FPV_CONTROL_GROUND_PORT_UART_H
#include <cstdint>
#include "port/poller.h"
#include <string>
#include <vector>
#include <span>
namespace drivers {
class UartDriver : public poller::PollObject {
public:
UartDriver(const std::string& path, int baud);
bool writeData(std::span<const uint8_t> data);
size_t readChunk(std::vector<uint8_t>& out);
~UartDriver() override;
#if _WIN32
protected:
OVERLAPPED overlapped{};
#endif
};
}
#endif //SDRPI_FPV_CONTROL_GROUND_PORT_UART_H

21
lib/port/udp.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef SDRPI_FPV_CONTROL_GROUND_PORT_UDP_H
#define SDRPI_FPV_CONTROL_GROUND_PORT_UDP_H
#include "port/poller.h"
#include <string>
#include <vector>
#include <cstdint>
#include <span>
namespace drivers {
class UdpDriver : public poller::PollObject {
public:
// port - локальный порт
explicit UdpDriver(uint16_t port);
bool sendTo(std::span<const uint8_t> data, const std::string& addr, uint16_t port);
bool recvPacket(std::vector<uint8_t>& out);
~UdpDriver() override;
};
}
#endif //SDRPI_FPV_CONTROL_GROUND_PORT_UDP_H

51
lib/port/unix/poller.cpp Normal file
View File

@@ -0,0 +1,51 @@
#include "port/poller.h"
#include <sys/poll.h>
#include <algorithm>
bool poller::PollObject::isPollIn() const { return revents & POLLIN; }
bool poller::PollObject::isPollOut() const { return revents & POLLOUT; }
bool poller::PollObject::isPollHup() const { return revents & POLLHUP; }
poller::PollObject::~PollObject() = default;
poller::PollWrapper::PollWrapper() = default;
void poller::PollWrapper::loop(int timeoutMs) {
if (this->objects.empty()) {
return;
}
// проверяем, что нет объектов с fd < 0, удаляем такие объекты если они есть
for (size_t index = 0; index < this->objects.size();) {
if (this->objects[index]->fd < 0) {
this->objects.erase(this->objects.begin() + static_cast<ssize_t>(index));
} else {
index++;
}
}
const auto qsize = this->objects.size();
// массив для вызова poll
pollfd pollFds[qsize];
// заполняем данные для poll
for (size_t i = 0; i < qsize; i++) {
pollFds[i].revents = 0;
pollFds[i].fd = this->objects[i]->fd;
pollFds[i].events = this->objects[i]->events;
}
// выполняем poll
poll(pollFds, qsize, timeoutMs);
// проверяем события
for (size_t i = 0; i < qsize; i++) {
objects[i]->revents = pollFds[i].revents;
// if (pollFds[i].revents != 0) {
// objects[i]->callback();
// }
}
}
poller::PollWrapper::~PollWrapper() = default;

60
lib/port/unix/uart.cpp Normal file
View File

@@ -0,0 +1,60 @@
#include "port/uart.h"
#include <sys/ioctl.h>
#include <asm/termbits.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <iostream>
#include <vector>
#include <cstring>
#include <atomic>
#include <span>
drivers::UartDriver::UartDriver(const std::string& path, int baud) {
fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0) throw std::runtime_error(std::string("UartDriver open error ") + path + ": " + std::strerror(errno));
struct termios2 tio{};
if (ioctl(fd, TCGETS2, &tio) != 0) {
close(fd);
throw std::runtime_error(std::string("UartDriver setup error: ") + std::strerror(errno));
}
// 8bit
tio.c_cflag &= ~CSIZE;
tio.c_cflag |= CS8;
// baud rate
tio.c_ispeed = baud;
tio.c_ospeed = baud;
// other
tio.c_iflag |= (INPCK|IGNBRK|IGNCR|ISTRIP);
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= (BOTHER|CREAD|CLOCAL);
if (ioctl(fd, TCSETS2, &tio) != 0) {
close(fd);
throw std::runtime_error(std::string("UartDriver setup error: ") + std::strerror(errno));
}
events = POLLIN;
}
bool drivers::UartDriver::writeData(std::span<const uint8_t> data) {
if (fd < 0) return false;
auto w = write(fd, data.data(), data.size());
return w == data.size();
}
size_t drivers::UartDriver::readChunk(std::vector<uint8_t>& out) {
if (fd < 0) return 0;
out.resize(1024);
auto r = read(fd, out.data(), out.size());
if (r <= 0) return 0;
out.resize(r);
return static_cast<size_t>(r);
}
drivers::UartDriver::~UartDriver() {
close(fd);
}

55
lib/port/unix/udp.cpp Normal file
View File

@@ -0,0 +1,55 @@
#include "port/udp.h"
#include <cstring>
#include <poll.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "crsf.h"
drivers::UdpDriver::UdpDriver(uint16_t port) {
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
throw std::runtime_error(std::string("UdpDriver open error: ") + std::strerror(errno));
}
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (sockaddr*)&addr, sizeof(addr)) < 0) if (fd < 0) {
close(fd);
throw std::runtime_error(std::string("UdpDriver bind error: ") + std::strerror(errno));
}
events = POLLIN;
}
bool drivers::UdpDriver::sendTo(std::span<const uint8_t> data, const std::string& addr, uint16_t port) {
if (fd < 0) return false;
sockaddr_in dst{};
dst.sin_family = AF_INET;
dst.sin_port = htons(port);
inet_aton(addr.c_str(), &dst.sin_addr);
auto w = sendto(fd, data.data(), data.size(), 0, (sockaddr*)&dst, sizeof(dst));
return w == data.size();
}
bool drivers::UdpDriver::recvPacket(std::vector<uint8_t>& out) {
out.resize(crsf::CRSF_MAX_FRAME_SIZE);
ssize_t received = recvfrom(fd, out.data(), out.size(), MSG_DONTWAIT, nullptr, nullptr);
if (received > 0) {
out.resize(received);
return true;
}
out.clear();
return false;
}
drivers::UdpDriver::~UdpDriver() {
close(fd);
}

64
lib/port/win/poller.cpp Normal file
View File

@@ -0,0 +1,64 @@
#include "port/poller.h"
#include <windows.h>
#include <stdexcept>
bool poller::PollObject::isPollIn() const { return revents != 0; }
bool poller::PollObject::isPollOut() const { return true; } // для Windows драйверов обычно всегда можно писать
bool poller::PollObject::isPollHup() const {
// UART: проверка ClearCommError
if (hCom != INVALID_HANDLE_VALUE) {
DWORD errors;
if (!ClearCommError(hCom, &errors, nullptr)) {
DWORD err = GetLastError();
if (err == ERROR_INVALID_HANDLE || err == ERROR_FILE_NOT_FOUND)
return true; // COM порт физически пропал
}
if (errors & CE_RXOVER) return true; // overflow тоже можно трактовать как проблему
}
// UDP: проверка ошибки сокета через SO_ERROR
if (sock != INVALID_SOCKET) {
int err = 0;
int len = sizeof(err);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0) {
if (err != 0) return true; // есть ошибка на сокете
}
}
return false;
}
poller::PollObject::~PollObject() = default;
poller::PollWrapper::PollWrapper() = default;
void poller::PollWrapper::loop(int timeoutMs) {
// собираем все события
std::vector<HANDLE> handles;
for (auto& obj : objects) {
#ifdef _WIN32
if (!obj) continue;
// UART HANDLE или UDP событие
if (obj->winHandle)
handles.push_back(obj->winHandle);
#endif
}
if (handles.empty()) return;
DWORD waitTime = (timeoutMs < 0) ? INFINITE : static_cast<DWORD>(timeoutMs);
DWORD rc = WaitForMultipleObjects(static_cast<DWORD>(handles.size()),
handles.data(),
FALSE, // ждем любого события
waitTime);
if (rc == WAIT_FAILED)
throw std::runtime_error("WaitForMultipleObjects failed");
// Определяем, какой объект сработал
int idx = rc - WAIT_OBJECT_0;
if (idx >= 0 && idx < static_cast<int>(handles.size())) {
auto& obj = objects[idx];
obj->revents = 1; // простая метка, что событие произошло
}
}
poller::PollWrapper::~PollWrapper() = default;

71
lib/port/win/uart.cpp Normal file
View File

@@ -0,0 +1,71 @@
#include "port/poller.h"
#include "port/uart.h"
#include <Windows.h>
#include <string>
#include <vector>
#include <span>
#include <stdexcept>
drivers::UartDriver::UartDriver(const std::string& path, int baud) {
hCom = CreateFileA(path.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
nullptr);
if (hCom == INVALID_HANDLE_VALUE)
throw std::runtime_error("Cannot open UART port");
// Настройка DCB
DCB dcb{};
dcb.DCBlength = sizeof(dcb);
if (!GetCommState(hCom, &dcb))
throw std::runtime_error("GetCommState failed");
dcb.BaudRate = baud;
dcb.ByteSize = 8;
dcb.Parity = EVENPARITY; // 8E2
dcb.StopBits = TWOSTOPBITS;
if (!SetCommState(hCom, &dcb))
throw std::runtime_error("SetCommState failed");
// Настройка событий
winHandle = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!winHandle) throw std::runtime_error("CreateEvent failed");
if (!SetCommMask(hCom, EV_RXCHAR))
throw std::runtime_error("SetCommMask failed");
// Первый вызов WaitCommEvent в асинхронном режиме
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = winHandle;
DWORD dummy;
WaitCommEvent(hCom, &dummy, &overlapped);
}
bool drivers::UartDriver::writeData(std::span<const uint8_t> data) {
DWORD written;
OVERLAPPED ov{};
ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
bool ok = WriteFile(hCom, data.data(), (DWORD)data.size(), &written, &ov) ||
GetOverlappedResult(hCom, &ov, &written, TRUE);
CloseHandle(ov.hEvent);
return ok && written == data.size();
}
size_t drivers::UartDriver::readChunk(std::vector<uint8_t>& out) {
out.resize(512);
DWORD read = 0;
OVERLAPPED ov{};
ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
bool ok = ReadFile(hCom, out.data(), (DWORD)out.size(), &read, &ov) ||
GetOverlappedResult(hCom, &ov, &read, TRUE);
CloseHandle(ov.hEvent);
out.resize(read);
return read;
}
drivers::UartDriver::~UartDriver() {
if (hCom != INVALID_HANDLE_VALUE) CloseHandle(hCom);
if (winHandle) CloseHandle(winHandle);
}

66
lib/port/win/udp.cpp Normal file
View File

@@ -0,0 +1,66 @@
// File: src/port/win/udp_driver_win.cpp
#include "pollobject.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <string>
#include <vector>
#include <span>
#include <stdexcept>
#pragma comment(lib, "Ws2_32.lib")
namespace drivers {
class UdpDriver : public poller::PollObject {
public:
explicit UdpDriver(uint16_t port) {
WSADATA wsa;
WSAStartup(MAKEWORD(2,2), &wsa);
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) throw std::runtime_error("Cannot create socket");
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
throw std::runtime_error("Bind failed");
winHandle = WSACreateEvent();
if (!winHandle) throw std::runtime_error("WSACreateEvent failed");
WSAEventSelect(sock, winHandle, FD_READ);
}
bool sendTo(std::span<const uint8_t> data, const std::string& addrStr, uint16_t port) {
sockaddr_in dest{};
dest.sin_family = AF_INET;
dest.sin_port = htons(port);
inet_pton(AF_INET, addrStr.c_str(), &dest.sin_addr);
int ret = sendto(sock, (const char*)data.data(), (int)data.size(), 0,
(sockaddr*)&dest, sizeof(dest));
return ret == (int)data.size();
}
bool recvPacket(std::vector<uint8_t>& out) {
out.resize(512);
sockaddr_in src{};
int len = sizeof(src);
int ret = recvfrom(sock, (char*)out.data(), (int)out.size(), MSG_PEEK,
(sockaddr*)&src, &len);
if (ret <= 0) return false; // ничего нет или ошибка
out.resize(ret);
return true;
}
~UdpDriver() override {
if (sock != INVALID_SOCKET) closesocket(sock);
if (winHandle) WSACloseEvent(winHandle);
WSACleanup();
}
};
} // namespace drivers