### Standalone Param Server Initialization Sequence Source: https://context7.com/spaceinventor/csh_example/llms.txt This C code snippet shows the key initialization sequence for a standalone POSIX executable that serves parameters over a network interface using CSP. It initializes CSP, adds a ZMQ interface, binds a service handler, and starts server tasks. ```c /* Build and run (Meson): * meson setup builddir * ninja -C builddir * ./builddir/param_example_server --zmq_addr 110 --zmq_proxy localhost * * Command-line options: * --eth_addr CSP address on Ethernet (0 = disabled) * --eth_mask Ethernet netmask (default 12) * --eth_dev Ethernet device name (default eth0) * --can_addr CSP address on CAN (0 = disabled) * --can_mask CAN netmask (default 12) * --zmq_addr CSP address on ZMQ (default 110) * --zmq_mask ZMQ netmask (default 8) * --zmq_proxy ZMQ proxy hostname (default localhost) */ /* Key initialisation sequence inside main(): */ csp_conf.hostname = "param_example_server"; csp_conf.model = "Example Libparam Server"; csp_conf.revision = csh_example_version_string; /* set by git describe at build time */ csp_init(); /* Add ZMQ hub interface */ csp_zmqhub_init_filter2("ZMQ", zmq_proxy, zmq_addr, zmq_mask, 1, &zmq_itf, 0, 6000, 7000); zmq_itf->is_default = 1; /* Bind CSP service handler and start router */ csp_bind_callback(csp_service_handler, CSP_ANY); pthread_create(&router_handle, NULL, router_task, NULL); /* Start VMEM server and param server */ run_in_thread(vmem_server_task, "VMEM server", NULL); csp_bind_callback(param_serve, PARAM_PORT_SERVER); /* Server loops forever; parameters are now remotely accessible: * * (on a remote CSH connected to the same ZMQ proxy) * csh> csp add zmq -d 10 localhost * csh> get -n 110 vmem_custom_u8 * 0 * csh> set -n 110 vmem_custom_u8 42 * csh> get -n 110 vmem_custom_u8 * 42 */ ``` -------------------------------- ### Declare Parameter Backed by Static RAM Source: https://context7.com/spaceinventor/csh_example/llms.txt Use `PARAM_DEFINE_STATIC_RAM` to register a parameter directly mapped to a C variable in static RAM. This allows CSH commands like `list`, `get`, and `set` to operate on the variable without runtime registration. An optional callback can be provided for actions on write. ```c #include /* Callback invoked on every write to ram_u8 */ void param_ramu8_cb(param_t *param, int offset) { printf("Parameter %s[%d] is set to %d\n", param->name, offset, param_get_uint8_array(param, offset)); } /* Backing storage: 10-element uint8 array */ uint8_t _ram_u8[10]; /* Args: id, name, type, array_len, elem_size, mask, callback, unit, storage, description */ PARAM_DEFINE_STATIC_RAM(155, ram_u8, PARAM_TYPE_UINT8, 10, sizeof(uint8_t), PM_CONF, param_ramu8_cb, "", &_ram_u8[0], "Test RAM U8"); /* Scalar uint16, no callback, pre-initialized to 555 */ uint16_t _ram_u16 = 555; PARAM_DEFINE_STATIC_RAM(156, ram_u16, PARAM_TYPE_UINT16, -1, sizeof(uint16_t), PM_CONF, NULL, "", &_ram_u16, "Test RAM U16"); /* * After loading the APM in CSH: * csh> list * 155 ram_u8 uint8[10] conf ... Test RAM U8 * 156 ram_u16 uint16 conf ... Test RAM U16 * * csh> set ram_u16 42 * csh> get ram_u16 * 42 */ ``` -------------------------------- ### CSH Init Script for ZMQ Interface and APM Loading Source: https://context7.com/spaceinventor/csh_example/llms.txt This bash script is passed to CSH with the -i flag to initialize CSP, add a ZMQ interface, and load APMs at startup. It requires a zmqproxy running on localhost. ```bash # init/zmq.csh # Usage: csh -i init/zmq.csh # Requires: zmqproxy running on localhost csp init # Initialise the CSP stack csp add zmq -d 10 localhost # Add ZMQ device at CSP address 10, proxy on localhost apm load -p . -s example # Load the shared library "libcsh_example.so" from CWD # Verify after startup: # csh> apm info # csh> list # shows ram_u8, ram_u16, vmem_u8, vmem_u16 # csh> help # shows aping # csh> aping 10 # ping CSP node 10 ``` -------------------------------- ### Implement Custom VMEM Driver Read/Write Handlers Source: https://context7.com/spaceinventor/csh_example/llms.txt Use this when the backing store for a VMEM region is not plain RAM. Supply custom read and write function pointers and declare the vmem_t struct in the 'vmem' ELF section for automatic discovery by the CSH 'vmem' command. ```c #include #include #include #define VMEM_CONF_EXAMPLE_ADDR 0x32000700 #define VMEM_CONF_EXAMPLE_SIZE 120 /* fits 8×u8 + 8×u16 + 8×u32 + 8×u64 */ static uint8_t vmem_example_buffer[VMEM_CONF_EXAMPLE_SIZE] = {0}; static void vmem_example_read(vmem_t *vmem, uint64_t addr, void *dataout, uint32_t len) { memcpy(dataout, &vmem_example_buffer[addr], len); } static void vmem_example_write(vmem_t *vmem, uint64_t addr, const void *datain, uint32_t len) { memcpy(&vmem_example_buffer[addr], datain, len); } /* Place in the "vmem" section so CSH discovers it via vmem command */ static vmem_t vmem_example __attribute__((section("vmem"), used)) = { .type = VMEM_TYPE_DRIVER, .name = "exmpl", .size = VMEM_CONF_EXAMPLE_SIZE, .read = vmem_example_read, .write = vmem_example_write, .big_endian = 0, .vaddr = VMEM_CONF_EXAMPLE_ADDR, .ack_with_pull = true, }; /* Parameters layered on top of the driver VMEM (param IDs must be unique per process) */ PARAM_DEFINE_STATIC_VMEM(200, vmem_custom_u8, PARAM_TYPE_UINT8, 8, sizeof(uint8_t), PM_CONF, NULL, "", example, 0x00, "Test VMEM U8"); PARAM_DEFINE_STATIC_VMEM(201, vmem_custom_u16, PARAM_TYPE_UINT16, 8, sizeof(uint16_t), PM_CONF, NULL, "", example, 0x08, "Test VMEM U16"); PARAM_DEFINE_STATIC_VMEM(202, vmem_custom_u32, PARAM_TYPE_UINT32, 8, sizeof(uint32_t), PM_CONF, NULL, "", example, 0x18, "Test VMEM U32"); PARAM_DEFINE_STATIC_VMEM(203, vmem_custom_u64, PARAM_TYPE_UINT64, 8, sizeof(uint64_t), PM_CONF, NULL, "", example, 0x38, "Test VMEM U64"); /* * csh> vmem * exmpl DRIVER 0x32000700 120 bytes * * csh> poke 0x32000700 0xAB * csh> peek 0x32000700 * 0xAB */ ``` -------------------------------- ### Declare Parameter Backed by VMEM Region Source: https://context7.com/spaceinventor/csh_example/llms.txt Use `VMEM_DEFINE_STATIC_RAM` to allocate a virtual-memory region and `PARAM_DEFINE_STATIC_VMEM` to map a parameter to an offset within that region. This is useful for parameters that need to persist across APM reloads or belong to a persistent memory map. ```c #include #include #include /* Create a 1024-byte VMEM region called "apmtest" */ VMEM_DEFINE_STATIC_RAM(apmtest, "apmtest", 1024); /* Map a uint8 parameter at offset 0x0 within the VMEM region */ PARAM_DEFINE_STATIC_VMEM(157, vmem_u8, PARAM_TYPE_UINT8, 0, sizeof(uint8_t), PM_CONF, NULL, "", apmtest, 0x0, "Test VMEM U8"); /* Map a uint16 parameter at offset 0x1 */ PARAM_DEFINE_STATIC_VMEM(158, vmem_u16, PARAM_TYPE_UINT16, 0, sizeof(uint16_t), PM_CONF, NULL, "", apmtest, 0x1, "Test VMEM U16"); /* * After loading the APM: * csh> list * 157 vmem_u8 uint8 conf ... Test VMEM U8 * 158 vmem_u16 uint16 conf ... Test VMEM U16 * * csh> set vmem_u8 7 * csh> get vmem_u8 * 7 */ ``` -------------------------------- ### Implement libinfo for APM Identification Source: https://context7.com/spaceinventor/csh_example/llms.txt This C function is called by CSH when 'apm info' is run. It prints the APM's name, version, and capabilities. ```c #include /* Called by CSH when the user runs `apm info` */ void libinfo(void) { printf("This APM is an example of how to add slash and param functionality\n"); printf("It also shows how to use Space Inventor's param implementation\n"); } /* * csh> apm info * Loaded APMs: * [csh_example] This APM is an example of how to add slash and param functionality * It also shows how to use Space Inventor's param implementation */ ``` -------------------------------- ### Register Custom Slash Command in CSH Source: https://context7.com/spaceinventor/csh_example/llms.txt Use the `slash_command` macro to register a C function as a named command in the CSH shell. The function receives a `struct slash *` context for argument parsing and returns a status code. Commands appear automatically in `help` output. ```c #include #include #include static int aping_cmd(struct slash *slash) { unsigned int node = 16383; /* broadcast by default */ unsigned int timeout = 1000; /* ms */ unsigned int size = 0; optparse_t *parser = optparse_new("aping", "[node]"); optparse_add_help(parser); optparse_add_unsigned(parser, 'n', "node", "NUM", 0, &node, "CSP node (default 16383)"); optparse_add_unsigned(parser, 't', "timeout", "NUM", 0, &timeout, "timeout [ms] (default 1000)"); optparse_add_unsigned(parser, 's', "size", "NUM", 0, &size, "ping payload size (default 0)"); int argi = optparse_parse(parser, slash->argc - 1, (const char **)slash->argv + 1); if (argi < 0) { optparse_del(parser); return SLASH_EINVAL; } /* Positional node argument overrides --node */ if (++argi < slash->argc) node = atoi(slash->argv[argi]); slash_printf(slash, "Ping node %u size %u timeout %u: ", node, size, timeout); int result = csp_ping(node, timeout, size, CSP_O_CRC32); if (result >= 0) slash_printf(slash, "Reply in %d [ms]\n", result); else slash_printf(slash, "No reply\n"); return SLASH_SUCCESS; } /* Third arg is argument synopsis shown by help, fourth is description */ slash_command(aping, aping_cmd, "", "aping APM test"); /* * csh> help * aping aping APM test * * csh> aping 10 * Ping node 10 size 0 timeout 1000: Reply in 3 [ms] * * csh> aping --node 10 --timeout 500 --size 32 * Ping node 10 size 32 timeout 500: Reply in 4 [ms] */ ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.