Compare commits
1 Commits
master
...
feat/port-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2445ce5868 |
@ -34,7 +34,7 @@ FetchContent_Declare(
|
|||||||
imgui
|
imgui
|
||||||
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
||||||
GIT_SHALLOW TRUE
|
GIT_SHALLOW TRUE
|
||||||
GIT_TAG v1.91.1
|
GIT_TAG v1.91.0
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(imgui)
|
FetchContent_MakeAvailable(imgui)
|
||||||
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
## Lixda Head Moving Light
|
|
||||||
Specifications:
|
|
||||||
|
|
||||||
- Channel: 9/14
|
|
||||||
- Control mode: DMX512, master/slave, sound active, automatic
|
|
||||||
- Voltage: AC 100-240V/50-60Hz
|
|
||||||
- LED quantity: 7
|
|
||||||
- Power consumption: 7 * 10 W
|
|
||||||
- LED: high power
|
|
||||||
- Mixing color: RGBW mixing color
|
|
||||||
- Pan/tilt: 540° / 270°
|
|
||||||
- Electronic dimming: 0-100%
|
|
||||||
- Plug type: US / EU / AU / UK(optional )
|
|
||||||
- Lifetime: 100,000 hours
|
|
||||||
- Product size: approx. 17.5 * 17 * 24.5cm / 6.88 * 6.69 * 9.64in ( L * W * H )
|
|
||||||
- Product weight: 2763g / 6.09lb
|
|
||||||
- Package size: 28 * 23 * 22cm / 10.9 * 9.05 * 8.66in ( L * W * H )
|
|
||||||
- Package weight: 3356g / 7.4lb
|
|
||||||
[manual](https://images-na.ssl-images-amazon.com/images/I/817xvVgs88L.pdf) (I think)
|
|
86
src/dmx.cpp
86
src/dmx.cpp
@ -2,62 +2,26 @@
|
|||||||
// Created by illyum on 8/15/2024.
|
// Created by illyum on 8/15/2024.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "dmx.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
DMXEngine::DMXEngine(const char* portName, int baudRate, int dataSize)
|
#include "dmx.h"
|
||||||
: running(false), buffer(dataSize, 0) {
|
#include "serial_port_factory.h"
|
||||||
hSerial = CreateFileA(portName, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
#include <iostream>
|
||||||
if (hSerial == INVALID_HANDLE_VALUE) {
|
#include <thread>
|
||||||
std::cerr << "Error opening " << portName << " port" << std::endl;
|
|
||||||
|
DMXEngine::DMXEngine(const char* portName)
|
||||||
|
: running(false), buffer(DMX_CHANNELS, 0) {
|
||||||
|
serialPort = SerialPortFactory::createSerialPort();
|
||||||
|
if (!serialPort->open(portName, DMX_BAUD_RATE)) {
|
||||||
|
std::cerr << "Failed to open serial port" << std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
configurePort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DMXEngine::~DMXEngine() {
|
DMXEngine::~DMXEngine() {
|
||||||
if (running) {
|
if (running) {
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
CloseHandle(hSerial);
|
delete serialPort;
|
||||||
}
|
|
||||||
|
|
||||||
void DMXEngine::configurePort() {
|
|
||||||
DCB dcbSerialParams = { 0 };
|
|
||||||
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
|
||||||
if (!GetCommState(hSerial, &dcbSerialParams)) {
|
|
||||||
std::cerr << "Error getting COM port state" << std::endl;
|
|
||||||
CloseHandle(hSerial);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
dcbSerialParams.BaudRate = 250000;
|
|
||||||
dcbSerialParams.ByteSize = 8;
|
|
||||||
dcbSerialParams.StopBits = TWOSTOPBITS;
|
|
||||||
dcbSerialParams.Parity = NOPARITY;
|
|
||||||
|
|
||||||
if (!SetCommState(hSerial, &dcbSerialParams)) {
|
|
||||||
std::cerr << "Error setting COM port state" << std::endl;
|
|
||||||
CloseHandle(hSerial);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
COMMTIMEOUTS timeouts = { 0 };
|
|
||||||
timeouts.ReadIntervalTimeout = 50;
|
|
||||||
timeouts.ReadTotalTimeoutConstant = 50;
|
|
||||||
timeouts.ReadTotalTimeoutMultiplier = 10;
|
|
||||||
timeouts.WriteTotalTimeoutConstant = 50;
|
|
||||||
timeouts.WriteTotalTimeoutMultiplier = 10;
|
|
||||||
|
|
||||||
if (!SetCommTimeouts(hSerial, &timeouts)) {
|
|
||||||
std::cerr << "Error setting COM port timeouts" << std::endl;
|
|
||||||
CloseHandle(hSerial);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMXEngine::start() {
|
void DMXEngine::start() {
|
||||||
@ -74,26 +38,21 @@ void DMXEngine::stop() {
|
|||||||
DMXEngine::dumpZeros();
|
DMXEngine::dumpZeros();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DMXEngine::setChannelValue(int channel, unsigned char value) {
|
void DMXEngine::setChannelValue(int channel, unsigned char value) {
|
||||||
if (channel >= 0 && channel < buffer.size()) {
|
if (channel >= 0 && channel < DMX_CHANNELS) {
|
||||||
buffer[channel] = value;
|
buffer[channel] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMXEngine::sendDMXData() {
|
void DMXEngine::sendDMXData() {
|
||||||
while (running) {
|
while (running) {
|
||||||
// Break condition
|
serialPort->sendBreak();
|
||||||
EscapeCommFunction(hSerial, SETBREAK);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
EscapeCommFunction(hSerial, CLRBREAK);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // MAB
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // MAB
|
||||||
|
|
||||||
DWORD bytesWritten;
|
std::vector<unsigned char> data = {0}; // Start code
|
||||||
unsigned char startCode = 0;
|
data.insert(data.end(), buffer.begin(), buffer.end());
|
||||||
WriteFile(hSerial, &startCode, 1, &bytesWritten, NULL);
|
|
||||||
WriteFile(hSerial, buffer.data(), buffer.size(), &bytesWritten, NULL);
|
|
||||||
|
|
||||||
|
serialPort->writeData(data);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,17 +60,16 @@ void DMXEngine::sendDMXData() {
|
|||||||
void DMXEngine::dumpZeros() {
|
void DMXEngine::dumpZeros() {
|
||||||
std::fill(buffer.begin(), buffer.end(), 0);
|
std::fill(buffer.begin(), buffer.end(), 0);
|
||||||
|
|
||||||
EscapeCommFunction(hSerial, SETBREAK);
|
serialPort->sendBreak();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
EscapeCommFunction(hSerial, CLRBREAK);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // MAB
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // MAB
|
||||||
|
|
||||||
DWORD bytesWritten;
|
std::vector<unsigned char> data = {0}; // Start code
|
||||||
unsigned char startCode = 0;
|
data.insert(data.end(), buffer.begin(), buffer.end());
|
||||||
WriteFile(hSerial, &startCode, 1, &bytesWritten, NULL);
|
|
||||||
WriteFile(hSerial, buffer.data(), buffer.size(), &bytesWritten, NULL);
|
serialPort->writeData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMXEngine::dmxThreadFunc(DMXEngine* engine) {
|
void DMXEngine::dmxThreadFunc(DMXEngine* engine) {
|
||||||
engine->sendDMXData();
|
engine->sendDMXData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
src/dmx.h
13
src/dmx.h
@ -9,6 +9,9 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include "serial_port.h"
|
||||||
|
|
||||||
|
const int DMX_BAUD_RATE = 250000;
|
||||||
|
|
||||||
struct DMXFixture {
|
struct DMXFixture {
|
||||||
unsigned char r, g, b;
|
unsigned char r, g, b;
|
||||||
@ -17,7 +20,7 @@ struct DMXFixture {
|
|||||||
|
|
||||||
class DMXEngine {
|
class DMXEngine {
|
||||||
public:
|
public:
|
||||||
explicit DMXEngine(const char* portName, int baudRate = 250000, int dataSize = 512);
|
DMXEngine(const char* portName);
|
||||||
~DMXEngine();
|
~DMXEngine();
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
@ -25,15 +28,17 @@ public:
|
|||||||
void setChannelValue(int channel, unsigned char value);
|
void setChannelValue(int channel, unsigned char value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void configurePort();
|
||||||
void sendDMXData();
|
void sendDMXData();
|
||||||
void dumpZeros();
|
void dumpZeros();
|
||||||
void configurePort();
|
|
||||||
static void dmxThreadFunc(DMXEngine* engine);
|
static void dmxThreadFunc(DMXEngine* engine);
|
||||||
|
|
||||||
void* hSerial;
|
SerialPort* serialPort;
|
||||||
std::atomic<bool> running;
|
|
||||||
std::vector<unsigned char> buffer;
|
std::vector<unsigned char> buffer;
|
||||||
std::thread dmxThread;
|
std::thread dmxThread;
|
||||||
|
bool running;
|
||||||
|
|
||||||
|
static const int DMX_CHANNELS = 512;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DMX_H
|
#endif // DMX_H
|
||||||
|
120
src/main.cpp
120
src/main.cpp
@ -5,10 +5,8 @@
|
|||||||
#include "dmx.h"
|
#include "dmx.h"
|
||||||
#include "DmxWriter.h"
|
#include "DmxWriter.h"
|
||||||
#include "BasicLogger.h"
|
#include "BasicLogger.h"
|
||||||
#include "ColorSwatch.h"
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
void DrawImGui();
|
void DrawImGui();
|
||||||
|
|
||||||
@ -31,13 +29,6 @@ int main() {
|
|||||||
Panel propertiesPanel = {{screenWidth * 0.75f, 0, screenWidth * 0.25f, screenHeight}, "Properties", LIGHTGRAY, DARKGRAY, true};
|
Panel propertiesPanel = {{screenWidth * 0.75f, 0, screenWidth * 0.25f, screenHeight}, "Properties", LIGHTGRAY, DARKGRAY, true};
|
||||||
|
|
||||||
std::string randomColors;
|
std::string randomColors;
|
||||||
SavedSwatch savedSwatch = {
|
|
||||||
.swatchName = "",
|
|
||||||
.colors = nullptr,
|
|
||||||
.count = 0
|
|
||||||
};
|
|
||||||
int dmxValues[14] = {0}; // Use an int array for ImGui sliders
|
|
||||||
|
|
||||||
|
|
||||||
while (!WindowShouldClose()) {
|
while (!WindowShouldClose()) {
|
||||||
timelinePanel.Update();
|
timelinePanel.Update();
|
||||||
@ -51,86 +42,24 @@ int main() {
|
|||||||
|
|
||||||
rlImGuiBegin();
|
rlImGuiBegin();
|
||||||
|
|
||||||
ImGui::Begin("DMX Channel Control");
|
ImGui::Begin("Random Color Clicker");
|
||||||
for (int i = 0; i < 14; ++i) {
|
if (ImGui::Button("Random Color!")) {
|
||||||
std::string label = "Channel " + std::to_string(i + 1);
|
unsigned char r = static_cast<unsigned char>(rand() % 256);
|
||||||
if (ImGui::SliderInt(label.c_str(), &dmxValues[i], 0, 255)) {
|
unsigned char g = static_cast<unsigned char>(rand() % 256);
|
||||||
dmxWriter.setChannel(i, static_cast<unsigned char>(dmxValues[i]));
|
unsigned char b = static_cast<unsigned char>(rand() % 256);
|
||||||
}
|
|
||||||
|
dmxWriter.setChannel(0, r);
|
||||||
|
dmxWriter.setChannel(1, g);
|
||||||
|
dmxWriter.setChannel(2, b);
|
||||||
|
|
||||||
|
randomColors = "Sent DMX Color - R: " + std::to_string(r) +
|
||||||
|
", G: " + std::to_string(g) +
|
||||||
|
", B: " + std::to_string(b);
|
||||||
}
|
}
|
||||||
|
ImGui::Text("%s", randomColors.c_str());
|
||||||
|
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
|
||||||
// ImGui::Begin("Random Color Clicker");
|
|
||||||
// if (ImGui::Button("Random Color!")) {
|
|
||||||
// unsigned char r = static_cast<unsigned char>(rand() % 256);
|
|
||||||
// unsigned char g = static_cast<unsigned char>(rand() % 256);
|
|
||||||
// unsigned char b = static_cast<unsigned char>(rand() % 256);
|
|
||||||
//
|
|
||||||
// dmxWriter.setChannel(0, r);
|
|
||||||
// dmxWriter.setChannel(1, g);
|
|
||||||
// dmxWriter.setChannel(2, b);
|
|
||||||
//
|
|
||||||
// randomColors = "Sent DMX Color - R: " + std::to_string(r) +
|
|
||||||
// ", G: " + std::to_string(g) +
|
|
||||||
// ", B: " + std::to_string(b);
|
|
||||||
// }
|
|
||||||
// ImGui::Text("%s", randomColors.c_str());
|
|
||||||
// ImGui::End();
|
|
||||||
//
|
|
||||||
// ImGui::Begin("Swatch Picker");
|
|
||||||
// static ImVec4 color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
//
|
|
||||||
// ImGui::ColorEdit4("Color Picker", (float*)&color);
|
|
||||||
//
|
|
||||||
// // Save button
|
|
||||||
// if (ImGui::Button("Save")) {
|
|
||||||
// SwatchColor* newColors = (SwatchColor*)realloc(savedSwatch.colors, (savedSwatch.count + 1) * sizeof(SwatchColor));
|
|
||||||
// if (newColors) {
|
|
||||||
// savedSwatch.colors = newColors;
|
|
||||||
// savedSwatch.colors[savedSwatch.count] = ImVec4ToSwatchColor(color);
|
|
||||||
// savedSwatch.count++;
|
|
||||||
// } else {
|
|
||||||
// ImGui::Text("Failed to allocate memory for new swatch!");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (savedSwatch.count != 0) {
|
|
||||||
// // Display saved swatches
|
|
||||||
// ImGui::Text("Saved Swatches:");
|
|
||||||
// for (size_t i = 0; i < savedSwatch.count; i++) {
|
|
||||||
// if (i < savedSwatch.count) { // Check to ensure we are within bounds
|
|
||||||
// ImVec4 displayColor = SwatchColorToImVec4(savedSwatch.colors[i]);
|
|
||||||
// ImGui::ColorButton(("##swatch" + std::to_string(i)).c_str(), displayColor, ImGuiColorEditFlags_NoAlpha, ImVec2(50, 50));
|
|
||||||
// if ((i + 1) % 10 != 0) ImGui::SameLine(); // Adjust number of swatches per line
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// // Save swatch to file
|
|
||||||
// if (ImGui::Button("Save to File")) {
|
|
||||||
// if (SaveSwatch("swatch.dat", &savedSwatch) != 0) {
|
|
||||||
// ImGui::Text("Failed to save swatch!");
|
|
||||||
// } else {
|
|
||||||
// ImGui::Text("Swatch saved to swatch.dat");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Load swatch from file
|
|
||||||
// if (ImGui::Button("Load from File")) {
|
|
||||||
// SavedSwatch loadedSwatch = { savedSwatch.swatchName, nullptr, 0 };
|
|
||||||
// if (LoadSwatch("swatch.dat", &loadedSwatch) != 0) {
|
|
||||||
// ImGui::Text("Failed to load swatch!");
|
|
||||||
// } else {
|
|
||||||
// FreeSwatch(&savedSwatch); // Free existing swatch
|
|
||||||
// savedSwatch = loadedSwatch;
|
|
||||||
// ImGui::Text("Swatch loaded from swatch.dat");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ImGui::End();
|
|
||||||
|
|
||||||
rlImGuiEnd();
|
rlImGuiEnd();
|
||||||
|
|
||||||
// Draw FPS on the screen
|
// Draw FPS on the screen
|
||||||
@ -153,5 +82,22 @@ int main() {
|
|||||||
|
|
||||||
void DrawImGui() {
|
void DrawImGui() {
|
||||||
rlImGuiBegin();
|
rlImGuiBegin();
|
||||||
|
|
||||||
|
// Begin a new window with a title
|
||||||
|
ImGui::Begin("My Custom Window");
|
||||||
|
|
||||||
|
// Add a button with a label
|
||||||
|
if (ImGui::Button("Click Me"))
|
||||||
|
{
|
||||||
|
// Action to be performed when the button is clicked
|
||||||
|
ImGui::Text("Button Clicked!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// End the window
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
// bool open = true;
|
||||||
|
// ImGui::ShowDemoWindow(&open);
|
||||||
|
|
||||||
rlImGuiEnd();
|
rlImGuiEnd();
|
||||||
}
|
}
|
||||||
|
22
src/serial_port.h
Normal file
22
src/serial_port.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Created by illyum on 8/16/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SERIAL_PORT_H
|
||||||
|
#define SERIAL_PORT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class SerialPort {
|
||||||
|
public:
|
||||||
|
virtual ~SerialPort() = default;
|
||||||
|
|
||||||
|
virtual bool open(const std::string& portName, int baudRate) = 0;
|
||||||
|
virtual void close() = 0;
|
||||||
|
virtual bool configure(int baudRate, int dataBits, int stopBits, int parity) = 0;
|
||||||
|
virtual bool writeData(const std::vector<unsigned char>& data) = 0;
|
||||||
|
virtual bool sendBreak() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SERIAL_PORT_H
|
25
src/serial_port_factory.h
Normal file
25
src/serial_port_factory.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Created by illyum on 8/16/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SERIAL_PORT_FACTORY_H
|
||||||
|
#define SERIAL_PORT_FACTORY_H
|
||||||
|
|
||||||
|
#include "serial_port.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "serial_port_windows.cpp"
|
||||||
|
typedef WindowsSerialPort PlatformSerialPort;
|
||||||
|
#elif defined(__linux__) || defined(__APPLE__)
|
||||||
|
#include "serial_port_posix.cpp"
|
||||||
|
typedef PosixSerialPort PlatformSerialPort;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SerialPortFactory {
|
||||||
|
public:
|
||||||
|
static SerialPort* createSerialPort() {
|
||||||
|
return new PlatformSerialPort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SERIAL_PORT_FACTORY_H
|
83
src/serial_port_posix.cpp
Normal file
83
src/serial_port_posix.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// Created by illyum on 8/16/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
// I don't have a mac to test it on so... I have zero idea if
|
||||||
|
// this will work, let alone compile
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
#include "serial_port.h"
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class PosixSerialPort : public SerialPort {
|
||||||
|
public:
|
||||||
|
PosixSerialPort() : fd(-1) {}
|
||||||
|
|
||||||
|
~PosixSerialPort() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool open(const std::string& portName, int baudRate) override {
|
||||||
|
fd = ::open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
|
||||||
|
if (fd == -1) {
|
||||||
|
std::cerr << "Error opening " << portName << " port" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return configure(baudRate, CS8, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() override {
|
||||||
|
if (fd != -1) {
|
||||||
|
::close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool configure(int baudRate, int dataBits, int stopBits, int parity) override {
|
||||||
|
struct termios options;
|
||||||
|
if (tcgetattr(fd, &options) < 0) {
|
||||||
|
std::cerr << "Error getting COM port attributes" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfsetispeed(&options, baudRate);
|
||||||
|
cfsetospeed(&options, baudRate);
|
||||||
|
|
||||||
|
options.c_cflag &= ~CSIZE;
|
||||||
|
options.c_cflag |= dataBits;
|
||||||
|
|
||||||
|
options.c_cflag &= ~PARENB;
|
||||||
|
if (parity) options.c_cflag |= PARENB;
|
||||||
|
|
||||||
|
options.c_cflag &= ~CSTOPB;
|
||||||
|
if (stopBits == 2) options.c_cflag |= CSTOPB;
|
||||||
|
|
||||||
|
options.c_cflag |= CLOCAL | CREAD;
|
||||||
|
|
||||||
|
options.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||||
|
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||||
|
options.c_oflag &= ~OPOST;
|
||||||
|
|
||||||
|
options.c_cc[VMIN] = 0;
|
||||||
|
options.c_cc[VTIME] = 5;
|
||||||
|
|
||||||
|
tcflush(fd, TCIFLUSH);
|
||||||
|
return tcsetattr(fd, TCSANOW, &options) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writeData(const std::vector<unsigned char>& data) override {
|
||||||
|
return ::write(fd, data.data(), data.size()) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendBreak() override {
|
||||||
|
return tcsendbreak(fd, 0) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
#endif
|
78
src/serial_port_windows.cpp
Normal file
78
src/serial_port_windows.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//
|
||||||
|
// Created by illyum on 8/16/2024.
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "serial_port.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class WindowsSerialPort : public SerialPort {
|
||||||
|
public:
|
||||||
|
WindowsSerialPort() : hSerial(INVALID_HANDLE_VALUE) {}
|
||||||
|
|
||||||
|
~WindowsSerialPort() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool open(const std::string& portName, int baudRate) override {
|
||||||
|
hSerial = CreateFileA(portName.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if (hSerial == INVALID_HANDLE_VALUE) {
|
||||||
|
std::cerr << "Error opening " << portName << " port" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return configure(baudRate, 8, TWOSTOPBITS, NOPARITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() override {
|
||||||
|
if (hSerial != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(hSerial);
|
||||||
|
hSerial = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool configure(int baudRate, int dataBits, int stopBits, int parity) override {
|
||||||
|
DCB dcbSerialParams = { 0 };
|
||||||
|
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
|
||||||
|
if (!GetCommState(hSerial, &dcbSerialParams)) {
|
||||||
|
std::cerr << "Error getting COM port state" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcbSerialParams.BaudRate = baudRate;
|
||||||
|
dcbSerialParams.ByteSize = dataBits;
|
||||||
|
dcbSerialParams.StopBits = stopBits;
|
||||||
|
dcbSerialParams.Parity = parity;
|
||||||
|
|
||||||
|
if (!SetCommState(hSerial, &dcbSerialParams)) {
|
||||||
|
std::cerr << "Error setting COM port state" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMTIMEOUTS timeouts = { 0 };
|
||||||
|
timeouts.ReadIntervalTimeout = 50;
|
||||||
|
timeouts.ReadTotalTimeoutConstant = 50;
|
||||||
|
timeouts.ReadTotalTimeoutMultiplier = 10;
|
||||||
|
timeouts.WriteTotalTimeoutConstant = 50;
|
||||||
|
timeouts.WriteTotalTimeoutMultiplier = 10;
|
||||||
|
|
||||||
|
return SetCommTimeouts(hSerial, &timeouts);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writeData(const std::vector<unsigned char>& data) override {
|
||||||
|
DWORD bytesWritten;
|
||||||
|
return WriteFile(hSerial, data.data(), data.size(), &bytesWritten, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendBreak() override {
|
||||||
|
EscapeCommFunction(hSerial, SETBREAK);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
EscapeCommFunction(hSerial, CLRBREAK);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // MAB
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
HANDLE hSerial;
|
||||||
|
};
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user