Skip to content

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.

my_uart.hpp
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

main.cpp
#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:

  1. Driver<MyUart>(uart, 1) targets Modbus address 1 with the default 200 ms response timeout. Nothing is sent yet.
  2. 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.
  3. ReadInstantaneous(3) reads registers 2100 to 2109 in one request. The 3 is 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