This is the C/C++ SDK for Chessnut electronic chess computers, such as the Chessnut Air.
Licensed under The MIT License (MIT).
just
)$ just
Available recipes:
build # build for Debug
clang-details # print clang details, including environment and architecture
clangd-check filename # run a clangd check on a single file
clean # clean
configure # configure a build
coverage # generate code coverage report
default # print available targets
do # clean, compile, build for Debug
docs # generated documentation (requires Doxygen)
format # format source code (.c and .h files) with clang-format
just-vars # evaluate and print all just variables
release # build for Release
run *args # run a Debug binary
run-release *args # run a Release binary
system-info # print system information such as OS and architecture
tidy # run clang-tidy (see .clang-tidy)
tidy-checks # show configured checks of clang-tidy
tidy-config # show effective configuration of clang-tidy
tidy-verify-config # verify configuration of clang-tidy
An example application is available at src/main.c.
cl_connect()
to connect to the chessboard.cl_disconnect()
to disconnect from the chessboard.#include <stdio.h>
#include <stdlib.h>
#include "easy_link_c.h"
int main(void) {
// Connect to the chessboard device with HID. If the device is not
// connected, it will automatically connect when the device is plugged into
// the computer.
if (cl_connect() == 1) {
printf("Successfully connected to chessboard\n");
} else {
printf("[ERROR] Failed to connect to chessboard. Exiting ...\n");
return EXIT_FAILURE;
}
// Disconnect from the board.
cl_disconnect();
return EXIT_SUCCESS;
}
cl_connect()
to connect to the chessboard.const char *fen
will be
populated with a FEN string
(Forsyth-Edwards Notation)
that describes the board position at the time of the callback, and the
parameter int len
will represent the size of this FEN string.cl_set_readtime_callback(callback)
.
cl_switch_real_time_mode()
. Setting the real-time mode is required,
otherwise you cannot obtain the FEN of the chessboard's current state.#include <stdio.h>
#include "easy_link_c.h"
void callback(const char *fen, int len) {
printf("Board position in FEN: %.*s\n", len, fen);
}
int main(void) {
cl_connect(); // we skip error handling here for the sake of brevity
cl_set_readtime_callback(callback);
cl_switch_real_time_mode();
for (;;) {
getchar();
}
cl_disconnect();
}
cl_connect()
to connect to the chessboard.char **led
array of 8 strings with 8 chars each, representing the
LED states of the 8x8 chessboard.
8
, the last string is row 1
on the
board.a
, the last character is
column h
.'1'
in a string means enable the LED of the associated square.
Char '0'
means disable the LED.cl_led(const char *leds[8])
.Illustration for const char *leds[8]
:
abcdefgh
8........8 leds[0]
7........7 leds[1]
6........6 leds[2]
5........5 leds[3]
4..x.....4 leds[4]
3........3 leds[5]
2........2 leds[6]
1........1 leds[7]
abcdefgh
For example, to enable the LED for square c4
only ('x' above), use:
{ "00000000",
"00000000",
"00000000",
"00000000",
"00100000", // <-- here
"00000000",
"00000000",
"00000000" };
Full example:
#include <stdio.h>
#include "easy_link_c.h"
int main(void) {
cl_connect(); // we skip error handling here for the sake of brevity
const char *leds[8] = { "11111111",
"00000000",
"11111111",
"00000000",
"11111111",
"00000000",
"11111111",
"00000000" };
if (cl_led(leds) == 0) {
printf("[ERROR] Could not enable/disable LEDs\n");
}
cl_disconnect();
}
cl_connect()
to connect to the chessboard.cl_beep(unsigned short frequencyHz, unsigned short durationMs)
.
The parameters set the frequency (in Hz) and duration (in milliseconds) of
the buzzer, respectively.#include "easy_link_c.h"
int main(void) {
cl_connect(); // we skip error handling here for the sake of brevity
if (cl_beep(1000, 200) == 0) {
printf("[ERROR] failed to beep\n");
}
cl_disconnect();
}
cl_connect()
to connect to the chessboard.char*
)
into which the information will be stored.
cl_version(char *version)
: version of the SDK library;
the version
parameter should have a length of at least 20cl_get_mcu_version(char *version)
: version of the MCU hardware;
the version
parameter should have a length of at least 100cl_get_ble_version(char *version)
: version of the BLE hardware
(Bluetooth Low Energy);
the version
parameter should have a length of at least 1000
if the function call failed.#include <stdio.h>
#include "easy_link_c.h"
int main(void) {
cl_connect(); // we skip error handling here for the sake of brevity
char sdk_version[20];
const size_t sdk_version_length = cl_version(sdk_version);
if (sdk_version_length > 0) {
printf("[DEBUG] SDK version: %.*s\n", (int)sdk_version_length, sdk_version);
// Example output:
// SDK version: 1.0.0
} else {
printf("[ERROR] Could not get SDK version\n");
}
char mcu_version[100_mcu_version_length];
const size_t mcu_version_length = cl_get_mcu_version(mcu_version);
if (mcu_version_length > 0) {
printf("MCU hardware version: %.*s\n", (int)mcu_version_length, mcu_version);
// Example output:
// MCU hardware version: CN_DVT9_210659
} else {
printf("[ERROR] Could not query MCU hardware version\n");
}
char ble_version[100_ble_version_length];
const size_t ble_version_length = cl_get_ble_version(ble_version);
if (ble_version_length > 0) {
printf("BLE hardware version: %.*s\n", (int)ble_version_length, ble_version);
// Example output:
// BLE hardware version: CNCA100_V201
} else {
printf("[ERROR] Could not query BLE hardware version\n");
}
cl_disconnect();
}
cl_connect()
to connect to the chess board.cl_get_battery()
. Note that the battery level
is only an estimate that is not always accurate.#include <stdio.h>
#include "easy_link_c.h"
int main(void) {
cl_connect(); // we skip error handling here for the sake of brevity
const int battery_level = cl_get_battery();
if (battery_level >= 0) {
printf("Battery level: %d%%\n", battery_level);
// Example output:
// Battery level: 67%
} else {
printf("[ERROR] Could not query the battery level\n");
}
cl_disconnect();
}
The chess board can store replays of played matches in its internal storage.
The file format is a sequence of FEN strings, separated by ;
. It is
recommended to validate the FEN strings after retrieval, as the board will,
for example, faithfully record board positions verbatim, even illegal ones.
Example game file content:
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR;rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR;rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR
To retrieve recorded game files from internal storage:
cl_connect()
to connect to the chess board.cl_get_file_count()
before attempting to retrieve any files.cl_get_file_and_delete(char* game_data, size_t len)
and then
automatically delete the game file from the chessboard's internal storage.
cl_get_file_and_keep()
to perform a dry-run of
file retrieval first (e.g., to verify that the provided char *game_data
is sufficiently large), and only if this succeeds do you call
cl_get_file_and_delete()
.cl_get_file_and_delete()
or its alias cl_get_file()
, both of which
will automatically delete the current game file after retrieval; the next
call will then operate on the next game file.#include <stdio.h>
#include "easy_link_c.h"
int main(void) {
cl_connect(); // we skip error handling here for the sake of brevity
const int file_count = cl_get_file_count();
if (file_count >= 0) {
printf("Stored game files: %d\n", file_count);
if (file_count > 0) {
// You must ensure that the retrieved game file fits into `game_content`,
// otherwise retrieval will fail and the respective game fail is lost!
char game_content[1024 * 10];
const int game_file_len = cl_get_file_and_delete(game_content, sizeof(game_content));
if (game_file_len > 0) {
printf("Game file content: %.*s\n", game_file_len, game_content);
//
// Example output:
// Game file content: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR;rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR;rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR
//
} else if (game_file_len == 0) {
printf("[WARNING] The game file is empty\n");
} else {
// When the provided `game_content` parameter was too small
printf("[ERROR] Failed to retrieve game file, which is now irrevocably lost\n");
}
}
} else {
printf("[ERROR] Could not retrieve number of stored game files");
}
cl_disconnect();
}
Supported platforms:
Dependencies:
Further details are provided below.
Build > Build All
to compile the project, including the EasyLinkSDK DLL
easylink.dll
and the main application main.exe
that depends on the
EasyLinkSDK DLL.Once the compilation is completed, open a terminal in PowerShell (or
cmd.exe
) and run:
# 1. Make easylink.ddl available to main.exe
$ cp out\build\x64-Debug\sdk\easylink.dll .\out\build\x64-Debug\src
# 2. Run main.exe
$ out\build\x64-Debug\src\main.exe
TODO: Automate and verify the Windows build setup. In the meantime, see the notes below as well as the GitHub workflow configuration, which uses Windows.
Install dependencies (e.g., with choco):
# clang toolchain
choco install -y cmake llvm ninja
choco install -y doxygen.install # optional, for generating documentation
# https://github.com/casey/just (a command runner)
choco install -y just # optional, but convenient
Then run the same configure and compile steps as described in the Linux/macOS section below.
use with gnu/linux
In order to use EasyLink as a user in the wheel group ( group can be arbitrary ) You must give the user read and write permissions for the Chessnut air. This can be done through a udev rule.
create a 99-chessnutair.rules file: /etc/udev/rules.d/99-chessnutair.rules, with the following:
SUBSYSTEM=="usb", ATTRS{idVendor}:="0x2d80", / ATTRS{idProduct}:="0x8002", GROUP="wheel", MODE="0660"
KERNEL=="hidraw2", GROUP="wheel", MODE="0660"
======== end =========
Currently supported USB Vender ID and Product ID
Vender ID: 0x2d80 Product IDs for different models:
Air : 0x80 Pro : 0x81 Air+: 0x82 Evo: 0x83 Go: 0x85**
Install dependencies:
### Debian/Ubuntu (EXPERIMENTAL: project compiles, but can't connect to chessboard)
# clang toolchain
sudo apt-get install -y build-essential clang clang-tidy cmake lldb ninja-build
sudo apt-get install -y doxygen # optional, for generating documentation
# Dependencies for EasyLinkSDK
sudo apt install libudev-dev libusb-dev libusb-1.0-0-dev
### macOS (EXPERIMENTAL: project compiles, but can't connect to chessboard)
# clang toolchain
brew install cmake llvm ninja
brew install doxygen # optional, for generating documentation
Ensure that the build setup uses clang as defined in .env:
# .env
CC=clang
CXX=clang++
Run the build with just
(or use cmake
directly):
# Option 1: You have just (https://github.com/casey/just) installed
$ just do
# Option 2: Use cmake directly
#
# 1. Configure
$ cmake -B build/ -S . -G "Ninja Multi-Config" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
# 2. Compile (pick one of the two)
$ cmake --build build/ --config Debug --target all # for a Debug build
$ cmake --build build/ --config Release --target all # for a Release build
If compilation succeeded, you can now run the main application that will attempt to connect to your chessboard.
just run
(or directly build/src/Debug/main
) to run the main
application. See example below.If the connection to the chessboard is successful, you will see output similar to:
# Or, from the top-level project directory:
#
# $ build/src/Debug/main (if you build the Debug version)
# $ build/src/Release/main (if you build the Release version)
#
$ just run
[DEBUG] SDK version: 1.0.0
[DEBUG] Connecting to chessboard via HID...
Successfully connected to chessboard
MCU hardware version: CN_DVT9_210659
BLE hardware version: CNCA100_V201
Battery level: 100%
[DEBUG] Disconnecting from chessboard
$