Compare commits
4 Commits
v1.2
...
e6c9099c80
| Author | SHA1 | Date | |
|---|---|---|---|
| e6c9099c80 | |||
| 05adb18909 | |||
| 0a7db042f6 | |||
| 5bd75b9d5d |
75
air/main.cpp
75
air/main.cpp
@@ -12,18 +12,18 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <sys/time.h>
|
#include <time.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Вспомогательная функция для получения текущего времени в миллисекундах
|
* Вспомогательная функция для получения текущего времени в миллисекундах
|
||||||
*/
|
*/
|
||||||
inline int64_t milliseconds() {
|
inline int64_t milliseconds() {
|
||||||
timeval tv{};
|
timespec ts{};
|
||||||
gettimeofday(&tv,nullptr);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
return ((tv.tv_sec * 1000000l) + tv.tv_usec) / 1000;
|
return static_cast<int64_t>(ts.tv_sec) * 1000 + static_cast<int64_t>(ts.tv_nsec) / 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int64_t SBUS_SEND_FRAME_INTERVAL = 15;
|
constexpr int64_t SBUS_SEND_FRAME_INTERVAL = 50;
|
||||||
constexpr int64_t SBUS_RXLOSS_INTERVAL = 500;
|
constexpr int64_t SBUS_RXLOSS_INTERVAL = 500;
|
||||||
|
|
||||||
class UDPServer {
|
class UDPServer {
|
||||||
@@ -95,8 +95,8 @@ struct SbusData {
|
|||||||
bool failsafe = false;
|
bool failsafe = false;
|
||||||
bool ch17 = false, ch18 = false;
|
bool ch17 = false, ch18 = false;
|
||||||
static constexpr size_t NUM_CH = 18;
|
static constexpr size_t NUM_CH = 18;
|
||||||
static constexpr int16_t SBUS_CH_MIN = 173 + 10;
|
static constexpr int16_t SBUS_CH_MIN = 173 + 20;
|
||||||
static constexpr int16_t SBUS_CH_MAX = 1812 - 10;
|
static constexpr int16_t SBUS_CH_MAX = 1812 - 20;
|
||||||
int16_t ch[NUM_CH];
|
int16_t ch[NUM_CH];
|
||||||
|
|
||||||
/* Message len */
|
/* Message len */
|
||||||
@@ -112,7 +112,6 @@ struct SbusData {
|
|||||||
static constexpr uint8_t FAILSAFE_MASK_ = 0x08;
|
static constexpr uint8_t FAILSAFE_MASK_ = 0x08;
|
||||||
uint8_t binaryBuffer[25];
|
uint8_t binaryBuffer[25];
|
||||||
|
|
||||||
void fillDataBuf() {
|
|
||||||
typedef struct sbusChannels_s {
|
typedef struct sbusChannels_s {
|
||||||
// 176 bits of data (11 bits per channel * 16 channels) = 22 bytes.
|
// 176 bits of data (11 bits per channel * 16 channels) = 22 bytes.
|
||||||
unsigned int chan0 : 11;
|
unsigned int chan0 : 11;
|
||||||
@@ -148,6 +147,7 @@ struct SbusData {
|
|||||||
} __attribute__ ((__packed__));
|
} __attribute__ ((__packed__));
|
||||||
static_assert(sizeof(sbusFrame_s) == sizeof(binaryBuffer));
|
static_assert(sizeof(sbusFrame_s) == sizeof(binaryBuffer));
|
||||||
|
|
||||||
|
void fillDataBuf() {
|
||||||
auto* dest = reinterpret_cast<sbusFrame_s*>(binaryBuffer);
|
auto* dest = reinterpret_cast<sbusFrame_s*>(binaryBuffer);
|
||||||
dest->syncByte = HEADER_;
|
dest->syncByte = HEADER_;
|
||||||
dest->channels.chan0 = std::min(std::max(ch[0], SBUS_CH_MIN), SBUS_CH_MAX);
|
dest->channels.chan0 = std::min(std::max(ch[0], SBUS_CH_MIN), SBUS_CH_MAX);
|
||||||
@@ -169,7 +169,7 @@ struct SbusData {
|
|||||||
|
|
||||||
dest->flags = 0;
|
dest->flags = 0;
|
||||||
if (ch[16] >= (SBUS_CH_MAX + SBUS_CH_MAX) / 2) { dest->flags |= CH17_MASK_; }
|
if (ch[16] >= (SBUS_CH_MAX + SBUS_CH_MAX) / 2) { dest->flags |= CH17_MASK_; }
|
||||||
if (ch[16] >= (SBUS_CH_MAX + SBUS_CH_MAX) / 2) { dest->flags |= CH18_MASK_; }
|
if (ch[17] >= (SBUS_CH_MAX + SBUS_CH_MAX) / 2) { dest->flags |= CH18_MASK_; }
|
||||||
|
|
||||||
dest->endByte = FOOTER_;
|
dest->endByte = FOOTER_;
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,6 @@ struct SbusData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class SerialPort {
|
class SerialPort {
|
||||||
private:
|
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -187,7 +186,7 @@ public:
|
|||||||
// Открытие последовательного порта
|
// Открытие последовательного порта
|
||||||
fd = ::open(port_path.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
|
fd = ::open(port_path.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
std::cerr << "Failed to open serial port " << port_path << ": " << std::strerror(errno) << std::endl;
|
std::cout << "Failed to open serial port " << port_path << ": " << std::strerror(errno) << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
struct termios2 tio{};
|
struct termios2 tio{};
|
||||||
@@ -209,7 +208,7 @@ public:
|
|||||||
tio.c_cflag |= (BOTHER|CREAD|CLOCAL);
|
tio.c_cflag |= (BOTHER|CREAD|CLOCAL);
|
||||||
|
|
||||||
if (ioctl(fd, TCSETS2, &tio) != 0) {
|
if (ioctl(fd, TCSETS2, &tio) != 0) {
|
||||||
std::cerr << "Failed to set termios2 attributes: " << std::strerror(errno) << std::endl;
|
std::cout << "Failed to set termios2 attributes: " << std::strerror(errno) << std::endl;
|
||||||
close(fd);
|
close(fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -234,25 +233,37 @@ public:
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int drain() {
|
||||||
|
int ret;
|
||||||
|
do {
|
||||||
|
ret = ioctl(fd, TCSBRK, 1);
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Метод для записи данных в порт
|
// Метод для записи данных в порт
|
||||||
bool write(std::span<const uint8_t> data) {
|
bool writeBuffer(std::span<const uint8_t> data) {
|
||||||
if (fd < 0) return false;
|
if (fd < 0) return false;
|
||||||
|
|
||||||
ssize_t written = ::write(fd, data.data(), data.size());
|
ssize_t written = write(fd, data.data(), data.size());
|
||||||
if (written < 0) {
|
if (written < 0) {
|
||||||
std::cerr << "Failed to write to serial port" << std::endl;
|
std::cout << "Failed to write to serial port " << strerror(errno) << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Принудительная отправка данных
|
// Принудительная отправка данных
|
||||||
// tcdrain(fd);
|
if (drain() < 0) {
|
||||||
|
std::cout << "Failed to flush data to serial port" << strerror(errno) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
// Парсим аргументы командной строки
|
// Парсим аргументы командной строки
|
||||||
std::string serial_port = "/dev/ttyUSB0";
|
std::string serial_port = "/dev/ttyPS0";
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
serial_port = argv[1];
|
serial_port = argv[1];
|
||||||
}
|
}
|
||||||
@@ -276,21 +287,31 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
int packetCount = 0;
|
int packetCount = 0;
|
||||||
int totalPacketCount = 0;
|
int totalPacketCount = 0;
|
||||||
|
int serialErrors = 0;
|
||||||
int64_t lastStatisticsShow = milliseconds();
|
int64_t lastStatisticsShow = milliseconds();
|
||||||
|
int64_t _lastLoopNow = lastStatisticsShow;
|
||||||
while (true) {
|
while (true) {
|
||||||
pollfd udpFd{.fd = udp_server.sockfd, .events = POLLIN, .revents = 0};
|
pollfd udpFd{.fd = udp_server.sockfd, .events = POLLIN, .revents = 0};
|
||||||
poll(&udpFd, 1, SBUS_SEND_FRAME_INTERVAL / 4);
|
poll(&udpFd, 1, SBUS_SEND_FRAME_INTERVAL / 4);
|
||||||
|
auto now = milliseconds();
|
||||||
|
if (std::abs(now - _lastLoopNow) > SBUS_SEND_FRAME_INTERVAL) {
|
||||||
|
std::cout << "Warning: Loop freeze, reset timers. now time:" << now << std::endl;
|
||||||
|
lastSbusWrite = 0;
|
||||||
|
lastSbusRecv = 0;
|
||||||
|
lastStatisticsShow = 0;
|
||||||
|
}
|
||||||
|
_lastLoopNow = now;
|
||||||
|
|
||||||
if (udpFd.revents & POLLIN) {
|
if (udpFd.revents & POLLIN) {
|
||||||
// Прием UDP пакета
|
// Прием UDP пакета
|
||||||
std::vector<uint16_t> data = udp_server.receive();
|
std::vector<uint16_t> data = udp_server.receive();
|
||||||
lastSbusRecv = milliseconds();
|
lastSbusRecv = now;
|
||||||
|
|
||||||
if (!data.empty()) {
|
if (!data.empty()) {
|
||||||
packetCount++;
|
packetCount++;
|
||||||
|
|
||||||
for (int i = 0; i < data.size() && i < SbusData::NUM_CH; ++i) {
|
for (int i = 0; i < SbusData::NUM_CH; ++i) {
|
||||||
auto item = static_cast<double>(data[i]);
|
auto item = i < data.size() ? static_cast<double>(data[i]) : 1500.0;
|
||||||
item -= 1000.0;
|
item -= 1000.0;
|
||||||
item = std::min(item, 1000.0);
|
item = std::min(item, 1000.0);
|
||||||
item = std::max(item, 0.0);
|
item = std::max(item, 0.0);
|
||||||
@@ -308,10 +329,18 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto now = milliseconds();
|
// std::cout << "from loop: current time " << now << std::endl;
|
||||||
if (now - lastSbusWrite >= SBUS_SEND_FRAME_INTERVAL && now - lastSbusRecv <= SBUS_RXLOSS_INTERVAL) {
|
if (now - lastSbusWrite >= SBUS_SEND_FRAME_INTERVAL && now - lastSbusRecv <= SBUS_RXLOSS_INTERVAL) {
|
||||||
lastSbusWrite = now;
|
lastSbusWrite = now;
|
||||||
if (!serial.write(sb.binaryBuffer)) {
|
if (!serial.writeBuffer(sb.binaryBuffer)) {
|
||||||
|
serialErrors++;
|
||||||
|
} else {
|
||||||
|
serialErrors = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// после 50 ошибок дальше не будем пытаться что-то писать, ибо это бесполезно
|
||||||
|
if (serialErrors >= 50) {
|
||||||
|
std::cout << "FATAL: 50 errors on serial port write operation, exit" << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +354,7 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
std::cout << "Error: " << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user