Add ESP-IDF and STM32 C examples; document platform examples
Both compile the same portable core (src/meshcore_companion.c) and differ only in the UART transport: - examples-esp-idf/tty_bridge: full ESP-IDF project (UART driver), core compiled directly via the component CMakeLists (no copy) - examples-stm32/uart_bridge: HAL drop-in (meshcore_setup/meshcore_poll) for a CubeMX/CubeIDE project, with integration README README updated: new examples in layout + an 'Other platform examples' section. Verified host build/test still pass and both new examples pass -Wall -Wextra syntax checks against stubbed platform headers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,11 @@ radio plays the *server* — the inverse of the phone/web app.
|
|||||||
Sibling of [`meshcore.js`](https://github.com/meshcore-dev/meshcore.js) (JS) and
|
Sibling of [`meshcore.js`](https://github.com/meshcore-dev/meshcore.js) (JS) and
|
||||||
`meshcore_py` (Python): this is the **C / C++** one.
|
`meshcore_py` (Python): this is the **C / C++** one.
|
||||||
|
|
||||||
|
SEE ALSO:
|
||||||
|
* RUST - meshcore-rs (aka meschore_rs) - https://github.com/andrewdavidmackenzie/meshcore-rs.git
|
||||||
|
* Javascript - meshcore.js - https://github.com/meshcore-dev/meshcore.js
|
||||||
|
* Python - meshcore_py - https://github.com/meshcore-dev/meshcore_py
|
||||||
|
|
||||||
## Two ways to use this repo
|
## Two ways to use this repo
|
||||||
|
|
||||||
The protocol logic is a single portable **C99 core** with no I/O, no `malloc`, and
|
The protocol logic is a single portable **C99 core** with no I/O, no `malloc`, and
|
||||||
@@ -19,10 +24,9 @@ two audiences from **one source of truth** (`src/meshcore_companion.{c,h}`):
|
|||||||
1. **Portable C library** — drop `src/meshcore_companion.{c,h}` into any project.
|
1. **Portable C library** — drop `src/meshcore_companion.{c,h}` into any project.
|
||||||
It assembles inbound frames from a byte stream, builds outbound command frames
|
It assembles inbound frames from a byte stream, builds outbound command frames
|
||||||
into buffers you own, and parses payloads into plain structs. You supply the
|
into buffers you own, and parses payloads into plain structs. You supply the
|
||||||
transport. A complete Linux example (POSIX `termios`) lives in
|
transport. The same core drops into Linux, ESP-IDF, bare STM32/nRF52, or any
|
||||||
`examples-linux/`, and the host unit test in `test/` runs with no hardware.
|
host bridge unchanged — see the example for each platform under `examples-*/`,
|
||||||
The same core drops into ESP-IDF, bare nRF52/STM32, or any host bridge
|
plus the host unit test in `test/` that runs with no hardware.
|
||||||
unchanged (those examples are planned).
|
|
||||||
|
|
||||||
2. **C++ Arduino library** — `src/MeshCoreCompanion.{h,cpp}` wrap the core in an
|
2. **C++ Arduino library** — `src/MeshCoreCompanion.{h,cpp}` wrap the core in an
|
||||||
Arduino-friendly class: inject any `Stream` (`Serial1` on a Grove UART, USB
|
Arduino-friendly class: inject any `Stream` (`Serial1` on a Grove UART, USB
|
||||||
@@ -45,11 +49,18 @@ meshcore_c/
|
|||||||
├── examples/
|
├── examples/
|
||||||
│ └── SensorChannelBridge/ # Arduino sketch (.ino)
|
│ └── SensorChannelBridge/ # Arduino sketch (.ino)
|
||||||
├── examples-linux/
|
├── examples-linux/
|
||||||
│ └── tty_bridge/ # portable C example: any Linux tty (USB or raw UART)
|
│ └── tty_bridge/ # portable C: any Linux tty (USB or raw UART)
|
||||||
|
├── examples-esp-idf/
|
||||||
|
│ └── tty_bridge/ # portable C: ESP-IDF UART driver (esp32/s3/c3)
|
||||||
|
├── examples-stm32/
|
||||||
|
│ └── uart_bridge/ # portable C: STM32 HAL UART drop-in
|
||||||
└── test/
|
└── test/
|
||||||
└── test_codec.c # host unit test (no hardware)
|
└── test_codec.c # host unit test (no hardware)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The portable-C examples all compile the *same* `src/meshcore_companion.c`; only
|
||||||
|
the byte transport differs per platform.
|
||||||
|
|
||||||
The Arduino/PlatformIO build only compiles `src/` (the manifests' `srcFilter` is
|
The Arduino/PlatformIO build only compiles `src/` (the manifests' `srcFilter` is
|
||||||
scoped there), so the Linux example and host test never enter a firmware build.
|
scoped there), so the Linux example and host test never enter a firmware build.
|
||||||
The CMake build only compiles the pure-C parts and ignores the Arduino wrapper
|
The CMake build only compiles the pure-C parts and ignores the Arduino wrapper
|
||||||
@@ -84,6 +95,26 @@ cc -std=c99 -Wall -Wextra -Isrc \
|
|||||||
examples-linux/tty_bridge/meshcore_tty.c src/meshcore_companion.c -o meshcore_tty
|
examples-linux/tty_bridge/meshcore_tty.c src/meshcore_companion.c -o meshcore_tty
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Other platform examples (same C core)
|
||||||
|
|
||||||
|
Each example compiles `src/meshcore_companion.c` directly and supplies only a
|
||||||
|
platform-specific UART transport — no copy of the core is kept anywhere.
|
||||||
|
|
||||||
|
- **ESP-IDF** — `examples-esp-idf/tty_bridge/`. A full IDF project using the UART
|
||||||
|
driver:
|
||||||
|
```sh
|
||||||
|
cd examples-esp-idf/tty_bridge
|
||||||
|
idf.py set-target esp32s3 && idf.py build flash monitor
|
||||||
|
```
|
||||||
|
- **STM32** — `examples-stm32/uart_bridge/`. A HAL drop-in (STM32 builds are
|
||||||
|
board/toolchain specific): add `meshcore_stm32.c` + `src/` to a CubeMX/CubeIDE
|
||||||
|
project and call `meshcore_setup()` / `meshcore_poll()` from `main()`. See its
|
||||||
|
README.
|
||||||
|
|
||||||
|
The transport is just two operations — *write bytes* and *read available bytes* —
|
||||||
|
so porting to bare-metal STM32, nRF52 (nRF5 SDK or Zephyr), or any other MCU only
|
||||||
|
changes those calls; the protocol code is identical.
|
||||||
|
|
||||||
## Use the Arduino library
|
## Use the Arduino library
|
||||||
|
|
||||||
Install via PlatformIO (`lib_deps = symlink:///path/to/meshcore_c`), the Arduino
|
Install via PlatformIO (`lib_deps = symlink:///path/to/meshcore_c`), the Arduino
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Project-level CMake for the ESP-IDF build system.
|
||||||
|
# Build with: idf.py set-target esp32s3 && idf.py build flash monitor
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(meshcore_tty_bridge)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# ESP-IDF example: tty_bridge
|
||||||
|
|
||||||
|
Drives a MeshCore Companion Radio over a hardware UART using the ESP-IDF UART
|
||||||
|
driver. The protocol logic is the repo's portable C core
|
||||||
|
(`src/meshcore_companion.c`) — this example only supplies the transport and an
|
||||||
|
`app_main()` loop.
|
||||||
|
|
||||||
|
## Wiring
|
||||||
|
|
||||||
|
| Companion radio | ESP32 (default pins) |
|
||||||
|
|-----------------|----------------------|
|
||||||
|
| TX → host RX | GPIO16 (`MC_UART_RX_PIN`) |
|
||||||
|
| RX ← host TX | GPIO17 (`MC_UART_TX_PIN`) |
|
||||||
|
| GND | GND |
|
||||||
|
|
||||||
|
Change the pins/UART at the top of `main/main.c`. The radio must run the serial
|
||||||
|
companion firmware (`companion_radio_usb`) bound to this UART.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Requires an installed [ESP-IDF](https://docs.espressif.com/projects/esp-idf/)
|
||||||
|
(v5.x). From this directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
idf.py set-target esp32s3 # or esp32, esp32c3, ...
|
||||||
|
idf.py build flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
`main/CMakeLists.txt` compiles `../../../src/meshcore_companion.c` directly, so
|
||||||
|
there is no copy of the core to keep in sync.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Compile the portable C core straight from the repo's src/ -- no copy, no
|
||||||
|
# duplication. The path is relative to this component directory:
|
||||||
|
# examples-esp-idf/tty_bridge/main -> ../../../src
|
||||||
|
set(MC_CORE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../src")
|
||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS "main.c" "${MC_CORE_DIR}/meshcore_companion.c"
|
||||||
|
INCLUDE_DIRS "." "${MC_CORE_DIR}")
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* main.c -- ESP-IDF example: drive a MeshCore Companion Radio over a UART.
|
||||||
|
*
|
||||||
|
* Same portable C99 core as every other example (src/meshcore_companion.{c,h}),
|
||||||
|
* here with the ESP-IDF UART driver as the byte transport. Wire the companion
|
||||||
|
* radio's serial lines to MC_UART_TX_PIN / MC_UART_RX_PIN and flash this onto an
|
||||||
|
* ESP32 / ESP32-S3 / ESP32-C3 etc.
|
||||||
|
*
|
||||||
|
* idf.py set-target esp32s3
|
||||||
|
* idf.py build flash monitor
|
||||||
|
*
|
||||||
|
* The companion radio must run the serial companion firmware (companion_radio_usb)
|
||||||
|
* with its interface bound to this UART.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
* Author: Scott Penrose / Digital Dimensions.
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include "meshcore_companion.h"
|
||||||
|
|
||||||
|
/* --- UART wiring: change to free pins on your board --- */
|
||||||
|
#define MC_UART_NUM UART_NUM_1
|
||||||
|
#define MC_UART_TX_PIN 17 /* host TX -> companion RX */
|
||||||
|
#define MC_UART_RX_PIN 16 /* host RX <- companion TX */
|
||||||
|
#define MC_UART_BAUD 115200
|
||||||
|
#define MC_UART_BUFSZ 1024
|
||||||
|
|
||||||
|
static const char *TAG = "meshcore";
|
||||||
|
|
||||||
|
static void send_payload(const uint8_t *payload, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t frame[MC_RX_BUFSZ];
|
||||||
|
size_t flen = mc_frame_encode(payload, len, frame, sizeof frame);
|
||||||
|
if (flen) uart_write_bytes(MC_UART_NUM, (const char *)frame, flen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_event(const mc_event_t *ev)
|
||||||
|
{
|
||||||
|
switch (ev->code) {
|
||||||
|
case MC_RESP_DEVICE_INFO:
|
||||||
|
ESP_LOGI(TAG, "radio model=%s fw=%d channels=%u build=%s",
|
||||||
|
ev->u.device_info.model, ev->u.device_info.fw_ver,
|
||||||
|
(unsigned)ev->u.device_info.max_channels,
|
||||||
|
ev->u.device_info.build_date);
|
||||||
|
break;
|
||||||
|
case MC_RESP_CHANNEL_MSG_RECV: /* body is "SenderName: message" */
|
||||||
|
ESP_LOGI(TAG, "[ch %d] %s", ev->u.channel_msg.channel_idx,
|
||||||
|
ev->u.channel_msg.text);
|
||||||
|
break;
|
||||||
|
case MC_RESP_CHANNEL_DATA_RECV: {
|
||||||
|
/* SNR is q4 (x4). Print exact dB without floating-point printf. */
|
||||||
|
int centi = ev->u.channel_data.snr_q4 * 25; /* /4 then *100 */
|
||||||
|
const char *sign = centi < 0 ? "-" : "";
|
||||||
|
ESP_LOGI(TAG, "[ch %d] %u bytes type=0x%04X snr=%s%d.%02d dB %s",
|
||||||
|
ev->u.channel_data.channel_idx,
|
||||||
|
(unsigned)ev->u.channel_data.data_len,
|
||||||
|
(unsigned)ev->u.channel_data.data_type,
|
||||||
|
sign, abs(centi) / 100, abs(centi) % 100,
|
||||||
|
ev->u.channel_data.path_len == MC_PATH_DIRECT ? "direct" : "flood");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MC_RESP_CURR_TIME:
|
||||||
|
ESP_LOGI(TAG, "device time = %u (epoch secs)", (unsigned)ev->u.curr_time);
|
||||||
|
break;
|
||||||
|
case MC_RESP_ERR:
|
||||||
|
ESP_LOGW(TAG, "radio error response (code=%d)", ev->u.err_code);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
const uart_config_t cfg = {
|
||||||
|
.baud_rate = MC_UART_BAUD,
|
||||||
|
.data_bits = UART_DATA_8_BITS,
|
||||||
|
.parity = UART_PARITY_DISABLE,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||||
|
.source_clk = UART_SCLK_DEFAULT,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(uart_driver_install(MC_UART_NUM, MC_UART_BUFSZ, 0, 0, NULL, 0));
|
||||||
|
ESP_ERROR_CHECK(uart_param_config(MC_UART_NUM, &cfg));
|
||||||
|
ESP_ERROR_CHECK(uart_set_pin(MC_UART_NUM, MC_UART_TX_PIN, MC_UART_RX_PIN,
|
||||||
|
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||||
|
|
||||||
|
/* Handshake: AppStart -> SelfInfo, DeviceQuery -> DeviceInfo. */
|
||||||
|
uint8_t cmd[MC_MAX_PAYLOAD];
|
||||||
|
size_t n;
|
||||||
|
n = mc_cmd_app_start(cmd, sizeof cmd, "esp-idf"); if (n) send_payload(cmd, n);
|
||||||
|
n = mc_cmd_device_query(cmd, sizeof cmd, 1); if (n) send_payload(cmd, n);
|
||||||
|
n = mc_cmd_get_device_time(cmd, sizeof cmd); if (n) send_payload(cmd, n);
|
||||||
|
|
||||||
|
mc_rx_t rx;
|
||||||
|
mc_rx_init(&rx);
|
||||||
|
ESP_LOGI(TAG, "listening on UART%d (rx=%d tx=%d)",
|
||||||
|
MC_UART_NUM, MC_UART_RX_PIN, MC_UART_TX_PIN);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
uint8_t in[128];
|
||||||
|
int got = uart_read_bytes(MC_UART_NUM, in, sizeof in, pdMS_TO_TICKS(100));
|
||||||
|
if (got <= 0) continue;
|
||||||
|
|
||||||
|
mc_rx_feed(&rx, in, (size_t)got);
|
||||||
|
|
||||||
|
uint8_t payload[MC_MAX_PAYLOAD];
|
||||||
|
size_t plen;
|
||||||
|
while (mc_rx_poll(&rx, payload, sizeof payload, &plen)) {
|
||||||
|
mc_event_t ev;
|
||||||
|
if (!mc_parse(payload, plen, &ev)) continue;
|
||||||
|
on_event(&ev);
|
||||||
|
/* Drain the radio's queue when it signals waiting messages. */
|
||||||
|
if (ev.code == MC_PUSH_MSG_WAITING ||
|
||||||
|
ev.code == MC_RESP_CHANNEL_MSG_RECV ||
|
||||||
|
ev.code == MC_RESP_CHANNEL_DATA_RECV ||
|
||||||
|
ev.code == MC_RESP_CONTACT_MSG_RECV) {
|
||||||
|
n = mc_cmd_sync_next_message(cmd, sizeof cmd);
|
||||||
|
if (n) send_payload(cmd, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# STM32 example: uart_bridge
|
||||||
|
|
||||||
|
Integration example showing how to drive a MeshCore Companion Radio from an STM32
|
||||||
|
using the CubeMX-generated HAL. The protocol logic is the repo's portable C core
|
||||||
|
(`src/meshcore_companion.c`); this file supplies only the UART transport and two
|
||||||
|
entry points you call from your generated `main()`.
|
||||||
|
|
||||||
|
Because an STM32 firmware build is tied to your specific MCU, clocks, pins and
|
||||||
|
linker script (all produced by STM32CubeIDE / CubeMX), this is **not** a
|
||||||
|
standalone buildable project — it is a drop-in.
|
||||||
|
|
||||||
|
## Steps (STM32CubeIDE / CubeMX)
|
||||||
|
|
||||||
|
1. Generate a project with one USART enabled at **115200 8N1** (e.g. `USART1`).
|
||||||
|
Wire it to the companion radio: host TX → radio RX, host RX ← radio TX, GND↔GND.
|
||||||
|
The radio must run the serial companion firmware (`companion_radio_usb`).
|
||||||
|
2. Add `src/meshcore_companion.c` and `src/meshcore_companion.h` from this repo to
|
||||||
|
your project (or add this repo's `src/` to the include paths).
|
||||||
|
3. Add `meshcore_stm32.c` to your project. If your USART handle isn't `huart1`,
|
||||||
|
change the `extern UART_HandleTypeDef huart1;` line near the top.
|
||||||
|
4. In the generated `main.c`:
|
||||||
|
```c
|
||||||
|
/* USER CODE BEGIN 2 */
|
||||||
|
meshcore_setup();
|
||||||
|
/* USER CODE END 2 */
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* USER CODE BEGIN 3 */
|
||||||
|
meshcore_poll();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `meshcore_poll()` polls the UART one byte at a time, which is fine for the
|
||||||
|
companion's low data rate. For high throughput, switch the RX side to
|
||||||
|
interrupt/DMA into a ring buffer and feed that to `mc_rx_feed()` — the core
|
||||||
|
code is unchanged.
|
||||||
|
- Logging uses `printf()`. Retarget it to a **separate** debug UART or SWO/ITM
|
||||||
|
(commonly USART2 = the ST-Link VCP) by implementing `_write()`; do not point it
|
||||||
|
at the companion UART.
|
||||||
|
- The same two-function transport pattern (`send bytes` / `read available bytes`)
|
||||||
|
ports directly to bare-metal STM32, nRF52 (nRF5 SDK or Zephyr), or any other
|
||||||
|
MCU — only the HAL calls change.
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* meshcore_stm32.c -- STM32 HAL integration example.
|
||||||
|
*
|
||||||
|
* Unlike the Linux and ESP-IDF examples, STM32 builds are board- and
|
||||||
|
* toolchain-specific (the CubeMX-generated HAL, startup code and linker script
|
||||||
|
* belong to your project), so this is an *integration example* rather than a
|
||||||
|
* standalone build. It shows the only two things the portable core needs from a
|
||||||
|
* platform: write some bytes, and read whatever bytes have arrived.
|
||||||
|
*
|
||||||
|
* How to use (STM32CubeIDE / CubeMX):
|
||||||
|
* 1. Generate a project with one USART enabled at 115200 8N1 (e.g. USART1).
|
||||||
|
* Wire it to the companion radio (host TX -> radio RX, host RX <- radio TX).
|
||||||
|
* 2. Add src/meshcore_companion.c and src/meshcore_companion.h to the project
|
||||||
|
* (Core/Src and Core/Inc, or add this repo's src/ to the include paths).
|
||||||
|
* 3. Add this file to the project.
|
||||||
|
* 4. In the generated main(): call meshcore_setup() once after MX_USARTx_UART_Init(),
|
||||||
|
* then call meshcore_poll() every iteration of the main while(1) loop.
|
||||||
|
*
|
||||||
|
* Byte-at-a-time polling is fine for the companion's low data rate; for high
|
||||||
|
* throughput switch the transport to interrupt/DMA RX into a ring buffer and
|
||||||
|
* feed that buffer to mc_rx_feed() — the core code does not change.
|
||||||
|
*
|
||||||
|
* Logging here uses printf(); retarget it to a *separate* debug UART or SWO/ITM
|
||||||
|
* (do not point it at the companion UART). On many CubeIDE projects that means
|
||||||
|
* implementing _write() to HAL_UART_Transmit on USART2 (the ST-Link VCP).
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
* Author: Scott Penrose / Digital Dimensions.
|
||||||
|
*/
|
||||||
|
#include "main.h" /* CubeMX-generated: pulls in stm32xxxx_hal.h + handles */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "meshcore_companion.h"
|
||||||
|
|
||||||
|
/* The USART you enabled in CubeMX and wired to the companion radio. */
|
||||||
|
extern UART_HandleTypeDef huart1;
|
||||||
|
#define MC_UART (&huart1)
|
||||||
|
|
||||||
|
static mc_rx_t s_rx;
|
||||||
|
|
||||||
|
static void send_payload(const uint8_t *payload, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t frame[MC_RX_BUFSZ];
|
||||||
|
size_t flen = mc_frame_encode(payload, len, frame, sizeof frame);
|
||||||
|
if (flen) HAL_UART_Transmit(MC_UART, frame, (uint16_t)flen, HAL_MAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_event(const mc_event_t *ev)
|
||||||
|
{
|
||||||
|
switch (ev->code) {
|
||||||
|
case MC_RESP_DEVICE_INFO:
|
||||||
|
printf("radio model=%s fw=%d channels=%u build=%s\r\n",
|
||||||
|
ev->u.device_info.model, ev->u.device_info.fw_ver,
|
||||||
|
(unsigned)ev->u.device_info.max_channels,
|
||||||
|
ev->u.device_info.build_date);
|
||||||
|
break;
|
||||||
|
case MC_RESP_CHANNEL_MSG_RECV: /* body is "SenderName: message" */
|
||||||
|
printf("[ch %d] %s\r\n", ev->u.channel_msg.channel_idx,
|
||||||
|
ev->u.channel_msg.text);
|
||||||
|
break;
|
||||||
|
case MC_RESP_CHANNEL_DATA_RECV: {
|
||||||
|
int centi = ev->u.channel_data.snr_q4 * 25; /* q4 -> /4 then *100 */
|
||||||
|
const char *sign = centi < 0 ? "-" : "";
|
||||||
|
printf("[ch %d] %u bytes type=0x%04X snr=%s%d.%02d dB %s\r\n",
|
||||||
|
ev->u.channel_data.channel_idx,
|
||||||
|
(unsigned)ev->u.channel_data.data_len,
|
||||||
|
(unsigned)ev->u.channel_data.data_type,
|
||||||
|
sign, abs(centi) / 100, abs(centi) % 100,
|
||||||
|
ev->u.channel_data.path_len == MC_PATH_DIRECT ? "direct" : "flood");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MC_RESP_CURR_TIME:
|
||||||
|
printf("device time = %u (epoch secs)\r\n", (unsigned)ev->u.curr_time);
|
||||||
|
break;
|
||||||
|
case MC_RESP_ERR:
|
||||||
|
printf("radio error response (code=%d)\r\n", ev->u.err_code);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call once after MX_USARTx_UART_Init(). */
|
||||||
|
void meshcore_setup(void)
|
||||||
|
{
|
||||||
|
mc_rx_init(&s_rx);
|
||||||
|
|
||||||
|
uint8_t cmd[MC_MAX_PAYLOAD];
|
||||||
|
size_t n;
|
||||||
|
n = mc_cmd_app_start(cmd, sizeof cmd, "stm32"); if (n) send_payload(cmd, n);
|
||||||
|
n = mc_cmd_device_query(cmd, sizeof cmd, 1); if (n) send_payload(cmd, n);
|
||||||
|
n = mc_cmd_get_device_time(cmd, sizeof cmd); if (n) send_payload(cmd, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call from your main while(1) loop. Non-blocking. */
|
||||||
|
void meshcore_poll(void)
|
||||||
|
{
|
||||||
|
uint8_t cmd[MC_MAX_PAYLOAD];
|
||||||
|
size_t n;
|
||||||
|
uint8_t b;
|
||||||
|
|
||||||
|
/* Drain every byte currently available (timeout 0 = return immediately). */
|
||||||
|
while (HAL_UART_Receive(MC_UART, &b, 1, 0) == HAL_OK) {
|
||||||
|
mc_rx_feed(&s_rx, &b, 1);
|
||||||
|
|
||||||
|
uint8_t payload[MC_MAX_PAYLOAD];
|
||||||
|
size_t plen;
|
||||||
|
while (mc_rx_poll(&s_rx, payload, sizeof payload, &plen)) {
|
||||||
|
mc_event_t ev;
|
||||||
|
if (!mc_parse(payload, plen, &ev)) continue;
|
||||||
|
on_event(&ev);
|
||||||
|
if (ev.code == MC_PUSH_MSG_WAITING ||
|
||||||
|
ev.code == MC_RESP_CHANNEL_MSG_RECV ||
|
||||||
|
ev.code == MC_RESP_CHANNEL_DATA_RECV ||
|
||||||
|
ev.code == MC_RESP_CONTACT_MSG_RECV) {
|
||||||
|
n = mc_cmd_sync_next_message(cmd, sizeof cmd);
|
||||||
|
if (n) send_payload(cmd, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user