const std = @import("std"); const builtin = @import("builtin"); pub fn getDriverVersion() f32 { return c_api.GetDriverVersion(); } pub const Labjack = struct { id: ?i32 = null, demobit: bool = false, pub fn autodetect() Labjack { return .{}; } pub fn with_serial_number(sn: i32) Labjack { return .{ .id = sn }; } pub fn connect(self: Labjack) LabjackError!struct { local_id: i32, firmware_version: f32 } { var id = self.cId(); const version = c_api.GetFirmwareVersion(&id); return if (version == 0) @as(c_api.LabjackCError, @bitCast(id)).toError() else .{ .local_id = @intCast(id), .firmware_version = version }; } pub fn firmwareVersion(self: Labjack) LabjackError!f32 { var id = self.cId(); const version = c_api.GetFirmwareVersion(&id); return if (version == 0) @as(c_api.LabjackCError, @bitCast(id)).toError() else version; } pub fn setAllDigitalOutputLow(self: Labjack) LabjackError!void { var id = self.cId(); // bitmask. D0 to D15 (LSB is D0). 0 is input, 1 is output var d_modes: c_long = 0xFF_FF; // bitmask. D0 to D15 (LSB is D0). 0 is output low, 1 is output high var d_outputs: c_long = 0; // bitmask. D0 to D15 (LSB is D0). 0 is output low, 1 is output high // the actual pin states read back from the device. an outvar from the API call. var d_states: c_long = 0; // bitmask. IO0 to IO3 (LSB is IO0). 0 is input, 1 is output const io_modes: c_long = 0b1111; // bitmask. IO0 to IO3 (LSB is IO0). 0 is output low, 1 is output high var io_outputs: c_long = 0; const status = c_api.DigitalIO( &id, self.demo(), &d_modes, io_modes, &d_outputs, &io_outputs, 1, // actually update the pin modes &d_states, ); if (!status.okay()) return status.toError(); } /// Read one analog input channel, either single-ended or differential pub fn analogReadOne(self: Labjack, input: AnalogInput) LabjackError!AnalogReadResult { if (!input.channel.isDifferential() and input.gain_index != 0) { return error.InvalidGain; } var id = self.cId(); var over_v: c_long = 0; var res: AnalogReadResult = undefined; const status = c_api.EAnalogIn( &id, self.demo(), input.channelNumber(), input.gainIndex(), &over_v, &res.voltage, ); if (status.okay()) { res.over_voltage = over_v > 0; return res; } else { return status.toError(); } } pub fn digitalWriteOne(self: Labjack, output: DigitalOutput) LabjackError!void { var id = self.cId(); const status = c_api.EDigitalOut( &id, self.demo(), output.channelNumber(), @intFromBool(output.isDLine()), @intFromBool(output.level), ); if (!status.okay()) return status.toError(); } pub fn readAnalogWriteDigital( self: Labjack, comptime incount: u3, inputs: [incount]AnalogInput, outputs: ?[4]bool, ledOn: bool, ) LabjackError![incount]AnalogReadResult { var id = self.cId(); var out_states: c_long = if (outputs) |out| PackedOutput.fromBoolArray(out).toCLong() else 0; var in_channels: [incount]c_long = undefined; var gains: [incount]c_long = undefined; for (inputs, &in_channels, &gains) |from, *inc, *gain| { inc.* = from.channelNumber(); gain.* = from.gainIndex(); } var v_out: [4]f32 = .{0} ** 4; var over_v: c_long = 0; const status = c_api.AISample( &id, self.demo(), &out_states, @intFromBool(outputs != null), @intFromBool(ledOn), incount, &in_channels, &gains, @intFromBool(false), &over_v, &v_out, ); if (!status.okay()) return status.toError(); var result: [incount]AnalogReadResult = undefined; for (v_out[0..incount], &result) |raw, *res| { res.voltage = raw; // there's no information about which channel had over voltage, so // maybe we should return a different type res.over_voltage = over_v > 0; } return result; } fn cId(self: Labjack) c_long { return self.id orelse -1; } fn demo(self: Labjack) c_long { return @intFromBool(self.demobit); } }; pub const AnalogInput = struct { channel: AnalogInputChannel, range: InputRange = .@"20 V", pub fn channelNumber(self: AnalogInput) u4 { return @intFromEnum(self.channel); } pub fn gainIndex(self: AnalogInput) GainIndex { return @intFromEnum(self.range); } }; pub const AnalogReadResult = struct { voltage: f32, over_voltage: bool, }; pub const DigitalOutput = struct { channel: DigitalOutputChannel, level: bool, pub fn channelNumber(self: DigitalOutput) u4 { return self.channel.channelNumber(); } pub fn isDLine(self: DigitalOutput) bool { return self.channel.isDLine(); } }; pub const AnalogInputChannel = enum(u4) { ai0 = 0, ai1 = 1, ai2 = 2, ai3 = 3, ai4 = 4, ai5 = 5, ai6 = 6, ai7 = 7, diff_01 = 8, diff_23 = 9, diff_45 = 10, diff_67 = 11, pub fn isDifferential(self: AnalogInputChannel) bool { return switch (self) { .diff_01, .diff_23, .diff_45, .diff_67 => true, else => false, }; } }; pub const DigitalOutputChannel = union(enum) { io: u2, d: u4, pub fn channelNumber(self: DigitalOutputChannel) u4 { return switch (self) { inline else => |val| val, }; } pub fn isDLine(self: DigitalOutputChannel) bool { return self == .d; } }; // differential only // 0 => G=1 ±20 volts // 1 => G=2 ±10 volts // 2 => G=4 ±5 volts // 3 => G=5 ±4 volts // 4 => G=8 ±2.5 volts // 5 => G=10 ±2 volts // 6 => G=16 ±1.25 volts // 7 => G=20 ±1 volt pub const GainIndex = u3; pub const InputRange = enum(GainIndex) { @"20 V" = 0, @"10 V" = 1, @"5 V" = 2, @"4 V" = 3, @"2.5 V" = 4, @"2 V" = 5, @"1.25 V" = 6, @"1 V" = 7, }; pub const PackedOutput = packed struct(u4) { io0: bool, io1: bool, io2: bool, io3: bool, pub fn setOut(self: *PackedOutput, output: DigitalOutput) !void { switch (output) { .io => |num| switch (num) { 0 => self.io0 = output.level, 1 => self.io1 = output.level, 2 => self.io2 = output.level, 3 => self.io3 = output.level, }, .d => return error.Invalid, } } pub fn fromBoolArray(states: [4]bool) PackedOutput { return .{ .io0 = states[0], .io1 = states[1], .io2 = states[2], .io3 = states[3], }; } pub fn toCLong(self: PackedOutput) c_long { return @as(u4, @bitCast(self)); } }; const Call: std.builtin.CallingConvention = if (builtin.os.tag == .windows and builtin.cpu.arch == .x86) .Stdcall else .C; pub const c_api = struct { pub const vendor_id: u16 = 0x0CD5; pub const u12_product_id: u16 = 0x0001; pub extern fn OpenLabJack( errorcode: *LabjackCError, vendor_id: c_uint, product_id: c_uint, idnum: *c_long, serialnum: *c_long, caldata: *[20]c_long, ) callconv(Call) c_long; pub extern fn CloseAll(local_id: c_long) callconv(Call) c_long; pub extern fn GetU12Information( handle: *anyopaque, serialnum: *c_long, local_id: *c_long, power: *c_long, cal_data: *[20]c_long, fcdd_max_size: *c_long, hvc_max_size: *c_long, ) callconv(Call) c_long; pub extern fn EAnalogIn( idnum: *c_long, demo: c_long, channel: c_long, gain: c_long, overVoltage: *c_long, voltage: *f32, ) callconv(Call) LabjackCError; pub extern fn EAnalogOut( idnum: *c_long, demo: c_long, analogOut0: f32, analogOut1: f32, ) callconv(Call) LabjackCError; pub extern fn ECount( idnum: *c_long, demo: c_long, resetCounter: c_long, count: *f64, ms: *f64, ) callconv(Call) LabjackCError; pub extern fn EDigitalIn( idnum: *c_long, demo: c_long, channel: c_long, readD: c_long, state: *c_long, ) callconv(Call) LabjackCError; pub extern fn EDigitalOut( idnum: *c_long, demo: c_long, channel: c_long, writeD: c_long, state: c_long, ) callconv(Call) LabjackCError; pub extern fn AsynchConfig( idnum: *c_long, demo: c_long, timeoutMult: c_long, configA: c_long, configB: c_long, configTE: c_long, fullA: c_long, fullB: c_long, fullC: c_long, halfA: c_long, halfB: c_long, halfC: c_long, ) callconv(Call) LabjackCError; pub extern fn Asynch( idnum: *c_long, demo: c_long, portB: c_long, enableTE: c_long, enableTO: c_long, enableDel: c_long, baudrate: c_long, numWrite: c_long, numRead: c_long, data: [*]c_long, ) callconv(Call) LabjackCError; pub extern fn AISample( idnum: *c_long, demo: c_long, stateIO: *c_long, updateIO: c_long, ledOn: c_long, numChannels: c_long, channels: [*]c_long, // numChannels length gains: [*]c_long, // numChannels length disableCal: c_long, overVoltage: *c_long, voltages: *[4]f32, ) callconv(Call) LabjackCError; pub extern fn AIBurst( idnum: *c_long, demo: c_long, stateIOin: c_long, updateIO: c_long, ledOn: c_long, numChannels: c_long, channels: [*]c_long, // numChannels length gains: [*]c_long, // numChannels length scanRate: *f32, disableCal: c_long, triggerIO: c_long, // 0=none, 1=IO0, or 2=IO1 triggerState: c_long, numScans: c_long, timeout: c_long, voltages: *[4096][4]f32, stateIOout: *[4096]c_long, overVoltage: *c_long, transferMode: c_long, // 0=auto,1=normal,2=turbo ) callconv(Call) LabjackCError; pub extern fn AIStreamStart( idnum: *c_long, demo: c_long, stateIOin: c_long, updateIO: c_long, ledOn: c_long, numChannels: c_long, channels: [*]c_long, // numChannels length gains: [*]c_long, // numChannels length scanRate: *f32, disableCal: c_long, reserved1: c_long, // always 0 readCount: c_long, ) callconv(Call) LabjackCError; pub extern fn AIStreamRead( localID: c_long, numScans: c_long, timeout: c_long, voltages: *[4096][4]f32, stateIOout: *[4096]c_long, reserved: ?*c_long, // unused ljScanBacklog: *c_long, overVoltage: *c_long, ) callconv(Call) LabjackCError; pub extern fn AIStreamClear( localID: c_long, ) callconv(Call) LabjackCError; pub extern fn AOUpdate( idnum: *c_long, demo: c_long, trisD: c_long, trisIO: c_long, stateD: [*c]c_long, stateIO: [*c]c_long, updateDigital: c_long, resetCounter: c_long, count: [*c]c_ulong, analogOut0: f32, analogOut1: f32, ) callconv(Call) LabjackCError; pub extern fn BitsToVolts( chnum: c_long, chgain: c_long, bits: c_long, volts: [*c]f32, ) callconv(Call) LabjackCError; pub extern fn VoltsToBits( chnum: c_long, chgain: c_long, volts: f32, bits: [*c]c_long, ) callconv(Call) LabjackCError; pub extern fn Counter( idnum: *c_long, demo: c_long, stateD: [*c]c_long, stateIO: [*c]c_long, resetCounter: c_long, enableSTB: c_long, count: [*c]c_ulong, ) callconv(Call) LabjackCError; pub extern fn DigitalIO( idnum: *c_long, demo: c_long, trisD: *c_long, // 16 bits trisIO: c_long, // 4 bits stateD: *c_long, // 16 bits stateIO: *c_long, // 4 bits updateDigital: c_long, outputD: *c_long, // 16 bits ) callconv(Call) LabjackCError; pub extern fn GetDriverVersion() callconv(Call) f32; pub extern fn StaticErrorString(errorcode: c_long) callconv(Call) [*:0]const u8; pub extern fn GetErrorString(errorcode: c_long, errorString: *[50]u8) callconv(Call) void; pub extern fn GetFirmwareVersion(idnum: *c_long) callconv(Call) f32; pub extern fn ListAll( productIDList: *[127]c_long, serialnumList: *[127]c_long, localIDList: *[127]c_long, powerList: *[127]c_long, calMatrix: *[127][20]c_long, numberFound: *c_long, fcddMaxSize: *c_long, hvcMaxSize: *c_long, ) callconv(Call) LabjackCError; pub extern fn LocalID( idnum: *c_long, localID: c_long, ) callconv(Call) LabjackCError; pub extern fn PulseOut( idnum: *c_long, demo: c_long, lowFirst: c_long, bitSelect: c_long, numPulses: c_long, timeB1: c_long, timeC1: c_long, timeB2: c_long, timeC2: c_long, ) callconv(Call) LabjackCError; pub extern fn PulseOutStart( idnum: *c_long, demo: c_long, lowFirst: c_long, bitSelect: c_long, numPulses: c_long, timeB1: c_long, timeC1: c_long, timeB2: c_long, timeC2: c_long, ) callconv(Call) LabjackCError; pub extern fn PulseOutFinish( idnum: *c_long, demo: c_long, timeoutMS: c_long, ) callconv(Call) LabjackCError; pub extern fn PulseOutCalc( frequency: *f32, timeB: *c_long, timeC: *c_long, ) callconv(Call) LabjackCError; pub extern fn ReEnum(idnum: *c_long) callconv(Call) LabjackCError; pub extern fn Reset(idnum: *c_long) callconv(Call) LabjackCError; pub extern fn ResetLJ(idnum: *c_long) callconv(Call) LabjackCError; pub extern fn CloseLabJack(localID: c_long) callconv(Call) LabjackCError; pub extern fn SHT1X( idnum: *c_long, demo: c_long, softComm: c_long, mode: c_long, statusReg: c_long, tempC: *f32, tempF: *f32, rh: *f32, ) callconv(Call) LabjackCError; pub extern fn SHTComm( idnum: *c_long, softComm: c_long, waitMeas: c_long, serialReset: c_long, dataRate: c_long, numWrite: c_long, numRead: c_long, datatx: [*]u8, // numWrite length datarx: [*]u8, // numRead length ) callconv(Call) LabjackCError; pub extern fn SHTCRC( statusReg: c_long, numWrite: c_long, numRead: c_long, datatx: *[4]u8, datarx: *[4]u8, ) callconv(Call) LabjackCError; pub extern fn Synch( idnum: *c_long, demo: c_long, mode: c_long, msDelay: c_long, husDelay: c_long, controlCS: c_long, csLine: c_long, csState: c_long, configD: c_long, numWriteRead: c_long, data: *[18]c_long, ) callconv(Call) LabjackCError; pub extern fn Watchdog( idnum: *c_long, demo: c_long, active: c_long, timeout: c_long, reset: c_long, activeD0: c_long, activeD1: c_long, activeD8: c_long, stateD0: c_long, stateD1: c_long, stateD8: c_long, ) callconv(Call) LabjackCError; pub extern fn ReadMem( idnum: *c_long, address: c_long, data3: *c_long, data2: *c_long, data1: *c_long, data0: *c_long, ) callconv(Call) LabjackCError; pub extern fn WriteMem( idnum: *c_long, unlocked: c_long, address: c_long, data3: c_long, data2: c_long, data1: c_long, data0: c_long, ) callconv(Call) LabjackCError; pub const LabjackCError = packed struct(c_ulong) { code: LabjackErrorCode, stream_thread_error_flag: u1, firmware_version_error_flag: u1, _unused: switch (@typeInfo(c_ulong).Int.bits) { 32 => u22, 64 => u54, else => @compileError("c_ulong has a mystery number of bits"), }, pub fn okay(self: LabjackCError) bool { return self.code == .no_error; } pub fn toError(self: LabjackCError) LabjackError { return switch (self.code) { .no_error => error.UnknownError, .unknown_error => error.UnknownError, .no_devices_found => error.NoDevicesFound, .device_n_not_found => error.DeviceNNotFound, .set_buffer_error => error.SetBufferError, .open_handle_error => error.OpenHandleError, .close_handle_error => error.CloseHandleError, .invalid_id => error.InvalidId, .array_size_or_value_error => error.ArraySizeOrValueError, .invalid_power_index => error.InvalidPowerIndex, .fcdd_size => error.FcddSize, .hvc_size => error.HvcSize, .read_error => error.ReadError, .read_timeout_error => error.ReadTimeoutError, .write_error => error.WriteError, .feature_error => error.FeatureError, .illegal_channel => error.IllegalChannel, .illegal_gain_index => error.IllegalGainIndex, .illegal_ai_command => error.IllegalAiCommand, .illegal_ao_command => error.IllegalAoCommand, .bits_out_of_range => error.BitsOutOfRange, .illegal_number_of_channels => error.IllegalNumberOfChannels, .illegal_scan_rate => error.IllegalScanRate, .illegal_num_samples => error.IllegalNumSamples, .ai_response_error => error.AiResponseError, .ram_cs_error => error.RamCsError, .ai_sequence_error => error.AiSequenceError, .num_streams_error => error.NumStreamsError, .ai_stream_start => error.AiStreamStart, .pc_buff_overflow => error.PcBuffOverflow, .lj_buff_overflow => error.LjBuffOverflow, .stream_read_timeout => error.StreamReadTimeout, .illegal_num_scans => error.IllegalNumScans, .no_stream_found => error.NoStreamFound, .illegal_input_error => error.IllegalInput, .echo_error => error.EchoError, .data_echo_error => error.DataEchoError, .response_error => error.ResponseError, .asynch_timeout_error => error.AsynchTimeout, .asynch_start_error => error.AsynchStartError, .asynch_frame_error => error.AsynchFrameError, .asynch_dio_config_error => error.AsynchDioConfigError, .input_caps_error => error.InputCapsError, .output_caps_error => error.OutputCapsError, .feature_caps_error => error.FeatureCapsError, .num_caps_error => error.NumCapsError, .get_attributes_warning => error.GetAttributesWarning, .wrong_firmware_version => error.WrongFirmwareVersion, .dio_config_error => error.DioConfigError, .claim_all_devices => error.ClaimAllDevices, .release_all_devices => error.ReleaseAllDevices, .claim_device => error.ClaimDevice, .release_device => error.ReleaseDevice, .claimed_abandoned => error.ClaimedAbandoned, .localid_neg => error.LocalidNeg, .stop_thread_timeout => error.StopThreadTimeout, .terminate_thread => error.TerminateThread, .feature_handle => error.FeatureHandle, .create_mutex => error.CreateMutex, .synch_csstatetris_error => error.SynchCsstatetrisError, .synch_scktris_error => error.SynchScktrisError, .synch_misotris_error => error.SynchMisotrisError, .synch_mositris_error => error.SynchMositrisError, .sht1x_crc_error => error.Sht1xCrcError, .sht1x_measready_error => error.Sht1xMeasreadyError, .sht1x_ack_error => error.Sht1xAckError, .sht1x_serial_reset_error => error.Sht1xSerialResetError, _ => error.UnknownErrorCode, }; } pub const LabjackErrorCode = enum(u8) { no_error = 0, // must be 0 unknown_error = 1, no_devices_found = 2, device_n_not_found = 3, set_buffer_error = 4, open_handle_error = 5, close_handle_error = 6, invalid_id = 7, array_size_or_value_error = 8, invalid_power_index = 9, fcdd_size = 10, hvc_size = 11, read_error = 12, read_timeout_error = 13, write_error = 14, feature_error = 15, illegal_channel = 16, illegal_gain_index = 17, illegal_ai_command = 18, illegal_ao_command = 19, bits_out_of_range = 20, illegal_number_of_channels = 21, illegal_scan_rate = 22, illegal_num_samples = 23, ai_response_error = 24, ram_cs_error = 25, ai_sequence_error = 26, num_streams_error = 27, ai_stream_start = 28, pc_buff_overflow = 29, lj_buff_overflow = 30, stream_read_timeout = 31, illegal_num_scans = 32, no_stream_found = 33, illegal_input_error = 40, echo_error = 41, data_echo_error = 42, response_error = 43, asynch_timeout_error = 44, asynch_start_error = 45, asynch_frame_error = 46, asynch_dio_config_error = 47, input_caps_error = 48, output_caps_error = 49, feature_caps_error = 50, num_caps_error = 51, get_attributes_warning = 52, wrong_firmware_version = 57, dio_config_error = 58, claim_all_devices = 64, release_all_devices = 65, claim_device = 66, release_device = 67, claimed_abandoned = 68, localid_neg = 69, stop_thread_timeout = 70, terminate_thread = 71, feature_handle = 72, create_mutex = 73, synch_csstatetris_error = 80, synch_scktris_error = 81, synch_misotris_error = 82, synch_mositris_error = 83, sht1x_crc_error = 89, sht1x_measready_error = 90, sht1x_ack_error = 91, sht1x_serial_reset_error = 92, _, pub fn describe(self: LabjackErrorCode) []const u8 { const res = StaticErrorString(@intFromEnum(self)); return std.mem.sliceTo(res, 0); } }; }; }; pub const LabjackError = error{ UnknownError, NoDevicesFound, DeviceNNotFound, SetBufferError, OpenHandleError, CloseHandleError, InvalidId, ArraySizeOrValueError, InvalidPowerIndex, FcddSize, HvcSize, ReadError, ReadTimeoutError, WriteError, FeatureError, IllegalChannel, IllegalGainIndex, IllegalAiCommand, IllegalAoCommand, BitsOutOfRange, IllegalNumberOfChannels, IllegalScanRate, IllegalNumSamples, AiResponseError, RamCsError, AiSequenceError, NumStreamsError, AiStreamStart, PcBuffOverflow, LjBuffOverflow, StreamReadTimeout, IllegalNumScans, NoStreamFound, IllegalInput, EchoError, DataEchoError, ResponseError, AsynchTimeout, AsynchStartError, AsynchFrameError, AsynchDioConfigError, InputCapsError, OutputCapsError, FeatureCapsError, NumCapsError, GetAttributesWarning, WrongFirmwareVersion, DioConfigError, ClaimAllDevices, ReleaseAllDevices, ClaimDevice, ReleaseDevice, ClaimedAbandoned, LocalidNeg, StopThreadTimeout, TerminateThread, FeatureHandle, CreateMutex, SynchCsstatetrisError, SynchScktrisError, SynchMisotrisError, SynchMositrisError, Sht1xCrcError, Sht1xMeasreadyError, Sht1xAckError, Sht1xSerialResetError, UnknownErrorCode, // InvalidGain, };