Closes #34 Implement MIDI peripherals
Implement device concept
Cleaning project

Reviewed-on: #35
This commit was merged in pull request #35.
This commit is contained in:
2025-12-02 18:02:17 +00:00
parent 932c288a9c
commit 4fbb75ad19
42 changed files with 1638 additions and 1564 deletions

View File

@@ -0,0 +1,7 @@
@REM Compiling DETECTFTDI library
g++ -shared -o ../../../build/bin/libdetectFTDI.dll src/detectFTDI.cpp -fPIC -Wl,--out-implib,../../../build/bin/libdetectFTDI.dll.a -L"lib" -lftd2xx -mwindows
@REM Compiling DMXSENDER library
g++ -shared -o ../../../build/bin/libdmxSender.dll src/dmxSender.cpp -fPIC -Wl,--out-implib,../../../build/bin/libdmxSender.dll.a -L"lib" -lftd2xx -mwindows
@REM g++ -shared -o libdmxSender.so dmxSender.cpp -fPIC -I"include" -L"lib" -lftd2xx -mwindows

View File

@@ -0,0 +1,19 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
char* serialNumber;
char* description;
int isOpen;
} FTDIEndpointC;
int get_endpoints_number();
void get_ftdi_devices(FTDIEndpointC* devices, int count);
void free_ftdi_device(FTDIEndpointC* device);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,30 @@
// Declare the C++ function from the shared library
#include <stdint.h>
typedef enum {
DMX_OK,
DMX_CHANNEL_TOO_LOW_ERROR,
DMX_CHANNEL_TOO_HIGH_ERROR,
DMX_VALUE_TOO_LOW_ERROR,
DMX_VALUE_TOO_HIGH_ERROR,
DMX_OPEN_ERROR,
DMX_SET_BAUDRATE_ERROR,
DMX_SET_DATA_CHARACTERISTICS_ERROR,
DMX_SET_FLOW_ERROR,
DMX_UNKNOWN_ERROR
} DMXError;
typedef void DMXDevice;
extern DMXDevice* dmx_create();
extern void* dmx_destroy(DMXDevice* dev);
extern DMXError dmx_connect(DMXDevice* dev, char* serialNumber);
extern DMXError dmx_activate(DMXDevice* dev);
extern DMXError dmx_deactivate(DMXDevice* dev);
extern DMXError dmx_setValue(DMXDevice* dev, uint16_t channel, uint8_t value);

Binary file not shown.

View File

@@ -0,0 +1,91 @@
#include "../include/detectFTDIBridge.h"
#include "detectFTDI.h"
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
int getFTDIEndpointsNumber() {
DWORD numDevs = 0;
if (FT_CreateDeviceInfoList(&numDevs) != FT_OK) {
std::cerr << "Unable to get FTDI devices: create list error\n";
}
return numDevs;
}
std::vector<FTDIEndpoint> scanFTDIEndpoints() {
DWORD numDevs = 0;
if (FT_CreateDeviceInfoList(&numDevs) != FT_OK) {
std::cerr << "Unable to get FTDI devices: create list error\n";
return {};
}
if (numDevs == 0) {
return {};
}
std::vector<FT_DEVICE_LIST_INFO_NODE> devInfo(numDevs);
if (FT_GetDeviceInfoList(devInfo.data(), &numDevs) != FT_OK) {
std::cerr << "Unable to get FTDI devices: get list error\n";
return {};
}
std::vector<FTDIEndpoint> endpoints;
endpoints.reserve(numDevs);
for (const auto& info : devInfo) {
if (info.SerialNumber[0] != '\0') {
endpoints.push_back({
info.SerialNumber,
info.Description,
static_cast<bool>(info.Flags & FT_FLAGS_OPENED)
});
}
}
return endpoints;
}
extern "C" {
int get_endpoints_number() {
return getFTDIEndpointsNumber();
}
void get_ftdi_devices(FTDIEndpointC* devices, int count) {
if (!devices || count <= 0) {
return;
}
auto list = scanFTDIEndpoints();
int n = std::min(count, static_cast<int>(list.size()));
for (int i = 0; i < n; ++i) {
const auto& src = list[i];
auto& dst = devices[i];
dst.serialNumber = static_cast<char*>(std::malloc(src.serialNumber.size() + 1));
std::strcpy(dst.serialNumber, src.serialNumber.c_str());
dst.description = static_cast<char*>(std::malloc(src.description.size() + 1));
std::strcpy(dst.description, src.description.c_str());
dst.isOpen = src.isOpen ? 1 : 0;
}
}
void free_ftdi_device(FTDIEndpointC* device) {
if (!device) return;
if (device->serialNumber) {
std::free(device->serialNumber);
device->serialNumber = nullptr;
}
if (device->description) {
std::free(device->description);
device->description = nullptr;
}
}
} // extern "C"

View File

@@ -0,0 +1,14 @@
#pragma once
#include "ftd2xx.h"
#include <string>
#include <vector>
struct FTDIEndpoint {
std::string serialNumber;
std::string description;
bool isOpen;
};
int getFTDIEndpointsNumber();
std::vector<FTDIEndpoint> scanFTDIEndpoints();

View File

@@ -0,0 +1,225 @@
//dmxSender.cpp
#include "dmxSender.h"
#include <iostream>
#define DMX_START_CODE 0x00
#define BREAK_DURATION_US 110
#define MAB_DURATION_US 16
#define DMX_CHANNELS 512
#define FREQUENCY 44
#define INTERVAL (1000000 / FREQUENCY)
// Initialize default values for starting the DMX device
DMXDevice::DMXDevice(){
std::cout << " [DMXSENDER] " << "Creating a new DMXDevice..." << std::endl;
ftHandle = nullptr;
isOutputActivated = false;
resetChannels();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << " [DMXSENDER] " << "DMXDevice created!" << std::endl;
}
// Properly close the DMX device
DMXDevice::~DMXDevice(){
std::cout << " [DMXSENDER] " << "Removing the DMXDevice..." << std::endl;
std::cout << " [DMXSENDER] " << "Deactivating the DMXDevice..." << std::endl;
deactivate();
std::cout << " [DMXSENDER] " << "DMXDevice deactivated!" << std::endl;
if (ftHandle != nullptr){
std::cout << " [DMXSENDER] " << "ftHandle not null, closing it..." << std::endl;
FT_Close(ftHandle);
std::cout << " [DMXSENDER] " << "FT_HANDLE closed!" << std::endl;
ftHandle = nullptr;
}
std::cout << " [DMXSENDER] " << "DMXDevice removed!" << std::endl;
}
// Connect the device on a specific port
DMXError DMXDevice::connect(char* serialNumber){
std::cout << " [DMXSENDER] " << "Connecting the DMXDevice..." << std::endl;
ftStatus = FT_OpenEx((PVOID)serialNumber, FT_OPEN_BY_SERIAL_NUMBER, &ftHandle);
if (ftStatus != FT_OK) {
std::cout << " [DMXSENDER] " << "Error when connecting the DMXDevice..." << std::endl;
return DMX_OPEN_ERROR;
}
std::cout << " [DMXSENDER] " << "DMXDevice connected, setting up..." << std::endl;
ftStatus = FT_SetBaudRate(ftHandle, 250000);
if (ftStatus != FT_OK) {
std::cout << " [DMXSENDER] " << "Error when setting the baudrate..." << std::endl;
FT_Close(ftHandle);
return DMX_SET_BAUDRATE_ERROR;
}
ftStatus |= FT_SetDataCharacteristics(ftHandle, 8, FT_STOP_BITS_2, FT_PARITY_NONE); // 8 bits, no parity, 1 stop bit
if (ftStatus != FT_OK) {
std::cout << " [DMXSENDER] " << "Error when setting the data characteristics..." << std::endl;
FT_Close(ftHandle);
return DMX_SET_DATA_CHARACTERISTICS_ERROR;
}
ftStatus |= FT_SetFlowControl(ftHandle, FT_FLOW_NONE, 0, 0);
if (ftStatus != FT_OK) {
std::cout << " [DMXSENDER] " << "Error when trying to set up the flow control..." << std::endl;
FT_Close(ftHandle);
return DMX_SET_FLOW_ERROR;
}
std::cout << " [DMXSENDER] " << "DMXDevice connected!" << std::endl;
return DMX_OK;
}
// Activate the DMX flow
DMXError DMXDevice::activate(){
std::cout << " [DMXSENDER] " << "Activating the DMXDevice..." << std::endl;
isOutputActivated.store(true);
// Send the DMX frames
std::thread updateThread([this]() {
this->sendDMX(ftHandle);
});
updateThread.detach();
std::cout << " [DMXSENDER] " << "DMXDevice activated!" << std::endl;
return DMX_OK;
}
// Deactivate the DMX flow
DMXError DMXDevice::deactivate(){
std::cout << " [DMXSENDER] " << "Deactivating the DMXDevice..." << std::endl;
std::cout << " [DMXSENDER] " << "Resetting channels..." << std::endl;
resetChannels();
std::cout << " [DMXSENDER] " << "Channels resetted!" << std::endl;
isOutputActivated.store(false);
std::cout << " [DMXSENDER] " << "DMXDevice deactivated!" << std::endl;
return DMX_OK;
}
// Set the value of a DMX channel
DMXError DMXDevice::setValue(uint16_t channel, uint8_t value){
std::cout << " [DMXSENDER] " << "Setting a channel value..." << std::endl;
if (channel < 1) {
std::cout << " [DMXSENDER] " << "Unable to set channel value: channel number too low!" << std::endl;
return DMX_CHANNEL_TOO_LOW_ERROR;
}
if (channel > 512) {
std::cout << " [DMXSENDER] " << "Unable to set channel value: channel number too high!" << std::endl;
return DMX_CHANNEL_TOO_HIGH_ERROR;
}
if(value < 0) {
std::cout << " [DMXSENDER] " << "Unable to set channel value: channel value too low!" << std::endl;
return DMX_VALUE_TOO_LOW_ERROR;
}
if(value > 255) {
std::cout << " [DMXSENDER] " << "Unable to set channel value: channel value too high!" << std::endl;
return DMX_VALUE_TOO_HIGH_ERROR;
}
dmxData[channel].store(value);
std::cout << " [DMXSENDER] " << "Channel value set!" << std::endl;
return DMX_OK;
}
// Send a break line
FT_STATUS DMXDevice::sendBreak(FT_HANDLE ftHandle) {
ftStatus = FT_SetBreakOn(ftHandle); // Set BREAK ON
if (ftStatus != FT_OK) {
std::cout << " [DMXSENDER] " << "Unable to put break signal ON!" << std::endl;
return ftStatus;
}
std::this_thread::sleep_for(std::chrono::microseconds(BREAK_DURATION_US));
ftStatus = FT_SetBreakOff(ftHandle); // Set BREAK OFF
if (ftStatus != FT_OK) {
std::cout << " [DMXSENDER] " << "Unable to put break signal OFF!" << std::endl;
return ftStatus;
}
return ftStatus;
}
// Continuously send the DMX frame
void DMXDevice::sendDMX(FT_HANDLE ftHandle) {
while (isOutputActivated) {
// Send the BREAK
ftStatus = sendBreak(ftHandle);
if (ftStatus != FT_OK) {
std::cout << " [DMXSENDER] " << "Unable to send break signal! Deactivating output..." << std::endl;
deactivate();
continue;
}
// Send the MAB
std::this_thread::sleep_for(std::chrono::microseconds(MAB_DURATION_US));
DWORD bytesWritten = 0;
// Send the DMX frame
ftStatus = FT_Write(ftHandle, dmxData, DMX_CHANNELS, &bytesWritten);
if (ftStatus != FT_OK || bytesWritten != DMX_CHANNELS) { // Error detected when trying to send the frame. Deactivate the line.
std::cout << " [DMXSENDER] " << "Error when trying to send the DMX frame! Deactivating output..." << std::endl;
deactivate();
continue;
}
// Wait before sending the next frame
std::this_thread::sleep_for(std::chrono::microseconds(INTERVAL - BREAK_DURATION_US - MAB_DURATION_US));
}
}
// Resetting the DMX channels
void DMXDevice::resetChannels(){
for (auto &v : dmxData) {
v.store(0);
}
dmxData[0].store(DMX_START_CODE);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
// Linkable functions from Golang
extern "C" {
// Create a new DMX device
DMXDevice* dmx_create() {
return new DMXDevice();
}
// Destroy a DMX device
void dmx_destroy(DMXDevice* dev) {
dev->~DMXDevice();
}
// Connect a DMX device
DMXError dmx_connect(DMXDevice* dev, char* serialNumber) {
try{
return dev->connect(serialNumber);
} catch (...) {
return DMX_UNKNOWN_ERROR;
}
}
// Activate a DMX device
DMXError dmx_activate(DMXDevice* dev) {
try{
return dev->activate();
} catch (...) {
return DMX_UNKNOWN_ERROR;
}
}
// Deactivate a DMX device
DMXError dmx_deactivate(DMXDevice* dev) {
try{
return dev->activate();
} catch (...) {
return DMX_UNKNOWN_ERROR;
}
}
// Set the channel value of a DMX device
DMXError dmx_setValue(DMXDevice* dev, int channel, int value) {
try {
return dev->setValue(channel, value);
} catch (...) {
return DMX_UNKNOWN_ERROR;
}
}
}

View File

@@ -0,0 +1,65 @@
// dmxSender.h
#pragma once
#include <vector>
#include <cstdint>
#include <atomic>
#include <thread>
#include "ftd2xx.h"
#define DMX_START_CODE 0x00
#define BREAK_DURATION_US 110
#define MAB_DURATION_US 16
#define DMX_CHANNELS 512
#define FREQUENCY 44
#define INTERVAL (1000000 / FREQUENCY)
typedef enum {
DMX_OK,
DMX_CHANNEL_TOO_LOW_ERROR,
DMX_CHANNEL_TOO_HIGH_ERROR,
DMX_VALUE_TOO_LOW_ERROR,
DMX_VALUE_TOO_HIGH_ERROR,
DMX_OPEN_ERROR,
DMX_SET_BAUDRATE_ERROR,
DMX_SET_DATA_CHARACTERISTICS_ERROR,
DMX_SET_FLOW_ERROR,
DMX_UNKNOWN_ERROR
} DMXError;
class DMXDevice {
public:
// Initialize default values for starting the DMX device
DMXDevice();
// Properly close the DMX device
~DMXDevice();
// Connect the device on a specific port
DMXError connect(char* serialNumber);
// Activate the DMX flow
DMXError activate();
// Deactivate the DMX flow
DMXError deactivate();
// Set the value of a DMX channel
DMXError setValue(uint16_t channel, uint8_t value);
// Resetting the DMX channels
void resetChannels();
private:
FT_STATUS ftStatus; // FTDI endpoint status
FT_HANDLE ftHandle = nullptr; // FTDI object
std::atomic<uint8_t> dmxData[DMX_CHANNELS + 1]; // For storing dynamically the DMX data
std::atomic<bool> isOutputActivated = false; // Boolean to start/stop the DMX flow
// Send a break line
FT_STATUS sendBreak(FT_HANDLE ftHandle);
// Continuously send the DMX frame
void sendDMX(FT_HANDLE ftHandle);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
#include "../src/detectFTDI.h"
#include <iostream>
int main(){
int endpointsNumber = getFTDIEndpointsNumber();
std::vector<FTDIEndpoint> endpoints = scanFTDIEndpoints();
// for (const auto& endpoint : endpoints) {
// std::cout << endpoint.serialNumber << " (" << endpoint.description << ") -> IS OPEN: " << endpoint.isOpen << std::endl;
// }
}

View File

@@ -0,0 +1,122 @@
#include "../src/dmxSender.h"
#include <iostream>
#include <thread>
#include <chrono>
int main(){
std::cout << "Debugging application DMXSENDER" << std::endl;
DMXDevice* dev = nullptr;
try {
dev = new DMXDevice();
}
catch(const std::exception &e){
std::cout << "Unable to create a DMX device: " << e.what() << std::endl;
}
if (!dev) {
std::cout << "Device not created, aborting." << std::endl;
return 1;
}
try {
bool err = dev->connect(0);
if (err == true) {
delete dev;
return 1;
}
}
catch (const std::exception &e){
std::cout << "Unable to connect" << e.what() << std::endl;
delete dev;
return 1;
}
try{
dev->activate();
}
catch(const std::exception &e){
std::cout << "Unable to activate" << e.what() << std::endl;
delete dev;
return 1;
}
try{
dev->setValue(1, 100);
dev->setValue(2, 255);
}
catch(const std::exception &e){
std::cout << "Unable to activate" << e.what() << std::endl;
delete dev;
return 1;
}
Sleep(500);
try{
dev->setValue(2, 127);
dev->setValue(3, 255);
}
catch(const std::exception &e){
std::cout << "Unable to activate" << e.what() << std::endl;
delete dev;
return 1;
}
Sleep(500);
try{
dev->setValue(3, 127);
dev->setValue(4, 255);
}
catch(const std::exception &e){
std::cout << "Unable to activate" << e.what() << std::endl;
delete dev;
return 1;
}
Sleep(500);
try{
dev->setValue(2, 0);
dev->setValue(3, 0);
dev->setValue(4, 0);
dev->setValue(5, 255);
}
catch(const std::exception &e){
std::cout << "Unable to activate" << e.what() << std::endl;
delete dev;
return 1;
}
Sleep(5000);
// try{
// dev->setValue(3, 255);
// }
// catch(const std::exception &e){
// std::cout << "Unable to activate" << e.what() << std::endl;
// delete dev;
// return 1;
// }
// Sleep(5000);
// try{
// dev->setValue(4, 255);
// }
// catch(const std::exception &e){
// std::cout << "Unable to activate" << e.what() << std::endl;
// delete dev;
// return 1;
// }
// Sleep(5000);
delete dev;
return 0;
}