C-nano Client#
This client provides a C API for interacting with a kRPC server. It is intended for use on embedded systems with tight resource constraints, hence the “nano” in its name.
Installing#
Manually include the source in your project#
The source files can be included in your project manually, by downloading and extracting the
source archive from the GitHub release page.
The header files can be found in the include directory and the source files are in src.
Arduino Library Manager#
If you are writing an Arduino sketch, the library can be installed using the Arduino Library Manager by searching for and installing “kRPC”.
Note
The source files installed by the Arduino Library Manager are renamed to end with .cpp so
that they are built using the C++ compiler. This allows the library to use the C++ only
HardwareSerial class for communication.
vcpkg#
The C-nano client is available from vcpkg. It can be installed as follows:
vcpkg install krpc-cnano
vcpkg install krpc-cnano:x64-windows
The library communicates over a serial port unless a transport feature is given. The tcp
feature builds it to communicate over TCP/IP instead:
vcpkg install "krpc-cnano[tcp]"
vcpkg install "krpc-cnano[tcp]:x64-windows"
The localsocket feature builds it to communicate over a unix domain socket with a server on
the same machine:
vcpkg install "krpc-cnano[localsocket]"
CMake#
CMake 3.15 or later is required. Download the source archive, extract it and run:
cmake -B build
cmake --build build
cmake --install build
By default, CMake looks for a system-installed nanopb. To download nanopb automatically
at build time instead, pass -DKRPC_FETCH_DEPS=ON (or -DKRPC_FETCH_NANOPB=ON) to
the configure step. When OFF (the default) the system package is required.
To install to a custom prefix:
cmake -B build -DCMAKE_INSTALL_PREFIX=/install/path
cmake --build build
cmake --install build
Compilation Options#
The following options control how the library operates. They must be specified at compile time as an argument to the compiler.
Error handling
KRPC_ERROR_CHECK_RETURN(the default) – when a remote procedure call gets an error, it returns the error code.KRPC_ERROR_CHECK_EXIT– terminates the program (by callingexit()) when an error occurs in a remote procedure call.KRPC_ERROR_CHECK_ASSERT– fails a debug assertion (by callingassert()) when an error occurs in a remote procedure call.KRPC_ERROR_CHECK_FN– specifies thekrpc_error_handlerfunction should be called when an error occurs in a remote procedure call. This should be set to a pointer to a function that takes a single parameter of typekrpc_error_t.KRPC_PRINT_ERRORS_TO_STDERR– enables printing of a descriptive error message to stderr when an error occursKRPC_ERROR_MESSAGES– captures the message describing an error returned by the server, which can then be read usingkrpc_get_error_message(). Without this every server error is indistinguishable, as they all returnKRPC_ERROR_RPC_FAILED. Enabling it costsKRPC_ERROR_MESSAGE_LENGTHbytes of static storage.KRPC_ERROR_MESSAGE_LENGTH– the size of the buffer used to store the message described above, defaulting to 256 bytes. Longer messages are truncated to fit.PB_NO_ERRMSG– disables error messages in the nanopb library, which kRPC uses to communicate with the server. Enabled by default in the Arduino version of the library.
Communication
KRPC_COMMUNICATION_POSIX– Specifies that the library should be built to communicate over a serial port using POSIX read/write functions communication mechanisms. This is the default when no other platform is detected.KRPC_COMMUNICATION_WINDOWS– Specifies that the library should be built to communicate over a serial port using the Windows API. The Windows platform will be auto-detected so you do not need to specify this manually.KRPC_COMMUNICATION_ARDUINO– Specifies that the library should be built using Arduino serial communication mechanisms. The Arduino platform will be auto-detected so you do not need to specify this manually.KRPC_COMMUNICATION_TCP– Specifies that the library should be built to communicate over TCP/IP with a server reachable over the network. A serial port is the usual choice for the devices this client is written for, so this is specified rather than auto-detected. Use-DKRPC_COMMUNICATION_TCP=ONfor a CMake build and thetcpfeature for a vcpkg install. These build a program linking the library for the same transport, and on Windows link it against winsock.KRPC_COMMUNICATION_LOCALSOCKET– Specifies that the library should be built to communicate over a unix domain socket, with a server running on the same machine. The connection is opened with the path of the server’s RPC socket in place of a serial port name. A serial port is the usual choice on the platforms providing both, so this is specified rather than auto-detected. Use-DKRPC_COMMUNICATION_LOCALSOCKET=ONfor a CMake build and thelocalsocketfeature for a vcpkg install, so that a program linking the library is built for the same transport.KRPC_COMMUNICATION_CUSTOM– Allows you to provide your own implementation for the communication mechanism.KRPC_SINGLE_CONNECTION– Only meaningful alongsideKRPC_COMMUNICATION_CUSTOM. A serial port carries the RPC and stream connections over one link, so each message is wrapped in a multiplexed message naming the connection it belongs to. Define this when a custom communication mechanism opens a connection to each server, as a socket does, and sends messages unwrapped.
Memory allocation
KRPC_BUFFER_SIZE– How much of a message to hold in memory while it is written to or read from the connection. The default is 1024 bytes, and 128 bytes when building for Arduino, where a kilobyte is a large share of the memory available. A larger message is carried in as many passes as it takes, so this bounds the memory a call costs rather than the size of a message. A message that fits is cheaper to send, as its size is written in front of it instead of found by a first pass over it. Two buffers of this size are used, one for sending and one for receiving, and neither outlives the call it is made for.KRPC_ALLOC_BLOCK_SIZE– The size of collections (lists, sets, etc.) are not know ahead of time, so when they are received from the server they are decoded into dynamically allocated memory on the heap. This option controls how many items to increase the capacity of the collection by when its space is exhausted. Setting this to 1 will consume the least amount of heap memory, but will require one heap allocation call per item. Setting this to a higher value will consume more memory, but require fewer allocations.KRPC_CUSTOM_MEMORY_ALLOC– Disables the default implementation of memory allocation functionskrpc_malloc,krpc_calloc,krpc_recallocand krpc_free so that you can provide your own implementation.
Note
On embedded systems you probably want to leave KRPC_PRINT_ERRORS_TO_STDERR undefined and
define PB_NO_ERRMSG to minimize the memory footprint of kRPC.
Getting Started#
Configuring the Server#
The C-nano client library communicates with the server using protobuf messages, over a serial port by default. The kRPC server, which runs in the game, needs to be configured to use the serial port protocol (instead of the default TCP/IP protocol). This can be done from the in-game server configuration window, which also allows settings such as the port name, baud rate and parity settings.
Building the library with KRPC_COMMUNICATION_TCP instead has it communicate over TCP/IP, with a
server left on its default protocol. A connection is then opened with the address and port the
server is listening on rather than the name of a port.
Note
A serial port carries data far more slowly than the game produces it, and the server drops a connection that sustains more data than the port can carry. See Buffering and Data Rates for the limits and how to stay within them.
Note
The client blocks while waiting for data from the server, with no timeout. A call left unanswered, after a dropped connection for example, never returns.
Linking#
After installation, CMake projects can link the library with:
find_package(krpc_cnano CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE krpc_cnano::krpc_cnano)
When installed via vcpkg, pass the vcpkg toolchain file at configure time:
cmake -B build -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake
POSIX Systems#
On POSIX systems (such as Linux) the following example program connects to the server, queries it for its version and prints it out:
#include <krpc_cnano.h>
#include <krpc_cnano/services/krpc.h>
int main() {
krpc_connection_t conn;
krpc_open(&conn, "COM0");
krpc_connect(conn, "Basic example");
krpc_schema_Status status;
krpc_KRPC_GetStatus(conn, &status);
printf("Connected to kRPC server version %s\n", status.version);
}
The krpc_connect() function is used to open a connection to a server. It takes as its first
argument a connection object into which the connection information is written. This is passed to
subsequent calls to interact with the server. The second argument is a name for the connection
(displayed in game) and the third is the name of the serial port to connect over.
Arduino#
The following example demonstrates how to connect to the server from an Arduino, through its serial port interface:
#include <krpc_cnano.h>
#include <krpc_cnano/services/krpc.h>
HardwareSerial * conn;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
conn = &Serial;
// Open the serial port connection
krpc_open(&conn, NULL);
// Set up communication with the server
krpc_connect(conn, "Arduino Example");
// Indicate successful connection by lighting the on-board LED
digitalWrite(LED_BUILTIN, HIGH);
}
void loop() {
}
Calling Remote Procedures#
The kRPC server provides procedures that a client can run. These procedures are arranged in groups
called services to keep things organized. The functionality for the services are defined in the
header files in krpc/services/.... For example, all of the functionality provided by the
SpaceCenter service is contained in the header file krpc/services/space_center.h.
The following example demonstrates how to invoke remote procedures using the Cnano client. It calls
krpc_SpaceCenter_ActiveVessel() to get a handle to the active vessel (of type
krpc_SpaceCenter_Vessel_t). It sets the name of the vessel and then prints out its altitude:
#include <krpc_cnano.h>
#include <krpc_cnano/services/space_center.h>
int main() {
krpc_connection_t conn;
krpc_open(&conn, "COM0");
krpc_connect(conn, "Remote Procedures example");
krpc_SpaceCenter_Vessel_t vessel;
krpc_SpaceCenter_ActiveVessel(conn, &vessel);
krpc_SpaceCenter_Vessel_set_Name(conn, vessel, "My Vessel");
// Get a handle to a Flight object for the vessel
krpc_SpaceCenter_Flight_t flight;
krpc_SpaceCenter_Vessel_Flight(conn, &flight, vessel, KRPC_NULL);
// Get the altiude
double altitude;
krpc_SpaceCenter_Flight_MeanAltitude(conn, &altitude, flight);
printf("%.2f\n", altitude);
}
Many procedures return an object identifying something in the game, such as a vessel or a part. See Object Lifetime for how such an object behaves when a game is loaded or what it identifies is destroyed.
Streams and Events#
These features are not supported by this client.
Client API Reference#
-
krpc_error_t krpc_open(krpc_connection_t *connection, const krpc_connection_config_t *arg)#
Create a communication handle over which the client can talk to a server.
When the library is built using
KRPC_COMMUNICATION_POSIX(the default when no other platform is detected) calling this function opens a serial port using the port name passed as arg, using a call toopen(arg, ...). In this case the type of the arg parameter isconst char *. For example:krpc_connection_t conn; krpc_open(&conn, "/dev/ttyS0");
When the library is built using
KRPC_COMMUNICATION_WINDOWS(auto-detected on Windows) calling this function opens the serial port named by arg using the Windows API, for examplekrpc_open(&conn, "COM1").When the library is built using
KRPC_COMMUNICATION_TCPcalling this function connects to the server over TCP/IP. arg is a pointer to akrpc_connection_config_tholding the address of the machine running the server and the port its RPC server listens on. The address may be a host name or an address literal, and each endpoint it resolves to is tried in turn. For example:krpc_connection_t conn; krpc_connection_config_t config; config.address = "127.0.0.1"; config.port = 50000; krpc_open(&conn, &config);
When the library is built using
KRPC_COMMUNICATION_ARDUINO, connection must be a pointer to aHardwareSerialobject. arg is optionally used to pass additional configuration options used to initialize the connection, including baud rate for the serial port.If arg is set to
NULLthe connection is initialized with a baud rate of 9600 and defaultsSERIAL_8N1for data, parity and stop bits. For example:krpc_connection_t conn; krpc_open(&conn, NULL);
When arg set to a pointer to a structure of type
krpc_connection_config_t, the baud rate, and data, parity and stop bits in the structure are used to initialize the connection. For example:krpc_connection_t conn; krpc_connection_config_t config; config.speed = 115200; config.config = SERIAL_5N1; krpc_open(&conn, &config);
Note
A serial connection has no end-of-file, so on Arduino a read fails with
KRPC_ERROR_EOFwhen the serial timeout elapses without a single byte arriving. This timeout is one second by default and applies to each byte, so a slow but progressing reply never trips it. If the server can take longer than this to start replying, raise the timeout withSerial.setTimeoutbefore callingkrpc_open().
-
krpc_error_t krpc_connect(krpc_connection_t connection, const char *name)#
Connect to a kRPC server.
- Parameters:
connection (krpc_connection_t) – A connection handle, created using a call to
krpc_open().name (const char*) – A descriptive name for the connection. This is passed to the server and appears in the in-game server window.
-
krpc_error_t krpc_close(krpc_connection_t connection)#
Closes the communication handle.
-
type krpc_error_t#
All kRPC functions return error codes of this type.
-
KRPC_OK#
The function completed successfully and no error occurred.
-
KRPC_ERROR_IO#
An input/output error occurred when communicating with the server.
-
KRPC_ERROR_EOF#
End of file was received from the server.
-
KRPC_ERROR_CONNECTION_FAILED#
Failed to establish a connection to the server.
-
KRPC_ERROR_NO_RESULTS#
The remote procedure call did not return a result.
-
KRPC_ERROR_RPC_FAILED#
The remote procedure call threw an exception.
-
KRPC_ERROR_ENCODING_FAILED#
The encoder failed to construct the remote procedure call.
-
KRPC_ERROR_DECODING_FAILED#
The decoder failed to interpret a result sent by the server.
-
KRPC_OK#
-
const char *krpc_get_error(krpc_error_t error)#
Returns a descriptive string for the given error code.
-
const char *krpc_get_error_message(void)#
Returns the message describing the most recent error returned by the server, or an empty string if there has not been one. It is formatted as
service.name: description, for exampleSpaceCenter.InvalidOperationException: Vessel does not exist, followed by the server stack trace when the server is configured to send one, and is truncated to fitKRPC_ERROR_MESSAGE_LENGTH.This function is only available when the library is built with
KRPC_ERROR_MESSAGESdefined, as storing the message is not free.