Last active
November 20, 2024 15:20
-
-
Save GOROman/6881b1ef30364a584c37eb66fd605395 to your computer and use it in GitHub Desktop.
ALIENTEK DP100 を C言語からコントロールするサンプル 無保証・自己責任で
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Alientek DP100 Control Test | |
// @GOROman | |
// for MacOS | |
// > brew install hidapi | |
// > gcc -o dp100 dp100.c -I/opt/homebrew/include/ -L/opt/homebrew/lib -lhidapi | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <unistd.h> // sleep関数のため | |
#include <hidapi/hidapi.h> | |
#define VENDOR_ID 0x2e3c // ALIENTEK | |
#define PRODUCT_ID 0xaf01 // DP100 | |
#define PACKET_SIZE 64 | |
// DeviceInfo構造体 | |
typedef struct { | |
uint8_t dev_type[16]; // デバイス名 | |
uint16_t hdw_ver; | |
uint16_t app_ver; | |
uint16_t boot_ver; | |
uint16_t run_area; | |
uint8_t dev_sn[12]; // 16進数シリアルナンバー | |
// 製造年この辺は値がおかしい | |
uint16_t year; | |
uint8_t moon; | |
uint8_t day; | |
} DeviceInfo; | |
// BasicSet構造体 | |
typedef struct { | |
uint8_t index; | |
uint8_t state; // 0:off 1:on | |
uint16_t vo_set; | |
uint16_t io_set; | |
uint16_t ovp_set; | |
uint16_t ocp_set; | |
} BasicSet; | |
#define INDEX_FLAG_SET 0x20 | |
#define INDEX_FLAG_ACTIVE 0x80 | |
// BasicInfo構造体 | |
typedef struct { | |
uint16_t vin; // 単位: mV | |
uint16_t vout; // 単位: mV | |
uint16_t iout; // ? | |
uint16_t vo_max; // 単位: mV | |
uint16_t temp1; // 単位: 100m度C | |
int16_t temp2; // 単位: 100m度C | |
uint16_t dc_5v; // 単位: mV | |
uint8_t out_mode; | |
uint8_t work_st; | |
} BasicInfo; | |
typedef enum { | |
OpCode_None = 0x00, | |
OpCode_DeviceInfo = 0x10, | |
OpCode_BasicInfo = 0x30, | |
OpCode_BasicSet = 0x35, | |
OpCode_SystemInfo = 0x40, | |
OpCode_ScanOut = 0x50, | |
OpCode_SerialOut = 0x55, | |
OpCode_Invalid = 0xFF | |
} OpCode; | |
// CRC-16/MODBUS 計算関数 | |
uint16_t calculate_crc16(const uint8_t *data, size_t length) { | |
uint16_t crc = 0xFFFF; // 初期値 | |
for (size_t i = 0; i < length; i++) { | |
crc ^= data[i]; | |
for (uint8_t j = 0; j < 8; j++) { | |
if (crc & 1) | |
crc = (crc >> 1) ^ 0xA001; // 多項式: 0xA001 | |
else | |
crc >>= 1; | |
} | |
} | |
return crc; // 計算結果(リトルエンディアン形式) | |
} | |
int send_frame(hid_device* handle, OpCode op_code, const uint8_t* data, size_t length) { | |
printf("Sending OpCode: 0x%02X\n", op_code); | |
printf("Data length: %zu\n", length); | |
uint8_t frame[PACKET_SIZE] = {0}; | |
frame[0] = 0xfb; // direction | |
frame[1] = op_code; // OpCode | |
frame[2] = 0x00; // reserved | |
frame[3] = length; // データ長 | |
if (data) { | |
memcpy(frame + 4, data, length); | |
} | |
uint16_t crc = calculate_crc16(frame, length + 4); | |
frame[length + 4] = crc & 0xFF; // CRC16(リトルエンディアン) | |
frame[length + 5] = crc >> 8; // CRC16(リトルエンディアン) | |
// 送信 | |
int res = hid_write(handle, frame, sizeof(frame)); | |
if (res < 0) { | |
fprintf(stderr, "Error writing to device: %ls\n", hid_error(handle)); | |
return -1; | |
} else { | |
printf("Sent %d bytes to the device:\n", res); | |
for (int i = 0; i < res; i++) { | |
printf("%02x ", frame[i]); | |
} | |
printf("\n"); | |
} | |
return 0; | |
} | |
int recv_frame(hid_device* handle, void* data, size_t size) { | |
int res; | |
uint8_t buf[PACKET_SIZE] = {0}; | |
printf("Waiting for response...\n"); | |
// 応答を受信 | |
memset(buf, 0, sizeof(buf)); | |
res = hid_read(handle, buf, sizeof(buf)); | |
if (res < 0) { | |
fprintf(stderr, "Error reading from device: %ls\n", hid_error(handle)); | |
return -1; | |
} | |
printf("Received %d bytes from the device:\n", res); | |
for (int i = 0; i < res; i++) { | |
printf("%02x ", buf[i]); | |
} | |
printf("\n"); | |
uint8_t len = buf[3]; | |
// CRC16の検証 | |
uint16_t recv_crc = buf[4+ len] | (buf[5+len] << 8); | |
uint16_t calc_crc = calculate_crc16(buf, len+4); | |
if (recv_crc != calc_crc) { | |
fprintf(stderr, "CRC mismatch: calculated 0x%04X, received 0x%04X\n", calc_crc, recv_crc); | |
} | |
// データ部 | |
memcpy(data, buf + 4, size); | |
return 0; | |
} | |
// DP100 デバイス情報取得 | |
void print_DeviceInfo(hid_device* handle) | |
{ | |
send_frame(handle, OpCode_DeviceInfo, NULL, 0); | |
DeviceInfo info; | |
if ( recv_frame(handle, &info, sizeof(DeviceInfo)) >= 0 ) { | |
// 受信成功 | |
printf("\tdev_type: %s\n", info.dev_type); | |
printf("\thdw_ver: %d\n", info.hdw_ver); | |
printf("\tapp_ver: %d\n", info.app_ver); | |
printf("\tboot_ver: %d\n", info.boot_ver); | |
printf("\trun_area: %d\n", info.run_area); | |
printf("\tdev_sn: "); | |
for(int i = 0; i < 12; i++) { | |
printf("%02X", info.dev_sn[i]); | |
} | |
printf("\n"); | |
} | |
} | |
void print_BasicInfo(hid_device* handle) | |
{ | |
send_frame(handle, OpCode_BasicInfo, NULL, 0); | |
BasicInfo info; | |
if ( recv_frame(handle, &info, sizeof(BasicInfo)) >= 0 ) { | |
// 受信成功 | |
printf("\tvin: %4.3fV\n", info.vin/1000.0f); | |
printf("\tvout: %4.3fV\n", info.vout/1000.0f); | |
printf("\tiout: %4.3fA\n", info.iout/1000.0f); | |
printf("\tvo_max: %4.3fV\n", info.vo_max/1000.0f); | |
printf("\ttemp1: %3.1f 'C\n", info.temp1 / 10.0f); | |
printf("\ttemp2: %3.1f 'C\n", info.temp2 / 10.0f); | |
printf("\tdc_5v: %4.3fV\n", info.dc_5v / 1000.0f); | |
printf("\tout_mode: %d\n", info.out_mode); | |
printf("\twork_st: %d\n", info.work_st); | |
} | |
} | |
void get_BasicSet(hid_device* handle, uint8_t index, BasicSet* info) | |
{ | |
uint8_t data = index; | |
send_frame(handle, OpCode_BasicSet, (uint8_t*)&data, sizeof(data)); | |
recv_frame(handle, info, sizeof(BasicSet)); | |
} | |
void set_BasicSet(hid_device* handle, BasicSet* set) | |
{ | |
send_frame(handle, OpCode_BasicSet, (uint8_t*)set, sizeof(BasicSet)); | |
uint8_t result = 0; | |
recv_frame(handle, &result, sizeof(result)); | |
} | |
int main() { | |
hid_device *handle; | |
unsigned char buf[PACKET_SIZE] = {0}; | |
int res; | |
// hidapiの初期化 | |
if (hid_init()) { | |
fprintf(stderr, "Failed to initialize hidapi.\n"); | |
return -1; | |
} | |
// デバイスをオープン | |
handle = hid_open(VENDOR_ID, PRODUCT_ID, NULL); | |
if (!handle) { | |
fprintf(stderr, "Failed to open HID device (VID: 0x%04x, PID: 0x%04x).\n", VENDOR_ID, PRODUCT_ID); | |
hid_exit(); | |
return -1; | |
} | |
// DP100 デバイス情報取得 | |
print_DeviceInfo(handle); | |
// DP100 基本情報取得 | |
print_BasicInfo(handle); | |
// DP100 基本設定をクエリ | |
BasicSet set; | |
get_BasicSet(handle, INDEX_FLAG_ACTIVE, &set); // 0x80 == current | |
printf("index: %d\n", set.index); | |
printf("state: 0x%02x\n", set.state); | |
printf("vo_set: %4.3fV\n", set.vo_set/1000.0f); | |
printf("io_set: %4.3fA\n", set.io_set/1000.0f); | |
printf("ovp_set: %4.3fV\n", set.ovp_set/1000.0f); | |
printf("ocp_set: %4.3fA\n", set.ocp_set/1000.0f); | |
sleep(2); | |
// 電圧と電流を設定する | |
set.vo_set = 1500; // 1.5V | |
set.io_set = 100; // 0.1A | |
for (int i=0;i<10;i++) { | |
// DP100 基本設定をセット | |
int index = 0; | |
set.vo_set += 100; | |
set.state = (i % 2) ? 0x01 : 0x00; // Lチカ | |
set.index = INDEX_FLAG_SET | index; | |
printf("-> index: %d\n", set.index); | |
printf("-> state: 0x%02x\n", set.state); | |
printf("-> vo_set: %4.3fV\n", set.vo_set/1000.0f); | |
set_BasicSet(handle, &set); | |
usleep(1000*50); | |
get_BasicSet(handle, INDEX_FLAG_ACTIVE, &set); // 0x80 == current | |
printf("index: 0x%02x\n", set.index); | |
printf("state: 0x%02x\n", set.state); | |
printf("vo_set: %4.3fV\n", set.vo_set/1000.0f); | |
printf("io_set: %4.3fA\n", set.io_set/1000.0f); | |
printf("ovp_set: %4.3fV\n", set.ovp_set/1000.0f); | |
printf("ocp_set: %4.3fA\n", set.ocp_set/1000.0f); | |
usleep(1000*200); | |
} | |
// デバイスを閉じる | |
hid_close(handle); | |
// hidapiの終了 | |
hid_exit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
参考にしたもの: https://github.com/lessu/open_dp100/blob/master/DP100_Protocol.md (Thanks!)