Quickstart¶
Read flow from a BASIS-2 on your MCU: wire the bus and power, write a small port class, call two driver functions.
Power goes to pin 4 only
The manual warns that power on any pin other than pin 4 can permanently damage the instrument. Wire every signal before you switch on the 12 V to 24 V supply.
1. Wire it¶
The usual setup is RS-485 through a half-duplex transceiver; the instrument's model decides whether pins 5 and 6 are RS-485 or RS-232.
| From | To | Notes |
|---|---|---|
| MCU UART TX | Transceiver DI | 8N1, 38400 baud out of the factory |
| MCU UART RX | Transceiver RO | |
| MCU GPIO or RTS | Transceiver DE and RE, tied | High while the MCU transmits |
| Transceiver A | BASIS-2 pin 6, A (−) | 6-pin JST-GH on top |
| Transceiver B | BASIS-2 pin 5, B (+) | |
| MCU GND | Transceiver GND, BASIS-2 pin 3 | One common ground |
| 12 V to 24 V DC | BASIS-2 pin 4 | Last, after everything else |
The Hardware page has the RS-232 wiring, the bus settings and the power figures.
2. Add the driver¶
Add the repository as a submodule and link the hf::alicat_basis2 CMake target (or put inc/ on the include
path and build with C++17). Installation covers CMake and ESP-IDF.
git submodule add https://github.com/hardfoc/hf-alicat-basis2-driver.git external/hf-alicat-basis2-driver
3. Write the port¶
The port is the only platform code. It writes bytes, reads bytes with a timeout and flushes the receive buffer.
Replace the platform_* calls with your SDK's.
class MyUart : public alicat_basis2::UartInterface<MyUart> {
public:
// Send a whole Modbus request; return once the last byte is on the wire.
void write(const uint8_t* data, std::size_t length) noexcept {
platform_uart_write(data, length);
}
// Wait for up to `max` bytes; return early only when all of them arrived.
std::size_t read(uint8_t* out, std::size_t max, uint32_t timeout_ms) noexcept {
return platform_uart_read(out, max, timeout_ms);
}
// Drop stale bytes (a late reply, line noise) before the next request.
void flush_rx() noexcept { platform_uart_flush_rx(); }
// Optional: a real sleep (at least `ms`) for UartInterface::delay_ms().
void delay_ms_impl(uint32_t ms) noexcept { platform_delay_ms(ms); }
// Optional: where the driver's failure logs go (printf-style).
void log_impl(const char* tag, const char* fmt, ...) noexcept {
va_list ap;
va_start(ap, fmt);
platform_log(tag, fmt, ap);
va_end(ap);
}
};
read() must wait until max bytes have arrived or the timeout has passed, then return what it has.
Porting explains each method and the RS-485 direction pin.
4. Read flow¶
#include "alicat_basis2.hpp"
#include <cstdio>
int read_flow_once(MyUart& uart) {
alicat_basis2::Driver<MyUart> meter(uart, 1); // Modbus address 1, 200 ms timeout
const auto id = meter.ReadIdentity(); // 1. who is on the bus
if (!id.ok()) {
std::printf("no reply: %s\n", alicat_basis2::ToString(id.error).data());
return -1; // wiring, baud rate or address
}
const auto live = meter.ReadInstantaneous(3); // 2. one request: registers 2100..2109
if (!live.ok()) {
return -1;
}
std::printf("SN %s %s flow %.3f %s %.2f C\n", id.value.serial_number,
alicat_basis2::GasShortName(live.value.gas).data(), static_cast<double>(live.value.flow),
alicat_basis2::ToString(id.value.flow_units).data(),
static_cast<double>(live.value.temperature_c));
return 0;
}
What each step does:
Driver<MyUart>(uart, 1)targets Modbus address 1 with the default 200 ms response timeout. Nothing is sent yet.ReadIdentity()reads the firmware version, serial number, address, unit ID, full scale and flow units. A reply proves the wiring, the baud rate and the address.ReadInstantaneous(3)reads registers 2100 to 2109 in one request. The3is the number of decimals the instrument's flow registers carry; see API guide.
This example is built by the host CI, so it compiles against the current headers.
If it does not work¶
| Symptom | First thing to check |
|---|---|
Timeout on every call |
Baud rate (38400 default), A and B swapped, DE/RE not driven, wrong address |
BadCrc |
A missing common ground, termination on a long bus, two instruments at one address |
flow is 10 or 100 times off |
The decimals passed to ReadInstantaneous() (API guide) |
| The LED blinks red fast | Mass flow, temperature or totalizer over range (Troubleshooting) |
Next¶
- API guide: setpoints, taring, the totalizer, errors.
- Examples: complete ESP32-S3 applications.
- Modbus-RTU protocol: frames, registers and what each call sends.