Alicat BASIS-2 Driver 1.0.0
API reference
Loading...
Searching...
No Matches
alicat_basis2.hpp
Go to the documentation of this file.
1
39#pragma once
40
45#include "alicat_basis2_version.h"
46
47#include <array>
48#include <cstdint>
49#include <cstddef>
50#include <cstring>
51
52namespace alicat_basis2 {
53
55inline const char* GetDriverVersion() noexcept {
56 return HF_ALICAT_BASIS2_VERSION_STRING;
57}
58
69template <typename UartT>
70class Driver {
71public:
81 Driver(UartT& uart, uint8_t address,
82 uint16_t timeout_ms = kDefaultTimeoutMs) noexcept
83 : uart_(uart), address_(address), timeout_ms_(timeout_ms) {}
84
85 Driver(const Driver&) = delete;
86 Driver& operator=(const Driver&) = delete;
87
89 void SetTimeoutMs(uint16_t ms) noexcept { timeout_ms_ = ms; }
91 uint16_t GetTimeoutMs() const noexcept { return timeout_ms_; }
92
103 void SetBusBaudRate(uint32_t bps) noexcept {
104 bus_bps_ = bps;
105 }
107 uint32_t GetBusBaudRate() const noexcept {
108 return bus_bps_;
109 }
111 uint32_t GetInterFrameGapUs() const noexcept {
112 return modbus::InterFrameGapUs(bus_bps_);
113 }
114
116 void SetAddress(uint8_t addr) noexcept { address_ = addr; }
118 uint8_t GetAddress() const noexcept { return address_; }
119
120 //==========================================================================
121 // INSTANTANEOUS DATA — single Modbus burst
122 //==========================================================================
123
139 DriverResult<InstantaneousData> ReadInstantaneous(uint8_t flow_decimals = 3) noexcept {
140 std::array<uint16_t, 10> regs{};
141 const auto rc = ReadHoldingRegisters(reg::SelectedGas, regs.data(),
142 static_cast<uint8_t>(regs.size()));
144
145 InstantaneousData out{};
146 out.gas = static_cast<Gas>(regs[0] & 0xFFU);
147
148 const uint16_t status = regs[1];
149 out.mass_flow_overrange = (status & 0x0001U) != 0;
150 out.temperature_overrange = (status & 0x0002U) != 0;
151 out.totalizer_overrange = (status & 0x0004U) != 0;
152 out.valve_held = (status & 0x0008U) != 0;
153 out.valve_thermal_management = (status & 0x0010U) != 0;
154
155 out.raw_temp_2102 = static_cast<int16_t>(regs[2]);
156 out.temperature_c = static_cast<float>(out.raw_temp_2102) * 0.01f;
157
158 out.raw_flow_2103 = static_cast<int16_t>(regs[3]);
159 out.flow = ScaleFlow(out.raw_flow_2103, flow_decimals);
160
161 out.raw_total_volume_2104_2105 =
162 (static_cast<uint32_t>(regs[4]) << 16) | regs[5];
163 out.total_volume = ScaleFlow(static_cast<int32_t>(out.raw_total_volume_2104_2105),
164 flow_decimals);
165
166 out.raw_setpoint_2053_2054 = static_cast<int16_t>(regs[6]);
167 out.setpoint = ScaleFlow(out.raw_setpoint_2053_2054, flow_decimals);
168
169 out.valve_drive_pct = static_cast<float>(static_cast<int16_t>(regs[7])) * 0.01f;
170
171 out.raw_batch_remaining_2108_2109 =
172 (static_cast<uint32_t>(regs[8]) << 16) | regs[9];
173 out.batch_remaining = ScaleFlow(static_cast<int32_t>(out.raw_batch_remaining_2108_2109),
174 flow_decimals);
175
177 }
178
179 //==========================================================================
180 // INSTRUMENT IDENTITY (slow registers; read once at init)
181 //==========================================================================
182
195
196 // Firmware version
197 std::array<uint16_t, 1> fw{};
198 if (auto rc = ReadHoldingRegisters(reg::FirmwareVersion, fw.data(), 1);
199 rc != DriverError::None) {
201 }
202 id.firmware_version_raw = fw[0];
203 id.fw_major = static_cast<uint8_t>((fw[0] >> 8) & 0xFFU);
204 id.fw_minor = static_cast<uint8_t>((fw[0] >> 4) & 0x0FU);
205 id.fw_patch = static_cast<uint8_t>(fw[0] & 0x0FU);
206
207 // Serial number
208 std::array<uint16_t, reg::SerialNumberCount> sn{};
209 if (auto rc = ReadHoldingRegisters(reg::SerialNumberStart, sn.data(),
211 rc != DriverError::None) {
213 }
214 for (std::size_t i = 0; i < reg::SerialNumberCount; ++i) {
215 id.serial_number[i * 2] = static_cast<char>((sn[i] >> 8) & 0xFFU);
216 id.serial_number[i * 2 + 1] = static_cast<char>(sn[i] & 0xFFU);
217 }
218 id.serial_number[12] = '\0';
219
220 // Address + unit id + flow units
221 std::array<uint16_t, 5> tail{}; // 45..49
222 if (auto rc = ReadHoldingRegisters(reg::ModbusAddress, tail.data(),
223 static_cast<uint8_t>(tail.size()));
224 rc != DriverError::None) {
226 }
227 id.modbus_address = static_cast<uint8_t>(tail[0] & 0xFFU);
228 id.ascii_unit_id = static_cast<char>(tail[1] & 0xFFU);
229 id.full_scale_flow_raw =
230 (static_cast<uint32_t>(tail[2]) << 16) | tail[3];
231 id.flow_units = static_cast<FlowUnits>(tail[4] & 0xFFU);
233 }
234
235 //==========================================================================
236 // SETPOINT (controllers only)
237 //==========================================================================
238
254 DriverResult<void> SetSetpoint(float value_in_user_units, uint8_t flow_decimals = 3) noexcept {
255 const int32_t scaled = static_cast<int32_t>(
256 value_in_user_units * Pow10(flow_decimals + 0)); // datasheet: × 1000 always
257 // The datasheet specifies value/1000 for the scaled register regardless of decimals.
258 const int32_t raw = static_cast<int32_t>(value_in_user_units * 1000.0f);
259 std::array<uint16_t, 2> regs = {
260 static_cast<uint16_t>((raw >> 16) & 0xFFFFU),
261 static_cast<uint16_t>(raw & 0xFFFFU),
262 };
263 (void)scaled;
264 return WriteMultipleRegisters(reg::SetpointCommand, regs.data(), 2);
265 }
266
273 return WriteSingleRegister(reg::SetpointSource, static_cast<uint16_t>(src));
274 }
275
289
290 DriverResult<void> SetMaxSetpointRamp(uint32_t pct_per_ms_x_10e7) noexcept {
291 std::array<uint16_t, 2> regs = {
292 static_cast<uint16_t>((pct_per_ms_x_10e7 >> 16) & 0xFFFFU),
293 static_cast<uint16_t>(pct_per_ms_x_10e7 & 0xFFFFU),
294 };
295 return WriteMultipleRegisters(reg::MaxSetpointRamp, regs.data(), 2);
296 }
297
298 //==========================================================================
299 // TARING / AUTOTARE
300 //==========================================================================
301
313
322 DriverResult<void> SetAutotareEnabled(bool enabled) noexcept {
323 return WriteSingleRegister(reg::AutotareEnable, enabled ? 1U : 0U);
324 }
325
326 //==========================================================================
327 // GAS SELECTION
328 //==========================================================================
329
337 return WriteSingleRegister(reg::SelectedGas, static_cast<uint16_t>(g));
338 }
339
340 //==========================================================================
341 // TOTALIZER
342 //==========================================================================
343
355
362 return WriteSingleRegister(reg::TotalizerLimitMode, static_cast<uint16_t>(mode));
363 }
364
375 DriverResult<void> SetTotalizerBatch(uint32_t value_scaled) noexcept {
376 std::array<uint16_t, 2> regs = {
377 static_cast<uint16_t>((value_scaled >> 16) & 0xFFFFU),
378 static_cast<uint16_t>(value_scaled & 0xFFFFU),
379 };
381 }
382
383 //==========================================================================
384 // FLOW PROCESSING
385 //==========================================================================
386
397
404 if (c < 0.0f || c > 30.0f) return DriverResult<void>::failure(DriverError::InvalidParameter);
405 const uint16_t raw = static_cast<uint16_t>(c * 100.0f);
407 }
408
409 //==========================================================================
410 // MEASUREMENT WINDOW (datasheet "Configure Measurement Triggering")
411 //==========================================================================
412
420 DriverResult<void> ConfigureMeasurementTrigger(uint16_t trigger_bits) noexcept {
422 }
423
434 DriverResult<void> StartMeasurementSamples(uint16_t num_samples) noexcept {
436 }
437
453 DriverResult<MeasurementData> ReadMeasurement(uint8_t flow_decimals = 3) noexcept {
454 constexpr uint16_t kFirst = reg::MeasurementSampleCount; // 4201
455 constexpr uint16_t kLast = reg::MeasurementPrevAvgFlow + reg::kPrevAvgFlowCount - 1; // 4215
456 std::array<uint16_t, kLast - kFirst + 1> regs{};
457 const auto rc = ReadHoldingRegisters(kFirst, regs.data(), static_cast<uint8_t>(regs.size()));
458 if (rc != DriverError::None)
460 const auto at = [&regs](uint16_t r) { return regs[r - kFirst]; };
461
462 MeasurementData out{};
464 out.num_samples_collected = at(reg::MeasurementSamplesAveraged);
465 out.min_temperature_raw = static_cast<int16_t>(at(reg::MeasurementMinTemp));
466 out.max_temperature_raw = static_cast<int16_t>(at(reg::MeasurementMaxTemp));
467 out.min_flow_raw = static_cast<int16_t>(at(reg::MeasurementMinFlow));
468 out.max_flow_raw = static_cast<int16_t>(at(reg::MeasurementMaxFlow));
469 out.avg_temperature_raw = static_cast<int16_t>(at(reg::MeasurementAvgTemp));
470 out.avg_flow_raw = static_cast<int16_t>(at(reg::MeasurementAvgFlow));
471 out.prev_avg_flow_raw =
472 static_cast<int32_t>((static_cast<uint32_t>(at(reg::MeasurementPrevAvgFlow)) << 16) |
474 out.min_temperature_c = static_cast<float>(out.min_temperature_raw) * 0.01f;
475 out.max_temperature_c = static_cast<float>(out.max_temperature_raw) * 0.01f;
476 out.avg_temperature_c = static_cast<float>(out.avg_temperature_raw) * 0.01f;
477 out.avg_flow = ScaleFlow(out.avg_flow_raw, flow_decimals);
479 }
480
481 //==========================================================================
482 // BUS / IDENTITY RECONFIGURATION
483 //==========================================================================
484
497 DriverResult<void> SetModbusAddress(uint8_t new_address) noexcept {
498 if (new_address < 1 || new_address > kMaxModbusAddress) {
500 }
501 const auto rc = WriteSingleRegister(reg::ModbusAddress, new_address);
502 if (rc.ok()) address_ = new_address;
503 return rc;
504 }
505
514 if (id < 'A' || id > 'Z') {
516 }
517 return WriteSingleRegister(reg::AsciiUnitId, static_cast<uint16_t>(id));
518 }
519
538 const auto rc = WriteSingleRegister(reg::BaudRate, static_cast<uint16_t>(br));
539 bus_bps_ = BaudRateToBps(br);
540 return rc;
541 }
542
554
555 //==========================================================================
556 // BUS DISCOVERY
557 //==========================================================================
558
571 std::size_t bitmap_bytes,
572 uint16_t probe_timeout_ms = 30,
573 uint8_t first_addr = 1,
574 uint8_t last_addr = kMaxModbusAddress) noexcept {
575 if (!present_bitmap || bitmap_bytes < 32) {
577 }
578 std::memset(present_bitmap, 0, bitmap_bytes);
579
580 const uint8_t saved_addr = address_;
581 const uint16_t saved_timeout = timeout_ms_;
582 timeout_ms_ = probe_timeout_ms;
583 quiet_ = true; // silent addresses are expected: do not log each timeout
584
585 uint8_t found = 0;
586 for (uint16_t a = first_addr; a <= last_addr; ++a) {
587 address_ = static_cast<uint8_t>(a);
588 std::array<uint16_t, 1> dummy{};
589 if (ReadHoldingRegisters(reg::FirmwareVersion, dummy.data(), 1) ==
591 present_bitmap[a / 8] |= static_cast<uint8_t>(1U << (a % 8));
592 ++found;
593 }
594 }
595
596 address_ = saved_addr;
597 timeout_ms_ = saved_timeout;
598 quiet_ = false;
599 return DriverResult<uint8_t>::success(found);
600 }
601
602 //==========================================================================
603 // RAW MODBUS PRIMITIVES
604 //==========================================================================
605
612 DriverError ReadHoldingRegisters(uint16_t reg_addr, uint16_t* out, uint8_t count) noexcept {
613 if (!out || count == 0 || count > 125) return DriverError::InvalidParameter;
614 std::array<uint8_t, 8> tx{};
615 const std::size_t tx_len = modbus::BuildReadHolding(address_, reg_addr, count,
616 tx.data(), tx.size());
617 if (tx_len == 0) return DriverError::BufferTooSmall;
618
619 // Expected response: addr(1) + fn(1) + bytecount(1) + payload(2*count) + crc(2)
620 const std::size_t need = 5 + 2U * count;
621 std::array<uint8_t, 5 + 2 * 32> rx{}; // up to 32 regs per call
622 if (need > rx.size()) return DriverError::BufferTooSmall;
623
624 SendRequest(tx.data(), tx_len);
625 std::size_t got = 0;
626 DriverError rc = ReceiveReply(reg::fn::ReadHolding, rx.data(), need, got);
627 if (rc == DriverError::None) {
628 rc = modbus::ParseReadHolding(rx.data(), got, address_, count, out);
629 }
630 return LogFailure(rc, reg::fn::ReadHolding, reg_addr);
631 }
632
639 DriverResult<void> WriteSingleRegister(uint16_t reg_addr, uint16_t value) noexcept {
640 std::array<uint8_t, 8> tx{};
641 const std::size_t tx_len = modbus::BuildWriteSingle(address_, reg_addr, value,
642 tx.data(), tx.size());
644
645 SendRequest(tx.data(), tx_len);
646
647 // Broadcast → no response.
648 if (address_ == kBroadcastAddress) return DriverResult<void>::success();
649
650 std::array<uint8_t, 8> rx{};
651 std::size_t got = 0;
652 DriverError rc = ReceiveReply(reg::fn::WriteSingle, rx.data(), rx.size(), got);
653 if (rc == DriverError::None) {
654 rc = modbus::ParseWriteSingleEcho(rx.data(), got, tx.data(), tx_len);
655 }
656 return DriverResult<void>{LogFailure(rc, reg::fn::WriteSingle, reg_addr)};
657 }
658
666 const uint16_t* regs, uint8_t count) noexcept {
667 if (!regs || count == 0 || count > 32) {
669 }
670 std::array<uint8_t, 9 + 32 * 2> tx{};
671 const std::size_t tx_len = modbus::BuildWriteMultiple(address_, reg_addr, regs, count,
672 tx.data(), tx.size());
674
675 SendRequest(tx.data(), tx_len);
676
677 if (address_ == kBroadcastAddress) return DriverResult<void>::success();
678
679 std::array<uint8_t, 8> rx{};
680 std::size_t got = 0;
681 DriverError rc = ReceiveReply(reg::fn::WriteMultiple, rx.data(), rx.size(), got);
682 if (rc == DriverError::None) {
683 rc = modbus::ParseWriteMultipleEcho(rx.data(), got, address_, reg_addr, count);
684 }
685 return DriverResult<void>{LogFailure(rc, reg::fn::WriteMultiple, reg_addr)};
686 }
687
695 return last_exception_;
696 }
697
698private:
700 static constexpr const char* kLogTag = "alicat_basis2";
701
703 DriverError LogFailure(DriverError rc, uint8_t fn, uint16_t reg_addr) noexcept {
704 if (rc == DriverError::None || quiet_) {
705 return rc;
706 }
708 uart_.log(kLogTag, "addr %u fn 0x%02X reg %u: ModbusException %s",
709 static_cast<unsigned>(address_), static_cast<unsigned>(fn),
710 static_cast<unsigned>(reg_addr), ToString(last_exception_).data());
711 } else {
712 uart_.log(kLogTag, "addr %u fn 0x%02X reg %u: %s", static_cast<unsigned>(address_),
713 static_cast<unsigned>(fn), static_cast<unsigned>(reg_addr), ToString(rc).data());
714 }
715 return rc;
716 }
717
727 void SendRequest(const uint8_t* tx, std::size_t tx_len) noexcept {
728 last_exception_ = ModbusExceptionCode::None;
729 uart_.delay_ms(modbus::InterFrameGapMs(bus_bps_));
730 uart_.flush_rx();
731 uart_.write(tx, tx_len);
732 }
733
749 DriverError ReceiveReply(uint8_t fn, uint8_t* rx, std::size_t expected_len,
750 std::size_t& got) noexcept {
751 got = uart_.read(rx, modbus::kReplyHeaderLength, timeout_ms_);
752 if (got == 0)
756 if (modbus::IsExceptionReply(rx, got)) {
757 got += uart_.read(rx + got, modbus::kExceptionFrameLength - got, timeout_ms_);
758 uint8_t code = 0;
759 const DriverError rc = modbus::ParseException(rx, got, address_, fn, &code);
761 last_exception_ = static_cast<ModbusExceptionCode>(code);
762 }
763 return rc;
764 }
765 got += uart_.read(rx + got, expected_len - got, timeout_ms_);
766 return DriverError::None;
767 }
768
769 static float Pow10(int n) noexcept {
770 float v = 1.0f;
771 for (int i = 0; i < n; ++i) v *= 10.0f;
772 return v;
773 }
774
777 static float ScaleFlow(int32_t raw, uint8_t decimals) noexcept {
778 return static_cast<float>(raw) / Pow10(decimals);
779 }
780
781 UartT& uart_;
782 uint8_t address_;
783 uint16_t timeout_ms_;
785 bool quiet_{false};
786 uint32_t bus_bps_{kDefaultBusBaudBps};
787};
788
789} // namespace alicat_basis2
Modbus-RTU framing and CRC-16 helpers used by the BASIS-2 driver.
Modbus-RTU holding-register addresses for Alicat BASIS-2.
Public type definitions for the Alicat BASIS-2 driver.
CRTP serial transport for the Alicat BASIS-2 driver.
Main BASIS-2 driver.
Definition alicat_basis2.hpp:70
ModbusExceptionCode GetLastException() const noexcept
Exception code of the last transaction's exception reply.
Definition alicat_basis2.hpp:694
uint8_t GetAddress() const noexcept
Definition alicat_basis2.hpp:118
void SetTimeoutMs(uint16_t ms) noexcept
Update the per-transaction timeout (ms).
Definition alicat_basis2.hpp:89
DriverResult< void > SetCommWatchdogMs(uint16_t ms) noexcept
Configure the communication watchdog (controllers only).
Definition alicat_basis2.hpp:285
DriverResult< void > SetSetpoint(float value_in_user_units, uint8_t flow_decimals=3) noexcept
Set the digital setpoint (controllers only).
Definition alicat_basis2.hpp:254
void SetAddress(uint8_t addr) noexcept
Switch the slave address this driver targets at runtime.
Definition alicat_basis2.hpp:116
DriverResult< void > SetFlowAveragingMs(uint16_t ms) noexcept
Configure on-chip flow averaging (IIR-style time constant).
Definition alicat_basis2.hpp:393
Driver(UartT &uart, uint8_t address, uint16_t timeout_ms=kDefaultTimeoutMs) noexcept
Construct a driver bound to one bus + one slave address.
Definition alicat_basis2.hpp:81
DriverResult< void > SetModbusAddress(uint8_t new_address) noexcept
Change the instrument's Modbus slave address.
Definition alicat_basis2.hpp:497
uint32_t GetInterFrameGapUs() const noexcept
Definition alicat_basis2.hpp:111
DriverResult< void > StartMeasurementSamples(uint16_t num_samples) noexcept
Start a measurement window of num_samples samples.
Definition alicat_basis2.hpp:434
void SetBusBaudRate(uint32_t bps) noexcept
Tell the driver the bus baud rate (default 38400, the factory rate).
Definition alicat_basis2.hpp:103
DriverResult< uint8_t > DiscoverPresentAddresses(uint8_t *present_bitmap, std::size_t bitmap_bytes, uint16_t probe_timeout_ms=30, uint8_t first_addr=1, uint8_t last_addr=kMaxModbusAddress) noexcept
Probe the entire bus to find which addresses respond.
Definition alicat_basis2.hpp:570
DriverResult< void > SetReferenceTemperatureC(float c) noexcept
Set the reference temperature used for standard / normal flow.
Definition alicat_basis2.hpp:403
DriverResult< void > SetAsciiUnitId(char id) noexcept
Change the instrument's ASCII unit-id (used by the legacy ASCII serial protocol).
Definition alicat_basis2.hpp:513
DriverResult< void > Tare() noexcept
Force a tare (zero-flow reference) on the instrument.
Definition alicat_basis2.hpp:310
uint32_t GetBusBaudRate() const noexcept
Definition alicat_basis2.hpp:107
DriverResult< void > SetAutotareEnabled(bool enabled) noexcept
Enable / disable automatic taring (controllers only).
Definition alicat_basis2.hpp:322
DriverResult< void > SetBaudRate(BaudRate br) noexcept
Change the serial baud rate (instrument register 21).
Definition alicat_basis2.hpp:537
DriverResult< MeasurementData > ReadMeasurement(uint8_t flow_decimals=3) noexcept
Read the current / most-recent measurement window result.
Definition alicat_basis2.hpp:453
DriverResult< void > SetMaxSetpointRamp(uint32_t pct_per_ms_x_10e7) noexcept
Definition alicat_basis2.hpp:290
DriverResult< void > SetTotalizerBatch(uint32_t value_scaled) noexcept
Set the totalizer batch volume (controllers only).
Definition alicat_basis2.hpp:375
DriverResult< void > ConfigureMeasurementTrigger(uint16_t trigger_bits) noexcept
Configure when a new measurement window auto-restarts.
Definition alicat_basis2.hpp:420
DriverResult< void > ResetTotalizer() noexcept
Reset the totalizer to zero.
Definition alicat_basis2.hpp:352
DriverResult< void > SetTotalizerLimitMode(TotalizerLimitMode mode) noexcept
Configure totalizer overflow behaviour.
Definition alicat_basis2.hpp:361
DriverResult< void > WriteMultipleRegisters(uint16_t reg_addr, const uint16_t *regs, uint8_t count) noexcept
Function 16: write count (1..32) consecutive holding registers.
Definition alicat_basis2.hpp:665
DriverResult< void > WriteSingleRegister(uint16_t reg_addr, uint16_t value) noexcept
Function 06: write one holding register.
Definition alicat_basis2.hpp:639
DriverResult< void > FactoryRestore() noexcept
Restore the instrument to factory defaults.
Definition alicat_basis2.hpp:551
Driver(const Driver &)=delete
DriverResult< InstantaneousData > ReadInstantaneous(uint8_t flow_decimals=3) noexcept
Read all instantaneous registers (2100..2109) in one burst.
Definition alicat_basis2.hpp:139
uint16_t GetTimeoutMs() const noexcept
Definition alicat_basis2.hpp:91
Driver & operator=(const Driver &)=delete
DriverResult< InstrumentIdentity > ReadIdentity() noexcept
Pull the static identification of the instrument.
Definition alicat_basis2.hpp:193
DriverResult< void > SetGas(Gas g) noexcept
Select one of the 9 factory-calibrated gases.
Definition alicat_basis2.hpp:336
DriverError ReadHoldingRegisters(uint16_t reg_addr, uint16_t *out, uint8_t count) noexcept
Function 03: read count holding registers starting at reg_addr.
Definition alicat_basis2.hpp:612
DriverResult< void > SetSetpointSource(SetpointSource src) noexcept
Switch the setpoint source (controllers only).
Definition alicat_basis2.hpp:272
constexpr uint32_t InterFrameGapUs(uint32_t bps) noexcept
Minimum idle time t3.5 between two Modbus-RTU frames, in microseconds.
Definition alicat_basis2_modbus.hpp:145
bool IsExceptionReply(const uint8_t *frame, std::size_t length) noexcept
Definition alicat_basis2_modbus.hpp:168
std::size_t BuildWriteMultiple(uint8_t unit_addr, uint16_t reg_addr, const uint16_t *regs, uint8_t count, uint8_t *out, std::size_t out_capacity) noexcept
Build a Function-16 (Write Multiple Registers) request.
Definition alicat_basis2_modbus.hpp:105
DriverError ParseReadHolding(const uint8_t *frame, std::size_t length, uint8_t expected_addr, uint8_t expected_count, uint16_t *regs) noexcept
Parse a Function-03 response into regs[].
Definition alicat_basis2_modbus.hpp:200
DriverError ParseWriteSingleEcho(const uint8_t *frame, std::size_t length, const uint8_t *request, std::size_t req_len) noexcept
Validate the echo of a Function-06 (Write Single) request against the just-sent request.
Definition alicat_basis2_modbus.hpp:220
std::size_t BuildReadHolding(uint8_t unit_addr, uint16_t reg_addr, uint16_t reg_count, uint8_t *out, std::size_t out_capacity) noexcept
Build a Function-03 (Read Holding Registers) request.
Definition alicat_basis2_modbus.hpp:74
std::size_t BuildWriteSingle(uint8_t unit_addr, uint16_t reg_addr, uint16_t value, uint8_t *out, std::size_t out_capacity) noexcept
Build a Function-06 (Write Single Register) request.
Definition alicat_basis2_modbus.hpp:89
constexpr uint32_t InterFrameGapMs(uint32_t bps) noexcept
InterFrameGapUs(bps) rounded up to whole milliseconds (for UartInterface::delay_ms()).
Definition alicat_basis2_modbus.hpp:156
DriverError ParseException(const uint8_t *frame, std::size_t length, uint8_t expected_addr, uint8_t expected_fn, uint8_t *code) noexcept
Parse a 5-byte exception reply (Modbus Application Protocol V1.1b3, section 7).
Definition alicat_basis2_modbus.hpp:183
constexpr std::size_t kReplyHeaderLength
Number of reply bytes a receiver reads before it can tell an exception reply (bit 7 of the function c...
Definition alicat_basis2_modbus.hpp:165
DriverError ParseWriteMultipleEcho(const uint8_t *frame, std::size_t length, uint8_t expected_addr, uint16_t expected_reg_addr, uint16_t expected_count) noexcept
Validate the response of a Function-16 (Write Multiple) request.
Definition alicat_basis2_modbus.hpp:233
constexpr std::size_t kExceptionFrameLength
Length of a Modbus-RTU exception reply: address, function | 0x80, exception code, CRC-16.
Definition alicat_basis2_modbus.hpp:161
constexpr uint8_t ReadHolding
Definition alicat_basis2_registers.hpp:99
constexpr uint8_t WriteMultiple
Definition alicat_basis2_registers.hpp:101
constexpr uint8_t WriteSingle
Definition alicat_basis2_registers.hpp:100
constexpr uint16_t SerialNumberCount
Definition alicat_basis2_registers.hpp:25
constexpr uint16_t MeasurementMinTemp
Definition alicat_basis2_registers.hpp:75
constexpr uint16_t MeasurementAvgTemp
Definition alicat_basis2_registers.hpp:80
constexpr uint16_t MeasurementTriggerMode
Definition alicat_basis2_registers.hpp:73
constexpr uint16_t MaxSetpointRamp
Definition alicat_basis2_registers.hpp:53
constexpr uint16_t TotalizerBatchVolume
Definition alicat_basis2_registers.hpp:52
constexpr uint16_t MeasurementMinFlow
Definition alicat_basis2_registers.hpp:77
constexpr uint16_t CommWatchdogMs
Definition alicat_basis2_registers.hpp:47
constexpr uint16_t TotalizerResetCommand
Definition alicat_basis2_registers.hpp:35
constexpr uint16_t TotalizerLimitMode
Definition alicat_basis2_registers.hpp:36
constexpr uint16_t FactoryRestoreCommand
Definition alicat_basis2_registers.hpp:39
constexpr uint16_t AsciiUnitId
Definition alicat_basis2_registers.hpp:30
constexpr uint16_t kPrevAvgFlowCount
Definition alicat_basis2_registers.hpp:89
constexpr uint16_t ModbusAddress
Definition alicat_basis2_registers.hpp:29
constexpr uint16_t MeasurementPrevAvgFlow
Previous window's average flow: signed 32-bit at 4214-4215, high register first.
Definition alicat_basis2_registers.hpp:88
constexpr uint16_t MeasurementAvgFlow
Definition alicat_basis2_registers.hpp:81
constexpr uint16_t FlowAveragingMs
Definition alicat_basis2_registers.hpp:37
constexpr uint16_t SetpointCommand
Definition alicat_basis2_registers.hpp:59
constexpr uint16_t AutotareEnable
Definition alicat_basis2_registers.hpp:48
constexpr uint16_t SelectedGas
Definition alicat_basis2_registers.hpp:60
constexpr uint16_t MeasurementMaxTemp
Definition alicat_basis2_registers.hpp:76
constexpr uint16_t FirmwareVersion
Definition alicat_basis2_registers.hpp:23
constexpr uint16_t SerialNumberStart
Definition alicat_basis2_registers.hpp:24
constexpr uint16_t BaudRate
Definition alicat_basis2_registers.hpp:22
constexpr uint16_t TareCommand
Definition alicat_basis2_registers.hpp:28
constexpr uint16_t MeasurementSamplesAveraged
Definition alicat_basis2_registers.hpp:79
constexpr uint16_t MeasurementSampleCount
Definition alicat_basis2_registers.hpp:74
constexpr uint16_t SetpointSource
Definition alicat_basis2_registers.hpp:49
constexpr uint16_t MeasurementMaxFlow
Definition alicat_basis2_registers.hpp:78
constexpr uint16_t ReferenceTemperature
Definition alicat_basis2_registers.hpp:34
Definition alicat_basis2.hpp:52
constexpr uint8_t kBroadcastAddress
Definition alicat_basis2_types.hpp:374
constexpr uint8_t kMaxModbusAddress
Definition alicat_basis2_types.hpp:375
FlowUnits
Flow-unit identifier read from instrument register 49.
Definition alicat_basis2_types.hpp:181
BaudRate
Baud-rate codes for register 21 (datasheet "Changing the Baud Rate").
Definition alicat_basis2_types.hpp:230
DriverError
Driver-level error codes.
Definition alicat_basis2_types.hpp:39
@ InvalidFrame
Frame too short, wrong function code, etc.
@ InvalidParameter
Out-of-range argument.
@ InvalidAddress
Modbus address outside 1..247.
@ None
Operation succeeded.
@ ModbusException
Slave returned a Modbus exception PDU.
@ BufferTooSmall
Provided buffer is too small for the response.
@ Timeout
No response within the configured timeout.
constexpr uint16_t kDefaultTimeoutMs
Definition alicat_basis2_types.hpp:379
const char * GetDriverVersion() noexcept
Driver version string (from generated alicat_basis2_version.h).
Definition alicat_basis2.hpp:55
constexpr uint16_t kMagicFactoryRestore
Definition alicat_basis2_types.hpp:377
SetpointSource
Setpoint source — register 516 (controllers only).
Definition alicat_basis2_types.hpp:263
constexpr uint16_t kMagicTare
Definition alicat_basis2_types.hpp:376
TotalizerLimitMode
Totalizer overflow behaviour — register 54.
Definition alicat_basis2_types.hpp:272
constexpr std::string_view ToString(DriverError e) noexcept
Convert a DriverError to a stable human-readable string (no allocation).
Definition alicat_basis2_types.hpp:56
constexpr uint32_t BaudRateToBps(BaudRate b) noexcept
Definition alicat_basis2_types.hpp:239
Gas
Index into the BASIS-2 calibrated gas table.
Definition alicat_basis2_types.hpp:145
constexpr uint32_t kDefaultBusBaudBps
Baud rate the driver assumes for the inter-frame gap until told otherwise (the instrument's factory d...
Definition alicat_basis2_types.hpp:386
ModbusExceptionCode
Exception code of a Modbus exception reply (function code | 0x80, one code byte).
Definition alicat_basis2_types.hpp:82
@ None
The last transaction got no exception reply.
Lightweight result wrapper. Aborts on value() if !ok().
Definition alicat_basis2_types.hpp:115
static constexpr DriverResult success(T v) noexcept
Definition alicat_basis2_types.hpp:122
static constexpr DriverResult failure(DriverError e) noexcept
Definition alicat_basis2_types.hpp:123
Decoded snapshot of registers 2100..2109 (one Modbus burst).
Definition alicat_basis2_types.hpp:302
Gas gas
Definition alicat_basis2_types.hpp:303
Static identification of an instrument (read once at init).
Definition alicat_basis2_types.hpp:347
Decoded measurement-window result (registers 4201..4215).
Definition alicat_basis2_types.hpp:326
uint16_t num_samples_requested
Register 4201: samples to take.
Definition alicat_basis2_types.hpp:341