PynectDirect SDK User Manual
SummaryPynectDirect is the Windows dynamic-link library (DLL) software development kit (SDK) for Pynect (Shenzhen Pynect Science and Technology Co., Ltd.) spectrometers. It exposes 58 public functions covering device discovery and opening (multiple devices simultaneously), parameter settings, wavelength and spectrum acquisition, device status reading, Flash and stored configuration, and more.
Version: v3.0.1
Intended for: users doing secondary development with the PynectDirect dynamic library
Applicable scenarios: entry-level students, laboratory researchers, enterprise software developers
Runtime environment: 64-bit Windows 7/10/11, 64-bit Python 3 (if using the Python integration)
Companion files:bin/PynectDirect.dll,bin/ftd2xx.dll,include/pynect_direct.h
Download link: PynectDirect SDK
Table of Contents
- SDK Introduction
- Quick Start
- Basic Concepts
- Common Data Types and Constants
- C Language Example
- C++ Language Example
- C# Language Example
- Python Language Example
- Complete API Reference
- Typical Application Workflows
- FAQ
- Error Codes and Troubleshooting
1. SDK Introduction
PynectDirect is the Windows dynamic-link library (DLL) SDK for Pynect (Shenzhen Pynect Science and Technology Co., Ltd.) fiber spectrometers. It is provided only for our own fiber spectrometers and does not support fiber spectrometers from other manufacturers. To start developing you only need two things:
- Dynamic library:
PynectDirect.dll(the SDK itself) +ftd2xx.dll(the FTDI USB driver, which must be in the same directory as the SDK) - Header file:
pynect_direct.h(the C function declarations, the authoritative source of signatures for all languages)
The SDK communicates directly with the fiber spectrometer over USB and exposes 58 public functions covering: device discovery and opening (multiple devices supported simultaneously), parameter settings (integration time, averaging, xenon lamp, trigger), wavelength and spectrum acquisition, device status reading, Flash and stored configuration, and more.
1.1 Supported Devices
| Device type | Description |
|---|---|
| SPEC-CDS350, SPEC-CMS960 | UV-Vis fiber spectrometer |
| NIR-F950 | Near-infrared fiber spectrometer |
After opening a device, the SDK automatically identifies its device type. The basic acquisition flow is the same for both device types; advanced features such as xenon lamp, external trigger, DAC and stored configuration are supported only on specific devices, so query the device capabilities before calling them (see Section 4.3).
1.2 Language Support
The SDK exports a standard C ABI (extern "C", __cdecl), so any language that supports the C ABI can call it directly:
| Language | Integration method | Example location |
|---|---|---|
| C | Link lib/libPynectDirect.dll.a (or the static library) |
Chapter 5 |
| C++ | Include pynect_direct.h directly (automatic extern "C") |
Chapter 6 |
| C# | DllImport("PynectDirect.dll") P/Invoke |
Chapter 7 |
| Python | Load the DLL with ctypes |
Chapter 8 |
| Other | Any language supporting the C ABI (Rust/Go/Delphi, etc.) | Declare per the header signatures |
1.3 Data Flow Model (Important)
- The device transmits all pixels every time: a single read returns the wavelength or intensity of all pixels (e.g., 2048 points).
- The wavelength range comes from the full data:
pynect_direct_get_full_wavelength_rangereturns the first and last points of the complete wavelength array (i.e., the min/max wavelength over all pixels); this value is always correct. - A selected band is cropped on the host:
pynect_direct_set_selected_wavelength_rangeonly records a pixel window on the host; theget_selected_*interfaces crop the window content from the full data and return it, and do not modify the device's built-in output range.
Therefore, switching the display band only needs to be done on the host, without interacting with the device; it is fast and does not affect other programs' access to the device.
2. Quick Start
2.1 File Layout
It is recommended to organize the SDK files into the following structure (Python shown as an example; other languages follow the same idea):
MyProject/
|-- your code files (xxx.py / xxx.c / xxx.cpp / xxx.cs / ...)
`-- dll/
|-- PynectDirect.dll
`-- ftd2xx.dll
PynectDirect.dll and ftd2xx.dll must be placed in the same folder.
- C/C++: add the
includedirectory to the header search path; at runtime the DLL is in the program directory or on the system PATH. - C#: put the DLL in the program output directory (next to the exe), or use an absolute path.
- Python: in the script, use
os.add_dll_directory()to add thedllfolder to the search path before loading withctypes.CDLL.
2.2 Environment Requirements
| Item | Requirement |
|---|---|
| Operating system | 64-bit Windows |
| Language runtime | all 64-bit (a 32-bit process cannot load a 64-bit DLL) |
| C/C++ compiler | MinGW-w64 GCC or MSVC (64-bit) |
| C# | .NET Framework 4.x or .NET 6+ (x64 target platform) |
| Python | 64-bit Python 3 |
2.3 Verify the Runtime Environment
Quick Python verification (no device required):
python -c "import ctypes; d=ctypes.CDLL(r'dll\PynectDirect.dll'); print('load ok, api =', d.pynect_direct_scan_devices())"
If it prints load ok, api = 0 or a device count, the DLL loaded successfully.
Full hardware verification (device required):
python .\examples\python\minimal_test.py --read-only
You should see FAIL=0. Read-only mode does not modify any device parameters.
2.4 Connect the Device
- Connect the fiber spectrometer to the computer via USB.
- Wait for Windows to finish installing the driver (the FTDI driver must already be installed).
- Close any other software currently using the spectrometer (the device is accessed exclusively).
- Run your own program.
3. Basic Concepts
3.1 Calling Sequence
The calling sequence is basically the same in all programs:
1. pynect_direct_scan_devices() scan devices, confirm presence
2. (optional) get_chip_serial_by_index() view each device's chip serial number
3. pynect_direct_open_device(0) open the device (or open by serial number)
4. get_device_info() / get_capabilities() read device info and capabilities
5. get_wavelength_count() get the data point count N
6. get_full_wavelength_data() get wavelength coordinates (double array, N elements)
7. get_full_spectrum_data() acquire intensity (uint16 array, N elements)
8. (optional) set_selected_wavelength_range() set the display band, then get_selected_*
9. pynect_direct_close_device() close the device
3.2 Return Value Convention
- Most functions return
0on success and a negative error code on failure (see Chapter 12). - Exceptions:
pynect_direct_scan_devices: returns the device count;0means no device (not an error).pynect_direct_flash_read/pynect_direct_external_spectrum_control: return the number of bytes actually read on success.pynect_direct_is_open: returns1(open) /0(closed).pynect_direct_close_device: no return value (void).
3.3 Array Capacity Convention
- Wavelength array element type: double (8 bytes)
- Intensity array element type: uint16 (2 bytes)
- Array length: call
pynect_direct_get_wavelength_countto get N, then allocate arrays of size N and pass N asmax_len. - String buffer: allocate 64 bytes recommended;
max_lenincludes space for the trailing\0.
3.4 Session Rules
- Opening multiple devices simultaneously is supported (up to
PYNECT_DIRECT_MAX_DEVICES= 4 devices).pynect_direct_open_devicedoes not close already-open devices when opening a new one. - The SDK uses a "current device" model: all parameter and data APIs operate on the currently selected device.
pynect_direct_open_device(0)→ opens device 0 and makes it the current devicepynect_direct_open_device(1)→ opens device 1 and makes it the current device (device 0 stays open)pynect_direct_select_device(0)→ switches the current device to device 0pynect_direct_get_open_device_count()→ the number of currently open devicespynect_direct_close_device()→ closes the currently selected devicepynect_direct_close_all_devices()→ closes all devices- Re-opening the same device (same chip serial number) does not create a duplicate handle; it only switches the current device to it.
- Each device has independent state: protocol, capabilities, parameters (integration time, averaging), and the selected wavelength window do not affect each other.
- Current-session parameters (integration time, averaging, etc.) are not retained after close/reset/power-cycle.
- Stored configuration (the
stored_*series) is the device's non-volatile parameter: once written it survives power loss and affects future power-on defaults; use with care. - After a device reset (
reset_device), the selected wavelength window already set on that device becomes invalid and must be set again.
4. Common Data Types and Constants
4.1 Error Codes
| Macro | Value | Meaning |
|---|---|---|
PYNECT_DIRECT_SUCCESS |
0 | Success |
PYNECT_DIRECT_ERR_NOT_INITIALIZED |
-1 | Not initialized (e.g., reading before setting the selected band) |
PYNECT_DIRECT_ERR_DEVICE_NOT_FOUND |
-2 | No device found or index out of range |
PYNECT_DIRECT_ERR_DEVICE_NOT_OPEN |
-3 | Device not open |
PYNECT_DIRECT_ERR_COMM_FAIL |
-4 | Communication failure |
PYNECT_DIRECT_ERR_INVALID_PARAM |
-5 | Invalid parameter |
PYNECT_DIRECT_ERR_OPEN_FAILED |
-6 | Failed to open device (occupied / driver abnormal) |
PYNECT_DIRECT_ERR_CRC |
-7 | Response CRC check failed |
PYNECT_DIRECT_ERR_PREAMBLE |
-8 | Response frame header mismatch |
PYNECT_DIRECT_ERR_TIMEOUT |
-9 | Device response timeout |
PYNECT_DIRECT_ERR_WPROT |
-10 | Write-protect error |
PYNECT_DIRECT_ERR_UNSUPPORTED |
-11 | The current device does not support this feature |
4.2 Protocol Enum
typedef enum PynectDirectDeviceProtocol {
PYNECT_DIRECT_PROTOCOL_AUTO = 0, // auto-detect (default, recommended)
PYNECT_DIRECT_PROTOCOL_UVVIS_CM2 = 1, // UV/VIS
PYNECT_DIRECT_PROTOCOL_NIR_LEGACY = 2, // NIR
PYNECT_DIRECT_PROTOCOL_UNKNOWN = 255
} PynectDirectDeviceProtocol;
4.3 Capability Bits (capabilities)
pynect_direct_get_capabilities returns a 32-bit mask indicating which features the device supports:
| Bit | Macro | Feature |
|---|---|---|
| 0x00000001 | PYNECT_DIRECT_CAP_INTEGRATION_TIME |
Integration time |
| 0x00000002 | PYNECT_DIRECT_CAP_AVERAGE_NUMBER |
Averaging |
| 0x00000004 | PYNECT_DIRECT_CAP_LEVEL_OUTPUT |
Level output |
| 0x00000008 | PYNECT_DIRECT_CAP_FLASH |
Flash read/write |
| 0x00000010 | PYNECT_DIRECT_CAP_WAVELENGTH_DATA |
Wavelength data |
| 0x00000020 | PYNECT_DIRECT_CAP_SPECTRUM_DATA |
Spectrum data |
| 0x00000040 | PYNECT_DIRECT_CAP_TEMPERATURE |
Temperature |
| 0x00000080 | PYNECT_DIRECT_CAP_HARDWARE_VERSION |
Hardware version |
| 0x00000100 | PYNECT_DIRECT_CAP_CALIBRATION |
Calibration coefficients |
| 0x00000200 | PYNECT_DIRECT_CAP_XENON |
Xenon lamp |
| 0x00000400 | PYNECT_DIRECT_CAP_USB_STATUS |
USB status |
| 0x00000800 | PYNECT_DIRECT_CAP_EXTERNAL_TRIGGER |
External trigger |
| 0x00001000 | PYNECT_DIRECT_CAP_DAC |
DAC |
| 0x00002000 | PYNECT_DIRECT_CAP_STORED_CONFIG |
Stored configuration |
| 0x00004000 | PYNECT_DIRECT_CAP_SLIT_WIDTH |
Slit width |
4.4 Common Constants
// Xenon lamp modes
#define XENON_OFF 0x00 // off
#define XENON_CONTINUOUS 0x01 // continuous
#define XENON_SINGLE 0x81 // single
// External trigger types
#define TRIGGER_OFF 0x00 // off
#define TRIGGER_ON_RISE 0xAA // rising-edge trigger
#define TRIGGER_ON_LEVEL 0xBB // level trigger
4.5 Data Structures
Device info:
typedef struct PynectDirectDeviceInfo {
PynectDirectDeviceProtocol protocol; // actual protocol type
uint32_t capabilities; // capability bits
char protocol_name[32]; // protocol name string
char model[32]; // model string
char serial_number[32]; // business serial number string
char hardware_version[64]; // hardware version string
} PynectDirectDeviceInfo;
Selected wavelength range info (returned by get_selected_wavelength_range):
typedef struct PynectDirectSelectedWavelengthRange {
double request_start_nm; // requested start wavelength (nm)
double request_end_nm; // requested end wavelength (nm)
double actual_start_nm; // actual cropped start wavelength (the real wavelength of the corresponding pixel)
double actual_end_nm; // actual cropped end wavelength
uint16_t start_pixel; // start pixel index (0-based)
uint16_t end_pixel; // end pixel index
uint16_t count; // number of pixels in the window
} PynectDirectSelectedWavelengthRange;
5. C Language Example
5.1 Build and Run
# enter the example directory (the required DLL, header and library are already in it)
cd examples/c
# Method 1: run directly (pre-compiled, no development environment required)
spectrum_demo.exe
# Method 2: build it yourself (MinGW-w64, 64-bit)
gcc spectrum_demo.c -Iinclude -Llib -lPynectDirect -o spectrum_demo.exe
After the program finishes it pauses and shows "Press any key to continue"; press any key to close the window and review the results (in the source this is
system("pause")).With MSVC:
cl spectrum_demo.c /Iinclude /link /LIBPATH:lib(requires a.libimport library, which can be obtained from the vendor; GCC environments uselibPynectDirect.dll.a).The full source is in the delivery package at
examples/c/spectrum_demo.c(i.e., the content of Section 5.2).
5.2 Full Source (spectrum_demo.c)
#define PYNECT_DIRECT_USE_DLL /* define when using the dynamic library */
#include "pynect_direct.h"
#include <stdio.h>
#include <stdlib.h>
static int require(int code, const char* what)
{
if (code != PYNECT_DIRECT_SUCCESS) {
printf("[FAIL] %s: return code = %d\n", what, code);
return 0;
}
return 1;
}
int main(void)
{
PynectDirectDeviceInfo info;
PynectDirectSelectedWavelengthRange sel;
uint16_t count = 0, i;
double* wavelengths = NULL;
uint16_t* spectrum = NULL;
double start_nm = 0.0, end_nm = 0.0;
unsigned int integration = 0;
int n;
/* 1. scan devices */
n = pynect_direct_scan_devices();
if (n <= 0) {
printf("No device found.\n");
return 1;
}
printf("Found %d device(s).\n", n);
/* 2. open the device (index 0) */
if (!require(pynect_direct_open_device(0), "open_device")) goto done;
/* 3. read device info */
if (require(pynect_direct_get_device_info(&info), "get_device_info")) {
printf("Protocol: %s\n", info.protocol_name);
printf("Model: %s\n", info.model);
printf("Serial: %s\n", info.serial_number);
printf("Hardware: %s\n", info.hardware_version);
}
/* 4. data point count */
if (!require(pynect_direct_get_wavelength_count(&count), "get_wavelength_count")) goto done;
printf("Wavelength count: %u\n", count);
if (count == 0) goto done;
/* 5. wavelength range (first and last of the full wavelength array, always correct) */
if (!require(pynect_direct_get_full_wavelength_range(&start_nm, &end_nm), "get_full_wavelength_range")) goto done;
printf("Wavelength range: %.2f ~ %.2f nm\n", start_nm, end_nm);
/* 6. allocate arrays */
wavelengths = (double*)malloc(count * sizeof(double));
spectrum = (uint16_t*)malloc(count * sizeof(uint16_t));
if (!wavelengths || !spectrum) {
printf("Out of memory.\n");
goto done;
}
/* 7. read wavelength coordinates */
if (!require(pynect_direct_get_full_wavelength_data(wavelengths, count), "get_full_wavelength_data")) goto done;
/* 8. set and read the integration time (temporary, not written to stored config) */
if (require(pynect_direct_get_integration_time(&integration), "get_integration_time"))
printf("Current integration time: %u us\n", integration);
pynect_direct_set_integration_time(50000); /* 50 ms */
/* 9. acquire the spectrum */
if (!require(pynect_direct_get_full_spectrum_data(spectrum, count), "get_full_spectrum_data")) goto done;
/* 10. two-column print: wavelength + corresponding intensity (first 5 + last 5 points) */
printf("%6s %13s %9s\n", "Index", "Wavelength(nm)", "Intensity");
printf("%6s %13s %9s\n", "-----", "--------------", "---------");
for (i = 0; i < 5 && i < count; i++)
printf("%6u %13.3f %9u\n", i, wavelengths[i], spectrum[i]);
if (count > 10) printf("... (total %u points)\n", count);
for (i = count > 5 ? count - 5 : 0; i < count; i++)
printf("%6u %13.3f %9u\n", i, wavelengths[i], spectrum[i]);
/* 11. selected band (host-side cropping, does not modify the device) */
if (start_nm < 400.0 && end_nm > 800.0) {
if (require(pynect_direct_set_selected_wavelength_range(400.0, 800.0), "set_selected_range")) {
if (require(pynect_direct_get_selected_wavelength_range(&sel), "get_selected_range")) {
printf("Selected: pixels %u..%u, count=%u, actual %.2f..%.2f nm\n",
sel.start_pixel, sel.end_pixel, sel.count,
sel.actual_start_nm, sel.actual_end_nm);
}
}
} else {
printf("Device does not cover 400-800 nm, skip selected range.\n");
}
/* 12. close the device */
done:
free(wavelengths);
free(spectrum);
pynect_direct_close_device();
printf("Done.\n");
return 0;
}
#define PYNECT_DIRECT_USE_DLL /* define when using the dynamic library */
#include "pynect_direct.h"
#include <stdio.h>
#include <stdlib.h>
static int require(int code, const char* what)
{
if (code != PYNECT_DIRECT_SUCCESS) {
printf("[FAIL] %s: return code = %d\n", what, code);
return 0;
}
return 1;
}
int main(void)
{
PynectDirectDeviceInfo info;
PynectDirectSelectedWavelengthRange sel;
uint16_t count = 0, i;
double* wavelengths = NULL;
uint16_t* spectrum = NULL;
double start_nm = 0.0, end_nm = 0.0;
unsigned int integration = 0;
int n;
/* 1. scan devices */
n = pynect_direct_scan_devices();
if (n <= 0) {
printf("No device found.\n");
system("pause");
return 1;
}
printf("Found %d device(s).\n", n);
/* 2. open the device (index 0) */
if (!require(pynect_direct_open_device(0), "open_device")) goto done;
/* 3. read device info */
if (require(pynect_direct_get_device_info(&info), "get_device_info")) {
printf("Protocol: %s\n", info.protocol_name);
printf("Model: %s\n", info.model);
printf("Serial: %s\n", info.serial_number);
printf("Hardware: %s\n", info.hardware_version);
}
/* 4. data point count */
if (!require(pynect_direct_get_wavelength_count(&count), "get_wavelength_count")) goto done;
printf("Wavelength count: %u\n", count);
if (count == 0) goto done;
/* 5. wavelength range (first and last of the full wavelength array, always correct) */
if (!require(pynect_direct_get_full_wavelength_range(&start_nm, &end_nm), "get_full_wavelength_range")) goto done;
printf("Wavelength range: %.2f ~ %.2f nm\n", start_nm, end_nm);
/* 6. allocate arrays */
wavelengths = (double*)malloc(count * sizeof(double));
spectrum = (uint16_t*)malloc(count * sizeof(uint16_t));
if (!wavelengths || !spectrum) {
printf("Out of memory.\n");
goto done;
}
/* 7. read wavelength coordinates */
if (!require(pynect_direct_get_full_wavelength_data(wavelengths, count), "get_full_wavelength_data")) goto done;
/* 8. set and read the integration time (temporary, not written to stored config) */
if (require(pynect_direct_get_integration_time(&integration), "get_integration_time"))
printf("Current integration time: %u us\n", integration);
pynect_direct_set_integration_time(50000); /* 50 ms */
/* 9. acquire the spectrum */
if (!require(pynect_direct_get_full_spectrum_data(spectrum, count), "get_full_spectrum_data")) goto done;
/* 10. two-column print: wavelength + corresponding intensity (first 5 + last 5 points) */
printf("%6s %13s %9s\n", "Index", "Wavelength(nm)", "Intensity");
printf("%6s %13s %9s\n", "-----", "--------------", "---------");
for (i = 0; i < 5 && i < count; i++)
printf("%6u %13.3f %9u\n", i, wavelengths[i], spectrum[i]);
if (count > 10) printf("... (total %u points)\n", count);
for (i = count > 5 ? count - 5 : 0; i < count; i++)
printf("%6u %13.3f %9u\n", i, wavelengths[i], spectrum[i]);
/* 11. selected band (host-side cropping, does not modify the device) */
if (start_nm < 400.0 && end_nm > 800.0) {
if (require(pynect_direct_set_selected_wavelength_range(400.0, 800.0), "set_selected_range")) {
if (require(pynect_direct_get_selected_wavelength_range(&sel), "get_selected_range")) {
printf("Selected: pixels %u..%u, count=%u, actual %.2f..%.2f nm\n",
sel.start_pixel, sel.end_pixel, sel.count,
sel.actual_start_nm, sel.actual_end_nm);
}
}
} else {
printf("Device does not cover 400-800 nm, skip selected range.\n");
}
/* 12. close the device */
done:
free(wavelengths);
free(spectrum);
pynect_direct_close_device();
printf("Done.\n");
system("pause");
return 0;
}
5.3 Output Example
Found 1 device(s).
Protocol: UVVIS_CM2
Model: PYNECT-UV-2048
Serial: SP123456
Hardware: 1.0
Wavelength count: 2048
Wavelength range: 199.04 ~ 1019.50 nm
Current integration time: 10000 us
Index Wavelength(nm) Intensity
----- -------------- ---------
0 199.04 128
1 200.08 130
2 201.12 127
3 202.16 125
4 203.20 131
... (total 2048 points)
2043 1018.46 122
2044 1019.50 121
2045 1020.54 119
2046 1021.58 124
2047 1022.62 126
Selected: pixels 808..1616, count=809, actual 400.00..800.00 nm
Done.
Press any key to continue. . .
6. C++ Language Example
6.1 Build and Run
cd examples/cpp
# Method 1: run directly (pre-compiled)
spectrum_demo.exe
# Method 2: build it yourself
g++ spectrum_demo.cpp -Iinclude -Llib -lPynectDirect -o spectrum_demo.exe
After the program finishes it pauses and shows "Press any key to continue"; press any key to close the window (in the source this is
system("pause")).
The header already contains extern "C", so C++ can simply #include "pynect_direct.h".
The full source is in the delivery package at examples/cpp/spectrum_demo.cpp (i.e., the content of Section 6.2).
6.2 Full Source (spectrum_demo.cpp)
#define PYNECT_DIRECT_USE_DLL
#include "pynect_direct.h"
#include <iomanip>
#include <iostream>
#include <vector>
#include <string>
static void require(int code, const std::string& what)
{
if (code != PYNECT_DIRECT_SUCCESS) {
std::cerr << "[FAIL] " << what << ": return code = " << code << std::endl;
throw std::runtime_error(what);
}
}
int main()
{
try {
/* 1. scan devices */
int n = pynect_direct_scan_devices();
if (n <= 0) {
std::cout << "No device found." << std::endl;
return 1;
}
std::cout << "Found " << n << " device(s)." << std::endl;
/* 2. open the device */
require(pynect_direct_open_device(0), "open_device");
/* 3. device info */
PynectDirectDeviceInfo info{};
require(pynect_direct_get_device_info(&info), "get_device_info");
std::cout << "Model: " << info.model << std::endl
<< "Serial: " << info.serial_number << std::endl
<< "Hardware:" << info.hardware_version << std::endl;
/* 4. data point count */
uint16_t count = 0;
require(pynect_direct_get_wavelength_count(&count), "get_wavelength_count");
std::cout << "Wavelength count: " << count << std::endl;
/* 5. wavelength range */
double start_nm = 0.0, end_nm = 0.0;
require(pynect_direct_get_full_wavelength_range(&start_nm, &end_nm),
"get_full_wavelength_range");
std::cout << "Wavelength range: " << start_nm << " ~ " << end_nm << " nm" << std::endl;
/* 6. read wavelength coordinates */
std::vector<double> wavelengths(count);
require(pynect_direct_get_full_wavelength_data(wavelengths.data(), count),
"get_full_wavelength_data");
/* 7. set the integration time and acquire the spectrum */
pynect_direct_set_integration_time(50000); /* 50 ms, temporary setting */
std::vector<uint16_t> spectrum(count);
require(pynect_direct_get_full_spectrum_data(spectrum.data(), count),
"get_full_spectrum_data");
/* 8. two-column print: wavelength + corresponding intensity (first 5 + last 5 points) */
std::cout << std::setw(6) << "Index" << std::setw(15) << "Wavelength(nm)"
<< std::setw(11) << "Intensity" << std::endl;
std::cout << std::setw(6) << "-----" << std::setw(15) << "--------------"
<< std::setw(11) << "---------" << std::endl;
for (int i = 0; i < 5; ++i)
std::cout << std::setw(6) << i << std::setw(15) << std::fixed
<< std::setprecision(3) << wavelengths[i]
<< std::setw(11) << spectrum[i] << std::endl;
if (count > 10)
std::cout << "... (total " << count << " points)" << std::endl;
for (int i = count - 5; i < count; ++i)
std::cout << std::setw(6) << i << std::setw(15) << std::fixed
<< std::setprecision(3) << wavelengths[i]
<< std::setw(11) << spectrum[i] << std::endl;
/* 9. selected band (host-side cropping) */
if (start_nm < 400.0 && end_nm > 800.0) {
require(pynect_direct_set_selected_wavelength_range(400.0, 800.0),
"set_selected_range");
PynectDirectSelectedWavelengthRange sel{};
require(pynect_direct_get_selected_wavelength_range(&sel),
"get_selected_range");
std::cout << "Selected: pixels " << sel.start_pixel << ".."
<< sel.end_pixel << ", count=" << sel.count << std::endl;
}
/* 10. close the device */
pynect_direct_close_device();
std::cout << "Done." << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
#define PYNECT_DIRECT_USE_DLL
#include "pynect_direct.h"
#include <iomanip>
#include <iostream>
#include <vector>
#include <string>
static void require(int code, const std::string& what)
{
if (code != PYNECT_DIRECT_SUCCESS) {
std::cerr << "[FAIL] " << what << ": return code = " << code << std::endl;
throw std::runtime_error(what);
}
}
int main()
{
try {
/* 1. scan devices */
int n = pynect_direct_scan_devices();
if (n <= 0) {
std::cout << "No device found." << std::endl;
system("pause");
return 1;
}
std::cout << "Found " << n << " device(s)." << std::endl;
/* 2. open the device */
require(pynect_direct_open_device(0), "open_device");
/* 3. device info */
PynectDirectDeviceInfo info{};
require(pynect_direct_get_device_info(&info), "get_device_info");
std::cout << "Model: " << info.model << std::endl
<< "Serial: " << info.serial_number << std::endl
<< "Hardware:" << info.hardware_version << std::endl;
/* 4. data point count */
uint16_t count = 0;
require(pynect_direct_get_wavelength_count(&count), "get_wavelength_count");
std::cout << "Wavelength count: " << count << std::endl;
/* 5. wavelength range */
double start_nm = 0.0, end_nm = 0.0;
require(pynect_direct_get_full_wavelength_range(&start_nm, &end_nm),
"get_full_wavelength_range");
std::cout << "Wavelength range: " << start_nm << " ~ " << end_nm << " nm" << std::endl;
/* 6. read wavelength coordinates */
std::vector<double> wavelengths(count);
require(pynect_direct_get_full_wavelength_data(wavelengths.data(), count),
"get_full_wavelength_data");
/* 7. set the integration time and acquire the spectrum */
pynect_direct_set_integration_time(50000); /* 50 ms, temporary setting */
std::vector<uint16_t> spectrum(count);
require(pynect_direct_get_full_spectrum_data(spectrum.data(), count),
"get_full_spectrum_data");
/* 8. two-column print: wavelength + corresponding intensity (first 5 + last 5 points) */
std::cout << std::setw(6) << "Index" << std::setw(15) << "Wavelength(nm)"
<< std::setw(11) << "Intensity" << std::endl;
std::cout << std::setw(6) << "-----" << std::setw(15) << "--------------"
<< std::setw(11) << "---------" << std::endl;
for (int i = 0; i < 5; ++i)
std::cout << std::setw(6) << i << std::setw(15) << std::fixed
<< std::setprecision(3) << wavelengths[i]
<< std::setw(11) << spectrum[i] << std::endl;
if (count > 10)
std::cout << "... (total " << count << " points)" << std::endl;
for (int i = count - 5; i < count; ++i)
std::cout << std::setw(6) << i << std::setw(15) << std::fixed
<< std::setprecision(3) << wavelengths[i]
<< std::setw(11) << spectrum[i] << std::endl;
/* 9. selected band (host-side cropping) */
if (start_nm < 400.0 && end_nm > 800.0) {
require(pynect_direct_set_selected_wavelength_range(400.0, 800.0),
"set_selected_range");
PynectDirectSelectedWavelengthRange sel{};
require(pynect_direct_get_selected_wavelength_range(&sel),
"get_selected_range");
std::cout << "Selected: pixels " << sel.start_pixel << ".."
<< sel.end_pixel << ", count=" << sel.count << std::endl;
}
/* 10. close the device */
pynect_direct_close_device();
std::cout << "Done." << std::endl;
system("pause");
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
system("pause");
return 1;
}
return 0;
}
7. C# Language Example
7.1 Build and Run
cd examples/csharp
# Method 1: run directly (pre-compiled, .NET Framework 4.x, runtime built into Windows)
spectrum_demo.exe
# Method 2: build it yourself (.NET Framework built-in compiler, 64-bit)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /platform:x64 spectrum_demo.cs
# Method 3: .NET SDK
dotnet build -c Release
After the program finishes it shows "Press any key to exit..."; press any key to close the window (in the source this is
WaitExit()).The full source is in the delivery package at
examples/csharp/spectrum_demo.cs(i.e., the content of Section 7.2). The example uses C# 5-compatible syntax and compiles under both .NET Framework 4.x and .NET 6+.
7.2 Full Source (spectrum_demo.cs)
using System;
using System.Runtime.InteropServices;
using System.Text;
class PynectDirectDemo
{
// ---------- Data structures (matching pynect_direct.h) ----------
[StructLayout(LayoutKind.Sequential)]
struct PynectDirectDeviceInfo
{
public int protocol;
public uint capabilities;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string protocol_name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string model;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string serial_number;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string hardware_version;
}
[StructLayout(LayoutKind.Sequential)]
struct PynectDirectSelectedWavelengthRange
{
public double request_start_nm;
public double request_end_nm;
public double actual_start_nm;
public double actual_end_nm;
public ushort start_pixel;
public ushort end_pixel;
public ushort count;
}
// ---------- DllImport (all use the cdecl calling convention) ----------
const string DLL = "PynectDirect.dll";
const int SUCCESS = 0;
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_scan_devices();
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_open_device(int index);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern void pynect_direct_close_device();
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_device_info(ref PynectDirectDeviceInfo info);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_wavelength_count(ref ushort count);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_full_wavelength_range(ref double start, ref double end);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_full_wavelength_data(double[] buffer, int max_len);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_full_spectrum_data(ushort[] buffer, int max_len);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_set_integration_time(uint us);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_integration_time(ref uint us);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_set_selected_wavelength_range(double start, double end);
[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
static extern int pynect_direct_get_selected_wavelength_range(
ref PynectDirectSelectedWavelengthRange range);
static void Require(int code, string what)
{
if (code != SUCCESS)
throw new InvalidOperationException(string.Format(
"{0} failed, code={1}", what, code));
}
static void WaitExit()
{
Console.Write("Press any key to exit...");
try { Console.ReadKey(); }
catch { /* exit directly in non-interactive environments (input redirected) */ }
}
static void Main()
{
try
{
int n = pynect_direct_scan_devices();
if (n <= 0) { Console.WriteLine("No device found."); WaitExit(); return; }
Console.WriteLine(string.Format("Found {0} device(s).", n));
Require(pynect_direct_open_device(0), "open_device");
var info = new PynectDirectDeviceInfo();
Require(pynect_direct_get_device_info(ref info), "get_device_info");
Console.WriteLine(string.Format(
"Model: {0}, Serial: {1}, HW: {2}",
info.model, info.serial_number, info.hardware_version));
ushort count = 0;
Require(pynect_direct_get_wavelength_count(ref count), "get_wavelength_count");
Console.WriteLine(string.Format("Wavelength count: {0}", count));
double start = 0, end = 0;
Require(pynect_direct_get_full_wavelength_range(ref start, ref end),
"get_full_wavelength_range");
Console.WriteLine(string.Format(
"Wavelength range: {0:F2} ~ {1:F2} nm", start, end));
var wavelengths = new double[count];
Require(pynect_direct_get_full_wavelength_data(wavelengths, count),
"get_full_wavelength_data");
uint integration = 0;
Require(pynect_direct_get_integration_time(ref integration), "get_integration_time");
Console.WriteLine(string.Format("Current integration time: {0} us", integration));
pynect_direct_set_integration_time(50000);
var spectrum = new ushort[count];
Require(pynect_direct_get_full_spectrum_data(spectrum, count),
"get_full_spectrum_data");
Console.WriteLine(string.Format(
"{0,-6} {1,13} {2,9}", "Index", "Wavelength(nm)", "Intensity"));
Console.WriteLine(string.Format(
"{0,-6} {1,13} {2,9}", "-----", "--------------", "---------"));
for (int i = 0; i < 5 && i < count; i++)
Console.WriteLine(string.Format(
"{0,-6} {1,13:F3} {2,9}", i, wavelengths[i], spectrum[i]));
if (count > 10)
Console.WriteLine(string.Format("... (total {0} points)", count));
for (int i = (count > 5 ? count - 5 : 0); i < count; i++)
Console.WriteLine(string.Format(
"{0,-6} {1,13:F3} {2,9}", i, wavelengths[i], spectrum[i]));
if (start < 400.0 && end > 800.0)
{
Require(pynect_direct_set_selected_wavelength_range(400.0, 800.0),
"set_selected_range");
var sel = new PynectDirectSelectedWavelengthRange();
Require(pynect_direct_get_selected_wavelength_range(ref sel),
"get_selected_range");
Console.WriteLine(string.Format(
"Selected: pixels {0}..{1}, count={2}",
sel.start_pixel, sel.end_pixel, sel.count));
}
pynect_direct_close_device();
Console.WriteLine("Done.");
WaitExit();
}
catch (Exception e)
{
Console.WriteLine(string.Format("Error: {0}", e.Message));
WaitExit();
}
}
}
8. Python Language Example
8.1 Environment
- 64-bit Python 3 (verify:
python -c "import struct; print(8 * struct.calcsize('P'))"prints64) - No third-party library required (only the standard library
ctypes)
The full source is in the delivery package at
examples/python/spectrum_demo.py(i.e., the content of Section 8.2). Run it from theexamples/python/directory;minimal_test.pyand the two DLLs are both in that directory.
8.2 Full Source (spectrum_demo.py)
import ctypes
import os
from pathlib import Path
DLL_DIR = Path(__file__).resolve().parent / "dll" # change to your dll directory
def load_sdk():
# Load the DLL and add the dll directory to the dependency search path (ftd2xx.dll in the same directory)
if os.name == "nt" and hasattr(os, "add_dll_directory"):
os.add_dll_directory(str(DLL_DIR))
return ctypes.CDLL(str(DLL_DIR / "PynectDirect.dll"))
# ---------- Data structures ----------
class PynectDirectDeviceInfo(ctypes.Structure):
_fields_ = [
("protocol", ctypes.c_int),
("capabilities", ctypes.c_uint32),
("protocol_name", ctypes.c_char * 32),
("model", ctypes.c_char * 32),
("serial_number", ctypes.c_char * 32),
("hardware_version", ctypes.c_char * 64),
]
class PynectDirectSelectedWavelengthRange(ctypes.Structure):
_fields_ = [
("request_start_nm", ctypes.c_double),
("request_end_nm", ctypes.c_double),
("actual_start_nm", ctypes.c_double),
("actual_end_nm", ctypes.c_double),
("start_pixel", ctypes.c_uint16),
("end_pixel", ctypes.c_uint16),
("count", ctypes.c_uint16),
]
P_U16 = ctypes.POINTER(ctypes.c_uint16)
P_UINT = ctypes.POINTER(ctypes.c_uint)
P_DOUBLE = ctypes.POINTER(ctypes.c_double)
# ---------- Type bindings (functions used in this example) ----------
def bind_api(sdk):
sdk.pynect_direct_scan_devices.restype = ctypes.c_int
sdk.pynect_direct_open_device.argtypes = [ctypes.c_int]
sdk.pynect_direct_open_device.restype = ctypes.c_int
sdk.pynect_direct_close_device.restype = None
sdk.pynect_direct_get_device_info.argtypes = [ctypes.POINTER(PynectDirectDeviceInfo)]
sdk.pynect_direct_get_device_info.restype = ctypes.c_int
sdk.pynect_direct_get_wavelength_count.argtypes = [P_U16]
sdk.pynect_direct_get_wavelength_count.restype = ctypes.c_int
sdk.pynect_direct_get_full_wavelength_range.argtypes = [P_DOUBLE, P_DOUBLE]
sdk.pynect_direct_get_full_wavelength_range.restype = ctypes.c_int
sdk.pynect_direct_get_full_wavelength_data.argtypes = [P_DOUBLE, ctypes.c_int]
sdk.pynect_direct_get_full_wavelength_data.restype = ctypes.c_int
sdk.pynect_direct_get_full_spectrum_data.argtypes = [P_U16, ctypes.c_int]
sdk.pynect_direct_get_full_spectrum_data.restype = ctypes.c_int
sdk.pynect_direct_get_integration_time.argtypes = [P_UINT]
sdk.pynect_direct_get_integration_time.restype = ctypes.c_int
sdk.pynect_direct_set_integration_time.argtypes = [ctypes.c_uint]
sdk.pynect_direct_set_integration_time.restype = ctypes.c_int
sdk.pynect_direct_set_selected_wavelength_range.argtypes = [ctypes.c_double, ctypes.c_double]
sdk.pynect_direct_set_selected_wavelength_range.restype = ctypes.c_int
sdk.pynect_direct_get_selected_wavelength_range.argtypes = [ctypes.POINTER(PynectDirectSelectedWavelengthRange)]
sdk.pynect_direct_get_selected_wavelength_range.restype = ctypes.c_int
def require(code, what):
if code != 0:
raise RuntimeError(f"{what} failed, return code={code}")
def main():
sdk = load_sdk()
bind_api(sdk)
n = sdk.pynect_direct_scan_devices()
if n <= 0:
print("No device found.")
return
print(f"Found {n} device(s).")
require(sdk.pynect_direct_open_device(0), "open_device")
try:
info = PynectDirectDeviceInfo()
require(sdk.pynect_direct_get_device_info(ctypes.byref(info)), "get_device_info")
print(f"Model: {info.model.decode().strip(chr(0))}")
print(f"Serial: {info.serial_number.decode().strip(chr(0))}")
print(f"Hardware: {info.hardware_version.decode().strip(chr(0))}")
count = ctypes.c_uint16()
require(sdk.pynect_direct_get_wavelength_count(ctypes.byref(count)), "get_wavelength_count")
n = count.value
print(f"Wavelength count: {n}")
start, end = ctypes.c_double(), ctypes.c_double()
require(sdk.pynect_direct_get_full_wavelength_range(
ctypes.byref(start), ctypes.byref(end)), "get_full_wavelength_range")
print(f"Wavelength range: {start.value:.2f} ~ {end.value:.2f} nm")
wl = (ctypes.c_double * n)()
require(sdk.pynect_direct_get_full_wavelength_data(wl, n), "get_full_wavelength_data")
integration = ctypes.c_uint()
require(sdk.pynect_direct_get_integration_time(ctypes.byref(integration)),
"get_integration_time")
print(f"Current integration time: {integration.value} us")
sdk.pynect_direct_set_integration_time(50000)
spec = (ctypes.c_uint16 * n)()
require(sdk.pynect_direct_get_full_spectrum_data(spec, n), "get_full_spectrum_data")
print(f"{'Index':<6}{'Wavelength(nm)':>15}{'Intensity':>10}")
print(f"{'-----':<6}{'--------------':>15}{'---------':>10}")
for i in range(min(5, n)):
print(f"{i:<6}{wl[i]:>15.3f}{spec[i]:>10}")
if n > 10:
print(f"... (total {n} points)")
for i in range(max(0, n - 5), n):
print(f"{i:<6}{wl[i]:>15.3f}{spec[i]:>10}")
if start.value < 400.0 and end.value > 800.0:
require(sdk.pynect_direct_set_selected_wavelength_range(400.0, 800.0),
"set_selected_range")
sel = PynectDirectSelectedWavelengthRange()
require(sdk.pynect_direct_get_selected_wavelength_range(ctypes.byref(sel)),
"get_selected_range")
print(f"Selected: pixels {sel.start_pixel}..{sel.end_pixel}, count={sel.count}")
finally:
sdk.pynect_direct_close_device()
print("Done.")
if __name__ == "__main__":
main()
8.3 Run
python .\spectrum_demo.py
The complete Python type bindings for all 58 APIs are in
API_SIGNATURESinexamples/python/minimal_test.py; they can be reused directly.
9. Complete API Reference
Convention: return
0on success (unless stated otherwise);*marks output parameters; a 64-byte character buffer is recommended.
9.1 Device Discovery, Opening and Identification
| Function | Description |
|---|---|
pynect_direct_scan_devices() |
Refresh the USB device list, print each device's index, description and chip serial number, and return the device count (without opening devices) |
pynect_direct_get_chip_serial_by_index(index, chip_serial, max_len) |
Read the USB chip serial number by scan index, for reliably distinguishing multiple devices |
pynect_direct_open_device(index) |
Open the device by index and identify its protocol and capabilities, and set it as the current device; does not close other open devices (re-opening the same device only switches) |
pynect_direct_open_device_by_chip_serial(chip_serial) |
Open the matching device by chip serial number; suitable for multi-device systems where the index may change |
pynect_direct_select_device(device_index) |
Switch the current device (0-based, matching the opening order); for multi-device scenarios |
pynect_direct_get_open_device_count() |
Return the number of currently open devices |
pynect_direct_close_device() |
Close the currently selected device; repeated calls are allowed |
pynect_direct_close_all_devices() |
Close all open devices |
pynect_direct_is_open() |
Return 1 if the current device is open / 0 if closed (does not communicate with the device) |
pynect_direct_set_protocol_preference(protocol) |
Set the protocol preference used when opening a device (default AUTO auto-detect) |
pynect_direct_get_device_protocol(protocol*) |
Read the current device's actual protocol type |
pynect_direct_get_capabilities(capabilities*) |
Read the current device's 32-bit capability bitmask |
pynect_direct_get_device_info(info*) |
Read the current device's protocol, capabilities, model, serial number and hardware version in one call |
9.2 Integration Time and Averaging
| Function | Description |
|---|---|
pynect_direct_set_integration_time(us) |
Set the current-session integration time (microseconds); does not affect stored configuration |
pynect_direct_get_integration_time(us*) |
Read the current-session integration time |
pynect_direct_set_average_number(count) |
Set the averaging count (1-255); more averaging means lower noise but longer acquisition time |
pynect_direct_get_average_number(count*) |
Read the averaging count |
9.3 Wavelength and Spectrum Data (Core)
| Function | Description |
|---|---|
pynect_direct_get_wavelength_count(count*) |
Read the full data point count N, which determines the array size |
pynect_direct_get_full_wavelength_range(start_nm*, end_nm*) |
Read the first and last of the full wavelength array (i.e., the min/max wavelength over all pixels), returned in ascending order |
pynect_direct_get_full_wavelength_data(buffer, max_len) |
Read the full wavelength array for each pixel (double) |
pynect_direct_get_full_spectrum_data(buffer, max_len) |
Acquire one full spectrum and return each pixel's intensity (uint16) |
pynect_direct_set_selected_wavelength_range(start_nm, end_nm) |
Select a continuous pixel window on the host (without modifying the device output); stored only for the current session |
pynect_direct_get_selected_wavelength_range(out_range*) |
Read the requested/actual boundaries, pixel indices and count of the selected band |
pynect_direct_get_selected_wavelength_data(buffer, max_len) |
Crop the selected window from the full wavelength data |
pynect_direct_get_selected_spectrum_data(buffer, max_len) |
Acquire a full spectrum and then crop the intensity of the selected window |
9.4 Device Status and Identity Information
| Function | Description |
|---|---|
pynect_direct_get_calibration_coefficients(coeffs[4]) |
Read the 4 wavelength calibration coefficients (read-only) |
pynect_direct_get_temperature(temp_c*) |
Read the device's internal temperature (degrees Celsius) |
pynect_direct_get_hardware_version(version_str, max_len) |
Read the hardware version string |
pynect_direct_get_serial_number(sn, max_len) |
Read the spectrometer's business serial number |
pynect_direct_get_detector_serial_number(sn, max_len) |
Read the detector serial number (UV/VIS) |
pynect_direct_get_device_model(model, max_len) |
Read the device model |
pynect_direct_get_usb_status(connected*) |
Read the USB connection status reported by the device internally |
pynect_direct_set_usb_baud_rate(rate) |
Write the USB communication-rate parameter (16-bit configuration code) |
pynect_direct_get_usb_baud_rate(rate*) |
Read the USB communication-rate parameter |
9.5 Xenon Lamp, DAC and Level Output
| Function | Description |
|---|---|
pynect_direct_set_xenon_pulse(period_10ns, high_10ns) |
Set the xenon pulse period and high-level width (unit: 10ns) |
pynect_direct_get_xenon_pulse(period*, high*) |
Read the xenon pulse parameters |
pynect_direct_set_xenon_mode(mode) |
Set the xenon mode (XENON_OFF/XENON_CONTINUOUS/XENON_SINGLE) |
pynect_direct_get_xenon_mode(mode*) |
Read the xenon mode |
pynect_direct_set_dac_voltage(dac_value) |
Set the DAC 12-bit raw output code (not a voltage value) |
pynect_direct_get_dac_voltage(dac_value*) |
Read the DAC raw code value |
pynect_direct_set_level_output(level) |
Set the digital level output bitmask |
pynect_direct_get_level_output(level*) |
Read the level output bitmask |
9.6 External Trigger Acquisition
| Function | Description |
|---|---|
pynect_direct_set_external_trigger_config(enable, trig_type, scan_num) |
Set the external trigger on/off, type (rising edge 0xAA / level 0xBB) and the planned frame count |
pynect_direct_get_external_trigger_config(enable*, trig_type*, scan_num*) |
Read the external trigger configuration |
pynect_direct_get_spectrum_capture_status(status*) |
Read the 64-bit raw status word of external-trigger acquisition |
pynect_direct_external_spectrum_control(frame_index, data_out, max_len) |
Read the intensity of a specified external-trigger frame; returns the number of bytes written on success |
9.7 Flash and Device Reset (High Risk)
| Function | Description |
|---|---|
pynect_direct_set_flash_write_protect() |
Send the device-defined write-protect command (no parameters) |
pynect_direct_get_flash_write_protect(enabled*) |
Read the write-protect status flag (1/0) |
pynect_direct_reset_device() |
Reset the device; invalidates temporary parameters and the selected band |
pynect_direct_flash_read(sector, data_out, max_len) |
Read the raw bytes of a specified sector; returns the actual byte count on success |
pynect_direct_flash_write(sector, data, len) |
Write a specified sector; wrong data may corrupt the calibration, use with care |
9.8 Stored Configuration and Slit (Non-volatile)
| Function | Description |
|---|---|
pynect_direct_set_stored_integration_time(us) |
Write the default integration time (affects future power-on defaults) |
pynect_direct_set_stored_average_number(count) |
Write the default averaging count |
pynect_direct_set_stored_smoothing_width(width) |
Write the default smoothing width |
pynect_direct_get_stored_integration_time(us*) |
Read the stored default integration time |
pynect_direct_get_stored_average_number(count*) |
Read the stored default averaging count |
pynect_direct_get_stored_smoothing_width(width*) |
Read the stored default smoothing width |
pynect_direct_get_slit_width(um*) |
Read the entrance slit width information |
10. Typical Application Workflows
10.1 Single Acquisition and Display (Most Common)
Workflow: scan → open → read point count → read wavelength → acquire spectrum → display/save → close.
Minimal Python example (type bindings are in Section 8.2 bind_api; omitted here):
import ctypes
from minimal_test import find_dll, load_library, bind_api # reuse the verified bindings
sdk = load_library(find_dll())
bind_api(sdk)
require(sdk.pynect_direct_open_device(0), "open") # 1. open device 0
count = ctypes.c_uint16()
require(sdk.pynect_direct_get_wavelength_count(ctypes.byref(count)), "count")
n = count.value # 2. data point count N
wl = (ctypes.c_double * n)()
require(sdk.pynect_direct_get_full_wavelength_data(wl, n), "wavelength")
sdk.pynect_direct_set_integration_time(50000) # 3. integration time 50 ms
spec = (ctypes.c_uint16 * n)()
require(sdk.pynect_direct_get_full_spectrum_data(spec, n), "spectrum")
for i in range(0, n, max(1, n // 10)): # 4. show 10 evenly spaced points
print(f"{wl[i]:8.3f} nm {spec[i]}")
print(f"... total {n} points, range {wl[0]:.2f} ~ {wl[-1]:.2f} nm")
sdk.pynect_direct_close_device() # 5. close the device
The full C version is in Section 5.2 (spectrum_demo.c); removing the "read info / select range" parts leaves the minimal single-acquisition workflow.
10.2 Displaying a Selected Band
set_selected_wavelength_range(400, 800)
get_selected_wavelength_range(&sel) // get sel.count
get_selected_wavelength_data(wl_buf, sel.count)
get_selected_spectrum_data(spec_buf, sel.count)
Switching the band just calls set_selected_wavelength_range again; the device is unaware of it, so it is very fast.
10.3 Multi-device Management (Opening Two Devices Simultaneously)
/* 1. scan devices and confirm the chip serial numbers */
int n = pynect_direct_scan_devices();
for (int i = 0; i < n; i++) {
char chip[64];
if (pynect_direct_get_chip_serial_by_index(i, chip, sizeof(chip)) == 0)
printf("device %d: %s\n", i, chip);
}
/* 2. open two devices by chip serial (independent, both stay open) */
pynect_direct_open_device_by_chip_serial("FTXXXX1");
pynect_direct_open_device_by_chip_serial("FTXXXX2");
printf("open devices: %d\n", pynect_direct_get_open_device_count()); // 2
/* 3. switch the current device and acquire (each device has independent state) */
pynect_direct_select_device(0); /* current: the first device */
uint16_t count;
pynect_direct_get_wavelength_count(&count);
pynect_direct_get_full_spectrum_data(spec0, count);
pynect_direct_select_device(1); /* current: the second device */
pynect_direct_get_wavelength_count(&count);
pynect_direct_get_full_spectrum_data(spec1, count);
/* 4. finish: close all or close one by one */
pynect_direct_close_all_devices();
10.4 Saving Data
Write the two columns wl[] (double) and spec[] (uint16) to a CSV file (see python_ctypes_example_guide.md in the Python manual for an example).
11. FAQ
Q1: DLL failed to load, saying PynectDirect.dll not found?
PynectDirect.dll and ftd2xx.dll must be in the same directory; check the path and bitness (must be a 64-bit process).
Q2: Python calls return strange results?
You forgot to set argtypes/restype. All Python calls must bind the types first (see Section 8.2 bind_api).
Q3: scan_devices returns 0?
The device is not connected, the driver is not installed, or the device is occupied by another program. Re-plug the device and retry.
Q4: Opening the device returns -6?
The device is exclusively occupied by another process (e.g., another acquisition program). Close other programs and retry.
Q5: get_selected_* returns -1?
set_selected_wavelength_range has not been called yet, or the window became invalid after a device reset. Set the selection first.
Q6: Intensity is all zero or fully saturated?
The integration time is too short or too long. Adjust set_integration_time; confirm the light source / xenon lamp works properly.
Q7: Can two programs access the same device simultaneously?
No. A single physical device can only be opened by one process; the second process will fail to open it (-6). However, one program can open multiple different devices simultaneously (up to 4) and switch between them with select_device.
Q7b: After opening multiple devices, do their parameters affect each other?
No. Each device has independent protocol, capabilities, integration time, averaging and selected-band state; after switching devices, you read/write that device's own state.
Q8: Does the integration time persist after power off?
No. set_integration_time only affects the current session; to persist it use set_stored_integration_time (which changes the device default, use with care).
Q9: Can a 32-bit program call the SDK?
No. The SDK is a 64-bit DLL.
Q10: C# calls return garbage or crash?
Check that the P/Invoke uses CallingConvention.Cdecl, that the struct field order/size matches the header, and that the build target is x64.
12. Error Codes and Troubleshooting
12.1 Error Code Quick Reference
| Return code | Meaning | Suggested action |
|---|---|---|
| 0 | Success | - |
| -1 | Not initialized (selection not set) | Call set_selected_wavelength_range first |
| -2 | Device not found / index out of range | Re-scan; check the device connection |
| -3 | Device not open | Call open_device first |
| -4 | Communication failure | Check the cable; re-plug the device |
| -5 | Invalid parameter | Check the index, range, array size and string buffer |
| -6 | Open failed (occupied) | Close other programs using the device |
| -7 | CRC check failed | Communication interference, retry; replace the cable |
| -8 | Frame header mismatch | Protocol identification abnormal; try forcing a specific protocol |
| -9 | Timeout | Check the device response; power-cycle the device |
| -10 | Write protect | Check the Flash write-protect status |
| -11 | Not supported | Query get_capabilities first to confirm the feature bit |
12.2 Troubleshooting Order
- Confirm the
bin/directory contains exactlyPynectDirect.dll+ftd2xx.dll(the example directory also has one copy of each). - Confirm Python/the program is 64-bit.
- Run
examples/python/minimal_test.py --read-onlyand see which step fails. - Confirm no other software is occupying the device.
- Re-plug the device USB and retry.
Appendix: Delivery Package Structure
PynectDirect_SDK/
|-- README.md
|-- bin/
| |-- PynectDirect.dll # SDK dynamic library
| `-- ftd2xx.dll # FTDI driver (required)
|-- include/
| `-- pynect_direct.h # public header (authoritative source of signatures)
|-- lib/
| |-- libPynectDirect.dll.a # GCC import library
| `-- libPynectDirect.a # GCC static library
|-- examples/ # one directory per example, dependencies included
| |-- c/ # C example: source + include + lib + two DLLs
| | |-- spectrum_demo.c
| | |-- include/pynect_direct.h
| | |-- lib/libPynectDirect.dll.a, libPynectDirect.a
| | `-- PynectDirect.dll, ftd2xx.dll
| |-- cpp/ # C++ example (same structure as c/)
| |-- csharp/ # C# example: spectrum_demo.cs + two DLLs
| `-- python/ # Python example: all py scripts + two DLLs
| |-- spectrum_demo.py # single acquisition (chapter 8)
| |-- minimal_test.py # device smoke test
| |-- python_ctypes_example.py # interactive selected-band example + guide
| `-- dual_spectrometer_test.py # dual-fiber spectrometer (UV/VIS + NIR) simultaneous acquisition test
`-- docs/
|-- PynectDirect_API_Guide.md
`-- PynectDirect用户使用手册v3.0.1.md # this document