#include #include #include #include #include #include #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #include // Forward declarations void generateRandomColors(unsigned char& r, unsigned char& g, unsigned char& b); void sendDMXData(HANDLE hSerial, unsigned char* data, int length); void setupImGui(GLFWwindow* window); void renderImGui(); // DMX data buffer const int num_channels = 512; unsigned char buffer[num_channels] = { 0 }; // Function to initialize OpenGL and create a window GLFWwindow* initOpenGL(); // Serial port handle HANDLE hSerial; std::atomic running(true); void dmxThreadFunc() { while (running) { // Send DMX data with a refresh rate of 25ms sendDMXData(hSerial, buffer, num_channels); std::this_thread::sleep_for(std::chrono::milliseconds(25)); } } int main() { // Open the serial port hSerial = CreateFileA("\\\\.\\COM3", GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hSerial == INVALID_HANDLE_VALUE) { std::cerr << "Error opening COM3 port" << std::endl; return 1; } // Configure the serial port DCB dcbSerialParams = { 0 }; dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(hSerial, &dcbSerialParams)) { std::cerr << "Error getting COM3 state" << std::endl; CloseHandle(hSerial); return 1; } dcbSerialParams.BaudRate = 250000; // DMX512 uses 250000 baud dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = TWOSTOPBITS; // DMX512 uses 2 stop bits dcbSerialParams.Parity = NOPARITY; if (!SetCommState(hSerial, &dcbSerialParams)) { std::cerr << "Error setting COM3 state" << std::endl; CloseHandle(hSerial); return 1; } // Set timeouts 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 COM3 timeouts" << std::endl; CloseHandle(hSerial); return 1; } // Initialize OpenGL and create a window GLFWwindow* window = initOpenGL(); if (!window) { CloseHandle(hSerial); return 1; } // Setup ImGui setupImGui(window); // Start the DMX thread std::thread dmxThread(dmxThreadFunc); // Main loop while (!glfwWindowShouldClose(window)) { // Poll events glfwPollEvents(); // Start the ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); // Render ImGui components renderImGui(); // Rendering ImGui::Render(); int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(0.45f, 0.55f, 0.60f, 1.00f); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Swap buffers glfwSwapBuffers(window); } // Stop the DMX thread running = false; dmxThread.join(); // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); CloseHandle(hSerial); return 0; } void generateRandomColors(unsigned char& r, unsigned char& g, unsigned char& b) { r = rand() % 256; g = rand() % 256; b = rand() % 256; } void sendDMXData(HANDLE hSerial, unsigned char* data, int length) { // Start with a DMX break condition EscapeCommFunction(hSerial, SETBREAK); std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Break duration EscapeCommFunction(hSerial, CLRBREAK); std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Mark-after-break duration // Write the start code followed by the DMX data DWORD bytesWritten; unsigned char startCode = 0; WriteFile(hSerial, &startCode, 1, &bytesWritten, NULL); WriteFile(hSerial, data, length, &bytesWritten, NULL); } GLFWwindow* initOpenGL() { // Initialize GLFW if (!glfwInit()) { std::cerr << "Failed to initialize GLFW" << std::endl; return nullptr; } // Create a windowed mode window and its OpenGL context GLFWwindow* window = glfwCreateWindow(1280, 720, "DMX Controller", NULL, NULL); if (!window) { std::cerr << "Failed to create GLFW window" << std::endl; glfwTerminate(); return nullptr; } // Make the window's context current glfwMakeContextCurrent(window); glfwSwapInterval(1); // Enable vsync // Initialize GLEW if (glewInit() != GLEW_OK) { std::cerr << "Failed to initialize GLEW" << std::endl; glfwDestroyWindow(window); glfwTerminate(); return nullptr; } return window; } void setupImGui(GLFWwindow* window) { // Setup ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; // Setup ImGui style ImGui::StyleColorsDark(); // Setup Platform/Renderer bindings ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 130"); } void renderImGui() { // Temporary variables to hold the slider values int channel1 = buffer[0]; int channel2 = buffer[1]; int channel3 = buffer[2]; // Create a window ImGui::Begin("DMX Controller"); // Create sliders for the first three DMX channels ImGui::SliderInt("Channel 1", &channel1, 0, 255); ImGui::SliderInt("Channel 2", &channel2, 0, 255); ImGui::SliderInt("Channel 3", &channel3, 0, 255); // Assign the slider values back to the DMX buffer buffer[0] = static_cast(channel1); buffer[1] = static_cast(channel2); buffer[2] = static_cast(channel3); ImGui::End(); }