Generated banner for Matlab Modernize Daq
Generated logo for Matlab Modernize Daq

Matlab Modernize Daq

MathWorksOfficialCreated by MathWorks

  • Skill
  • Tool integration
  • Science and research
  • BSD-2-Clause
  • No issues found

From the author

Port MATLAB Data Acquisition Toolbox code from the discouraged (legacy) session-based interface (daq.createSession, addAnalogInputChannel, startBackground, DataAvailable listeners, queueOutputData, wait) to the recommended DataAcquisition interface (daq("ni"), addinput, start, ScansAvailableFcn, write, preload). Use when migrating legacy DAQ scripts, converting session-API calls, working with DataAcquisition objects, writing multi-feature DAQ scripts that combine triggers, callbacks, continuous acquisition, or analog output. Also use when a user says their DAQ script "used to work" or "errors on R20XX", since modernizing legacy session-API code is a frequent fix for those failures. Trigger keywords: daq, DAQ, NI, session interface, addtrigger, addclock, ScansAvailableFcn, ScansRequiredFcn, daq.createSession, addAnalogInputChannel, startBackground, queueOutputData, DataAvailable, evt.Data, evt.TimeStamps, wait(d), preload, discouraged, legacy, modernize, R2020a.
— the description by MathWorks

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured procedure for porting legacy session-based MATLAB DAQ code to the modern DataAcquisition interface introduced in R2020a. It addresses common pitfalls such as the removal of wait(d), the replacement of DataAvailable listeners with ScansAvailableFcn, and the transition from queueOutputData to preload and write. The skill includes verified patterns for triggers, callbacks, and continuous acquisition, ensuring that users can modernize their scripts while avoiding silent failures and common runtime errors.

What it does

  • Maps legacy session-API tokens to modern DataAcquisition interface equivalents.
  • Provides verified code patterns for triggers, clocks, and callback-driven streaming.
  • Guides the implementation of state-sharing patterns for callbacks using nested functions or handle classes.
  • Replaces deprecated lifecycle methods like wait(d) with modern polling or duration-based start commands.
  • Includes a verification script to detect silent gaps and common errors in modernized DAQ code.
  • Offers guidance on handling errors within callbacks using ErrorOccurredFcn.

When to use it

  • When migrating legacy DAQ scripts that use daq.createSession or addAnalogInputChannel.
  • When a script fails with errors related to deprecated functions like wait or DataAvailable.
  • When writing new, complex DAQ scripts involving continuous acquisition or analog output.
  • When replacing legacy listeners with modern ScansAvailableFcn or ScansRequiredFcn callbacks.

Advantages

  • Reduces runtime errors by replacing deprecated and discouraged API calls.
  • Prevents silent failures in callbacks by implementing proper error handling.
  • Ensures waveform continuity in continuous output tasks through correct preload and write patterns.
  • Provides a clear, verified path for modernizing complex multi-feature DAQ workflows.

Usage examples

“How do I update my legacy DAQ script that uses addAnalogInputChannel and startBackground?”
The agent will guide you through mapping these to the modern addinput and start commands, while also updating your callback structure to use ScansAvailableFcn.
“My DAQ script errors with 'Undefined function wait' on R2024a. How do I fix it?”
The agent will explain that wait(d) is removed and provide the modern alternative using a while loop polling the d.Running property.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/matlab/matlab-modernize-daq/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip matlab-modernize-daq.zip -d .agents/skills

Documentation by MathWorks

Port DAQ Code to the Modern DataAcquisition Interface

Translate legacy session-based Data Acquisition Toolbox code (daq.createSession, addAnalogInputChannel, DataAvailable listeners, startBackground, queueOutputData, wait) to the modern DataAcquisition interface introduced in R2020a (daq("ni"), addinput, ScansAvailableFcn, start, write, preload). Also covers writing new multi-feature DAQ scripts where the modern API patterns are easy to get wrong under cognitive load.

When to Use

  • Porting an existing script that calls daq.createSession, addAnalogInputChannel, addAnalogOutputChannel, addCounterInputChannel, addCounterOutputChannel, or addDigitalChannel.

  • Replacing DataAvailable / DataRequired / ErrorOccurred listeners with ScansAvailableFcn / ScansRequiredFcn / ErrorOccurredFcn.

  • Replacing startBackground / startForeground / s.wait() lifecycle calls.

  • Replacing queueOutputData with preload + write.

  • Writing a new DAQ script that combines two or more of: external triggers, continuous acquisition, callback-driven streaming, analog output with refill, or cross-callback state.

  • Debugging errors like Undefined function 'wait' for input arguments of type 'daq.interfaces.DataAcquisition' or Unrecognized method, property, or field 'Data' for class 'matlabshared.asyncio.buffer.ElementsAvailableInfo'.

When NOT to Use

  • Single-channel foreground acquisition with no trigger and no callbacks (e.g. "acquire 1 s of voltage from ai0 at 10 kHz and plot"). The modern API is straightforward at this scale and this skill would be unnecessary overhead.

  • Picking a DAQ vendor or evaluating non-NI hardware. This skill's evidence base is NI.

  • Signal processing or analysis of already-acquired data (filtering, spectral analysis, etc.). This skill only covers acquiring and generating data through the DataAcquisition interface, not post-processing it.

  • Simulink, App Designer, or real-time-target DAQ workflows.

  • Modernizing matlab.unittest test suites or framework-style test classes (parameterized fixtures, assumeTrue gating, arity-contract tests, helper-class hierarchies). The API substitutions in this skill apply, but the test-harness scaffolding (parent-class stripping, fixture redesign, retiring tests that target removed contracts) is out of scope. Use this skill to translate the API calls inside test bodies, then make the harness-level judgment calls separately.

Workflow

When porting a session-based script or composing a new multi-feature DAQ script, follow this order. Verify after every step — most failure modes in this domain are silent.

  1. Inventory the legacy script (porting only). Identify: device(s), channel types, rate, continuous vs finite, trigger/clock connections, callbacks (DataAvailable/DataRequired/ErrorOccurred), state shared across callbacks, output queueing.

  2. Translate vocabulary. Map every session-API token to its modern counterpart. Consult references/session-to-modern-mapping.md — load it whenever a session-API token appears in the legacy script. Common silent-rename traps: NotifyWhenDataAvailableExceeds → ScansAvailableFcnCount, NumberOfScans/NumScans → implicit (no equivalent argument), ExternalTriggerTimeout → DigitalTriggerTimeout, IsContinuous=true → start(d, "Continuous") argument.

  3. Build the DataAcquisition object. d = daq("ni"), set d.Rate, add channels with addinput/addoutput. Apply per-channel properties via the channel handle returned by addinput/addoutput.

  4. Wire triggers and clocks. Use references/canonical-snippets.md §1 — load it when the script involves addtrigger or addclock. Argument order is (d, type, role, trigSrc, trigDest) — Source is 4th, Destination is 5th. Verify: the returned trigger object's Source and Destination fields show what you intended.

  5. Wire callbacks. Use canonical snippets §2 — load it when the script needs ScansAvailableFcn or ScansRequiredFcn. Inside the callback, always pull data via read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix") — the evt argument is matlabshared.asyncio.buffer.ElementsAvailableInfo and exposes only NumElementsAvailable, no Data, no TimeStamps. Strongly recommend wiring d.ErrorOccurredFcn = @(~,evt) fprintf("DAQ error: %s\n", evt.Error.message) — without it, exceptions inside other callbacks are silently swallowed and the run "succeeds" while every fire throws. For strict ports where the legacy script had no ErrorOccurred listener, surface this trade-off to the user instead of silently adding the handler — let them opt in.

  6. Pick a state-sharing pattern (callbacks only). If a callback needs to maintain state across fires (running counters, phase accumulator, latch flags), see references/callback-state-patterns.md — load it any time a callback maintains state. Default: handle-class wrapper or nested function. Avoid src.UserData.X = src.UserData.X + ... field-mutation; it does not persist.

  7. Wire the lifecycle. Use canonical snippets §3 — load it when blocking on completion. There is no wait(d) method on the modern object. For finite acquisitions, prefer start(d, "Duration", seconds(N)) then read or a Running poll. For continuous, set a stop condition inside the callback (if scansRead >= target; stop(src); end) and poll d.Running from the caller.

  8. Run verifyDataAcquisition before live execution. Script: scripts/verifyDataAcquisition.m. Catches the four highest-frequency silent gaps (missing ErrorOccurredFcn, evt.Data in callback source, wait(d) token, UserData.X = UserData.X + ... field mutation) by inspecting the live object and grepping the source file. Run via mcp__matlab__run_matlab_file if available, or copy into the user's project.

  9. Live verification. Run a short version (e.g. 1–2 s instead of the user's full duration). Check that the script reaches start cleanly, that the callback fires at least once, and that no errors print from ErrorOccurredFcn. Use mcp__matlab__run_matlab_file against a saved .m file, not mcp__matlab__evaluate_matlab_code — the eval-string buffer runs in script mode and rejects local/nested function definitions, which Pattern 5 and most callback bodies require.

Key Functions

FunctionPurposeToolboxAvailable From
daqCreate a DataAcquisition object (d = daq("ni"))Data Acquisition ToolboxR2020a
addinputAdd input channel (analog, counter, or digital)Data Acquisition ToolboxR2020a
addoutputAdd output channelData Acquisition ToolboxR2020a
addtriggerAdd trigger connection: addtrigger(d, type, role, trigSrc, trigDest)Data Acquisition ToolboxR2020a
addclockAdd clock connection (similar signature)Data Acquisition ToolboxR2020a
readForeground acquire OR pull data inside ScansAvailableFcnData Acquisition ToolboxR2020a
writeWrite output samples (also from inside ScansRequiredFcn)Data Acquisition ToolboxR2020a
preloadLoad initial output buffer before start(d, "Continuous"). Output channels only.Data Acquisition ToolboxR2020a
startBegin background acquisition: start(d), start(d, "Continuous"), start(d, "Duration", seconds(N))Data Acquisition ToolboxR2020a
stopStop a running acquisitionData Acquisition ToolboxR2020a
flushDiscard buffered scans before readingData Acquisition ToolboxR2020a
daqlistEnumerate available devices (replaces daq.getDevices)Data Acquisition ToolboxR2020a
daqresetReset DAQ subsystem (replaces daq.reset)Data Acquisition ToolboxR2020a

Properties on DataAcquisition: Rate, Running, NumScansAcquired, NumScansOutputByHardware, ScansAvailableFcn, ScansAvailableFcnCount, ScansRequiredFcn, ScansRequiredFcnCount, ErrorOccurredFcn, DigitalTriggerTimeout, UserData.

Patterns

The five canonical patterns below appear in full, with verified code, in references/canonical-snippets.md. Inline below is the minimal recall snippet for each — enough to anchor the agent's memory under load. Load the reference for the full, runnable forms.

Pattern 1: Add an external start trigger
trg = addtrigger(d, "Digital", "StartTrigger", "External", "Dev1/PFI0");
trg.Condition = "RisingEdge";
d.DigitalTriggerTimeout = 30;

The 4th argument is the Source ("External" for an external signal). The 5th is the Destination (the device terminal). Reversing them is the most-frequent error in this domain. The trigger condition ("RisingEdge", "FallingEdge") is set on the trigger object returned by addtrigger (trg.Condition), not on the DataAcquisition object. There is no d.DigitalTriggerCondition property — inventing one passes static lint but errors at runtime.

Pattern 2: ScansAvailableFcn body — pull data via read, never evt.Data
d.ScansAvailableFcnCount = round(d.Rate * 0.5);
d.ScansAvailableFcn = @(src, ~) onBlock(src);
d.ErrorOccurredFcn  = @(~, evt) fprintf("DAQ error: %s\n", evt.Error.message);

function onBlock(src)
    [data, t] = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix");
    plot(t, data); drawnow limitrate;
end

The evt argument carries only NumElementsAvailable; it has no Data or TimeStamps. Wiring ErrorOccurredFcn is strongly recommended — without it, callback exceptions are silently swallowed and the run "succeeds" while every fire throws. For strict ports of legacy scripts that did not have an ErrorOccurred listener, ask the user before adding one rather than inserting it silently.

Pattern 3: Block until acquisition completes — there is no wait(d)
start(d, "Duration", seconds(10));
while d.Running
    pause(0.05);
end

wait(d) does not exist on daq.interfaces.DataAcquisition. Poll d.Running, or rely on start(d, "Duration", ...) + a stop condition inside the callback.

Pattern 4: Continuous AO playback with auto-refill
d = daq("ni");
d.Rate = 1000;
addoutput(d, "Dev1", "ao0", "Voltage");
d.ScansRequiredFcnCount = d.Rate;
d.ScansRequiredFcn = @(src, ~) write(src, generateNextBlock());
preload(d, generateNextBlock());
preload(d, generateNextBlock());
start(d, "Continuous");

preload is output-only — calling it on input-only setups errors. Two preloaded blocks before start give the buffer headroom for the first ScansRequiredFcn to fire.

Pattern 5: Cross-callback state via nested function or handle class
function runStreamingAcquisition()
    d = daq("ni");
    addinput(d, "Dev1", "ai0", "Voltage");
    d.Rate = 1000;
    d.ScansAvailableFcnCount = 200;

    scansRead = 0;
    target    = d.Rate * 5;
    d.ScansAvailableFcn = @(src, ~) onBlock(src);
    d.ErrorOccurredFcn  = @(~, evt) fprintf("DAQ error: %s\n", evt.Error.message);

    start(d, "Continuous");
    while d.Running
        pause(0.05);
    end

    function onBlock(src)
        data = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix");
        scansRead = scansRead + size(data, 1);
        if scansRead >= target
            stop(src);
        end
    end
end

Nested function captures scansRead by reference, so updates persist across fires. Do not use src.UserData.X = src.UserData.X + ... — that pattern does not persist; counters reset every fire and closed-loop control silently outputs zero forever. Full alternatives (handle class, full UserData rewrite) and their tradeoffs are in references/callback-state-patterns.md.

Conventions

  • Strongly recommend wiring d.ErrorOccurredFcn whenever any other *Fcn callback is set, since the listener swallows callback exceptions otherwise. For strict ports of legacy scripts that lacked an ErrorOccurred listener, surface the trade-off to the user rather than silently adding the handler — adding it is a defensible default but fidelity to the source is also defensible.

  • Always pull data via read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix") inside ScansAvailableFcn. Never reach for evt.Data or evt.TimeStamps.

  • Always verify the returned trigger object after addtrigger — confirm Source and Destination reflect intent.

  • Never use wait(d). Poll d.Running or use start(d, "Duration", ...).

  • Never mutate fields of d.UserData from inside a callback (src.UserData.X = src.UserData.X + ...). State must live in a closure-captured variable, a handle-class wrapper, or a whole-struct rewrite of UserData.

  • Never call preload on an input-only DataAcquisition. It is output-only.

  • Prefer start(d, "Duration", seconds(N)) over start(d, "NumScans", N) for finite acquisition. NumScans is silently coerced or ignored.

  • Prefer the modern function names: daqlist over daq.getDevices, daqreset over daq.reset, daq("ni") over daq.createSession('ni').

Common Mistakes

MistakeWhy It's WrongCorrect Approach
addtrigger(d, "Digital", "StartTrigger", "Dev1/PFI0", "External")Source/Destination reversed. 4th arg is Source, 5th is Destination.addtrigger(d, "Digital", "StartTrigger", "External", "Dev1/PFI0")
plot(evt.TimeStamps, evt.Data) inside ScansAvailableFcnevt is ElementsAvailableInfo; only NumElementsAvailable. Throws silently.[data, t] = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix")
wait(d)No such method on daq.interfaces.DataAcquisition. Throws.while d.Running, pause(0.05); end
src.UserData.Counter = src.UserData.Counter + nField mutation through src.UserData does not persist across callback fires.Closure variable in nested function, or handle-class wrapper. See callback-state-patterns.md.
preload(d, ...) on a DAQ with only input channelspreload is output-only.For inputs, just call start(d, ...). No prepare/preload step needed.
start(d, "NumScans", 1000)NumScans is ignored — emits warning. Buffer size determined by preload or read.start(d, "Duration", seconds(N)), or rely on preload size.
s.IsContinuous = true; s.startBackground();Both are session-API. Modern API has no IsContinuous property.start(d, "Continuous")
addlistener(d, 'DataAvailable', @cb)Listener events were renamed to *Fcn properties on the object.d.ScansAvailableFcn = @cb
s.queueOutputData(block)Session-API. Modern API uses preload (initial) and write (refill).preload(d, block) before start; write(d, block) from ScansRequiredFcn.

References

  • references/session-to-modern-mapping.md — full rename table for every function and property that is commonly ported verbatim by mistake. Load whenever a session-API token appears in the legacy script.

  • references/canonical-snippets.md — minimal, self-contained, runnable forms of the five patterns above. Load when composing a multi-feature script and you want a verified anchor.

  • references/callback-state-patterns.md — four state-sharing options (UserData / closure / handle-class / nested function), when to pick which, and the silent failure modes of the wrong choice. Load when any callback needs cross-fire state.

  • scripts/verifyDataAcquisition.m — static-checks a DataAcquisition object and its source script before live execution. Returns a structured report flagging the four highest-frequency silent gaps.


Copyright 2026 The MathWorks, Inc.


Files

references/callback-state-patterns.md

Callback State Patterns

When a ScansAvailableFcn or ScansRequiredFcn needs to maintain state across fires (running counters, phase accumulators, latch flags, file handles), four mechanisms are available. Three work, one fails silently. This file is the decision guide.

Quick decision

NeedUse
Simple scalar counter or flag, in a function-form scriptNested function (Pattern A)
Complex state shared across multiple callbacks and external codeHandle class wrapper (Pattern B)
Quick prototype, single callback, can re-create state each fireAnonymous-function closure (Pattern C, with caveats)
Code already passes d everywhere and you don't want extra filesWhole-struct UserData rewrite (Pattern D, advanced)

Never use: src.UserData.X = src.UserData.X + ... field mutation (see anti-pattern below).

The callback is a nested function inside the entry function. Local variables in the entry function are captured by reference in nested functions, so updates persist.

function runStreamingAcquisition()
    d = daq("ni");
    addinput(d, "Dev1", "ai0", "Voltage");
    d.Rate = 1000;
    d.ScansAvailableFcnCount = 200;

    scansRead = 0;
    target    = d.Rate * 5;

    d.ScansAvailableFcn = @(src, ~) onBlock(src);
    d.ErrorOccurredFcn  = @(~, evt) fprintf("DAQ error: %s\n", evt.Error.message);

    start(d, "Continuous");
    while d.Running
        pause(0.05);
    end

    function onBlock(src)
        data      = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix");
        scansRead = scansRead + size(data, 1);
        if scansRead >= target
            stop(src);
        end
    end
end

When to use: the simplest correct form. Prefer this when the whole acquisition fits in one entry function.

When NOT to use: when state needs to be inspected from outside the entry function, or shared between multiple unrelated functions.

Pattern B — Handle class wrapper

A handle class instance is passed by reference. Mutations to its properties persist across all references.

classdef AcquisitionState < handle
    properties
        ScansRead (1,1) double = 0
        Target    (1,1) double
    end
    methods
        function obj = AcquisitionState(target)
            obj.Target = target;
        end
    end
end

% Usage:
state = AcquisitionState(d.Rate * 5);
d.ScansAvailableFcn = @(src, ~) onBlock(src, state);
d.ErrorOccurredFcn  = @(~, evt) fprintf("DAQ error: %s\n", evt.Error.message);
start(d, "Continuous");
while d.Running
    pause(0.05);
end

function onBlock(src, state)
    data           = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix");
    state.ScansRead = state.ScansRead + size(data, 1);
    if state.ScansRead >= state.Target
        stop(src);
    end
end

When to use: state is complex (multiple fields, methods like reset(), summary()), or callers outside the entry function need to read or modify it.

Cost: one additional class file. Worth it for nontrivial state.

Pattern C — Anonymous-function closure (caveats)

Anonymous functions snapshot variables by value at creation time. They cannot mutate them.

% This works:
gain = 0.5;
d.ScansAvailableFcn = @(src, ~) processBlock(src, gain);
% gain is captured by value; processBlock can read but not change it for next time.

% This DOES NOT work the way you might expect:
counter = 0;
d.ScansAvailableFcn = @(src, ~) (counter = counter + 1);  % Not even valid MATLAB syntax

When to use: read-only state (constants, configuration values that don't change across fires).

When NOT to use: any state that needs to update across fires. Pattern A or B instead.

Pattern D — Whole-struct UserData rewrite (advanced)

d.UserData is a property; assigning d.UserData = newStruct calls the property setter and persists. Field mutation through d.UserData.X = ... from inside a callback does not persist reliably — see anti-pattern.

d.UserData = struct('ScansRead', 0, 'Target', d.Rate * 5);
d.ScansAvailableFcn = @(src, ~) onBlock(src);
d.ErrorOccurredFcn  = @(~, evt) fprintf("DAQ error: %s\n", evt.Error.message);
start(d, "Continuous");

function onBlock(src)
    data = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix");
    s    = src.UserData;                  % copy whole struct out
    s.ScansRead = s.ScansRead + size(data, 1);
    src.UserData = s;                     % whole-struct assignment back
    if s.ScansRead >= s.Target
        stop(src);
    end
end

When to use: state belongs conceptually with the device and you don't want a separate class file. The whole-struct rewrite (s = src.UserData; ... ; src.UserData = s) is the only safe UserData mutation form.

Verify after: read d.UserData.<field> after a few callback fires and confirm the value advanced. Don't trust scan-count totals as a proxy — NumScansAcquired advances independent of your callback state.

Anti-pattern — src.UserData.X = src.UserData.X + … field mutation

% DO NOT DO THIS — field mutation inside the callback does not persist.
function onBlock(src)
    data = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix");
    src.UserData.ScansRead = src.UserData.ScansRead + size(data, 1);  % SILENTLY FAILS
    if src.UserData.ScansRead >= src.UserData.Target
        stop(src);
    end
end

This pattern reads src.UserData (a copy), mutates a field on the copy, and the assignment back through src.UserData.ScansRead = … does not propagate. The counter resets every fire; closed-loop control laws output zero forever; running totals stay at the increment of a single block.

Why it's silent: the syntax is legal MATLAB and produces no error. The field appears to update within the same callback fire, but the next fire sees the original value. Listener exception handling never sees a problem because there is no exception.

Test for it: insert fprintf("UserData.X = %d\n", src.UserData.X); at the top of the callback. If the value never advances across fires, this anti-pattern is in play.

Summary table

PatternPersists across fires?When to useNotes
A. Nested functionYes (by reference)Default for in-script callbacksCaptures everything in scope
B. Handle classYes (by reference)Complex state, external access, methodsExtra class file
C. Anonymous closure (read-only)N/A — read onlyConfiguration constantsCannot mutate
D. Whole-struct UserData rewriteYes (whole-property)Lightweight, self-contained on dOnly safe form: s = src.UserData; …; src.UserData = s
Anti: field mutation on UserDataNo — silent failureNeverThe most-common silent bug in DAQ callbacks

Copyright 2026 The MathWorks, Inc.


Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an MCP server

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
MathWorks
Publisher
MathWorks (Official)
Source
matlab/matlab-agentic-toolkit · skills-catalog/test-and-measurement/matlab-modernize-daq · commit 2a7f215
Licence
BSD 2-Clause License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-modernize-daq.zip