({{, MicrocontrollerEmbedCode[sys,μc,p] 
embeds the systems model sys to the microcontroller μc using p.}})

OpenersNotesSection(Spacer1)Details

MicrocontrollerEmbedCode generates the source code for sys to work with the microcontroller specification μc, compiles it, and embeds the resulting machine code to the μc.

In addition a (PackageLink[MicrocontrollerCodeData, paclet:MicrocontrollerKit/ref/MicrocontrollerCodeData, MicrocontrollerKit Package Symbol]) object is generated that can be used to obtain various properties.

The systems model sys can be a discrete-time (RefLink[TransferFunctionModel, paclet:ref/TransferFunctionModel]), (RefLink[StateSpaceModel, paclet:ref/StateSpaceModel]), (RefLink[AffineStateSpaceModel, paclet:ref/AffineStateSpaceModel]), (RefLink[NonlinearStateSpaceModel, paclet:ref/NonlinearStateSpaceModel]), or (RefLink[SystemsConnectionsModel, paclet:ref/SystemsConnectionsModel]).

The microcontroller μc is given as an association with the following primary keys:

Possible input and output channel specifications:

Other possible keys for μc:

Keys of μc to directly inject code into the source code:

The programming mechanism p is specified as an association.

Possible keys of p include:

If p is a string the external programmer is assumed to be connected to the serial port p.

MicrocontrollerEmbedCode[sys,μc,p,specs] can be used to give additional specifications.

The specifications specs is as association with the following possible keys:

OpenersPrimaryExamplesSection(Spacer1)Examples  (40)

OpenersExampleSection0(Spacer1)Basic Examples  (1)

Load the package:

Needs["MicrocontrollerKit`"]

Turn the LED on an Adafruit Trinket 5V at half brightness:

ℳ = MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[(5/2), \[Null], 0.25`, {}Automatic, ]), <|"Target"-> "AdafruitTrinket5V", "Outputs"->1->"Analog"|>, 
<|"ExternalProgrammer"-> "usbtiny"|>]

The byte count of the embedded machine code:

ℳ["MachineCodeByteCount"]

The source code that was compiled to generate the machine code:

Pane[Framed[ℳ["SourceCode"],Background->ColorData["HTML","AliceBlue"]],{Automatic,300},Scrollbars->{False,True}]

OpenersExampleSection1(Spacer1)Scope  (36)

OpenersExampleSubsection0(Spacer1)Digital Output  (2)

Turn on the LED connected to pin 13:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[5, \[Null], 1, {}Automatic, ]),<|"Target"-> "ArduinoUno","Outputs"->13->"Digital"|>,"/dev/cu.usbmodem14101"]

A systems model with no input, and a state output that toggles every 2 seconds:

sys=(NonlinearStateSpaceModelFull[Which[x>0,0,True,1], x, x, \[Null], 2, {}Automatic, ]);

Use it to blink the LED attached to pin 13:

MicrocontrollerEmbedCode[sys,<|"Target"-> "ArduinoUno","Outputs"->13->"Digital"|>,"/dev/cu.usbmodem14101"]

OpenersExampleSubsection1(Spacer1)Pulse Output  (2)

Turn pin 13 on for one second during each cycle of 4 seconds:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[1, \[Null], 4, {}Automatic, ]),<|"Target"->"ArduinoUno","Outputs"->13->"Pulse"|>,"/dev/cu.usbmodem14101"]

Specify the sampling period of the pulse to be different from the overall sampling period:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[1, \[Null], 4, {}Automatic, ]),<|"Target"->"ArduinoUno","Outputs"->13-><|"Type"->"Pulse",SamplingPeriod->2|>|>,"/dev/cu.usbmodem14101"]

OpenersExampleSubsection2(Spacer1)Analog Output  (5)

Turn on the LED connected to pin 3 at half brightness:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[(5/2), \[Null], 1, {}Automatic, ]),<|"Target"->"ArduinoUno","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101"]

Turn it on at a different brightness level:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[3.75`, \[Null], 1, {}Automatic, ]),<|"Target"->"ArduinoUno","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101"]

Analog output only works with pins having pulse-width modulation:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[3.75`, \[Null], 1, {}Automatic, ]),<|"Target"->"ArduinoUno","Outputs"->4->"Analog"|>,"/dev/tty.usbmodem14101"]

Specify a range, if necessary, to properly scale the output:

μc=<|"Target"->"ArduinoUno","Outputs"->3-><|"Type"-> "Analog","MaxValue"->10|> |>;

Get the LED to glow with maximum brightness:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[10, \[Null], 1, {}Automatic, ]),μc,"/dev/cu.usbmodem14101"]

Half brightness:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[5, \[Null], 1, {}Automatic, ]),μc,"/dev/cu.usbmodem14101"]

Analog signals with positive and negative values need a second pin for direction:

μc= <|"Target"->"ArduinoUno","Outputs"->3-><|"Type"->"Analog","Negative"->4|>|>;

The LED at pin 3 glows with half brightness and the LED at pin 4 is turned on:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[-2.5, \[Null], 1, {}Automatic, ]), μc, "/dev/cu.usbmodem14101"]

The LED at pin 4 is turned off:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[2.5, \[Null], 1, {}Automatic, ]), μc,"/dev/cu.usbmodem14101"]

The LED at pin 4 is turned on for positive values:

μc= <|"Target"->"ArduinoUno","Outputs"->3-><|"Type"->"Analog","Positive"->4|>|>;

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[2.5, \[Null], 1, {}Automatic, ]), μc,"/dev/cu.usbmodem14101"]

The pin 4 LED is turned off for negative values:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[-2.5, \[Null], 1, {}Automatic, ]), μc,"/dev/tty.usbmodem1411"]

Specify a desired waveform generation mode:

ℳ=MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[2.5, \[Null], 1, {}Automatic, ]), <|"Target"->"ArduinoUno","Outputs"->3-><|"Type"-> "Analog","TimerMode"->5|>|>,"/dev/cu.usbmodem14101"]

ℳ["PulseWidthWaveform"]

OpenersExampleSubsection3(Spacer1)Analog Input  (3)

Read the potentiometer connected to "A0" and use it to adjust the brightness of the LED on pin 3:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101"]

Use different voltage references:

μc:=<|"Target"->"ArduinoUno","Inputs"->"A0"-><|"Type"->"Analog","Reference"->#|>,"Outputs"->3->"Analog"|>&;

p="/dev/cu.usbmodem14101";

Use the voltage of 5V applied at the "AVCC" pin:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),μc["AVCC"],p]

Use the internal reference voltage:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),μc["Internal"],p]

Use the voltage of 3.3V applied at the AREF pin:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),μc["AREF"->3.3],p]

Specify the prescaler to be used by the input clock of the ADC:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"A0"-><|"Type"->"Analog","Prescaler"->64|>,"Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101"]

OpenersExampleSubsection4(Spacer1)Digital Input  (3)

Turn the LED on pin 3 on and off using a button attached to pin 8:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 0.1, , ]),<|"Target"->"ArduinoUno","Inputs"->"8"->"Digital","Outputs"->"3"->"Digital"|>,"/dev/cu.usbmodem14101"]

Turn on the LED using the logic value:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[Which[u>0,1,True,0], \[Null], 0.1`, {u}Automatic, ]),<|"Target"->"ArduinoUno","Inputs"->"8"->"Logic","Outputs"->"3"->"Digital"|>,"/dev/cu.usbmodem14101"]

Set the LED brightness using the number of toggles, assumed to be a maximum of 5 every 10 seconds:

μc:=<|"Target"->"ArduinoUno","Inputs"->2->#,"Outputs"->3-><|"Type"->"Analog","MaxValue"->5|>|>&

p="/dev/cu.usbmodem14101";

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 10, , ]),μc["ToggleCount"],p]

Use the number of rising edges:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 10, , ]),μc["RisingEdgeCount"],p]

The number of falling edges:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 10, , ]),μc["FallingEdgeCount"],p]

The number of low counts:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 10, , ]),μc["LowCount"],p]

OpenersExampleSubsection5(Spacer1)Serial (RS-232) Communication  (2)

Set up the microcontroller to send and receive numerical data:

ℳ=MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->0->"Serial","Outputs"->1->"Serial"|>,"/dev/cu.usbmodem14101"]

The serial type can also be directly given, since the serial pins are unique on the Arduino Uno:

ℳ=MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"Serial","Outputs"->"Serial"|>,"/dev/cu.usbmodem14101"]

Open a serial connection to the device:

dev =DeviceOpen["Serial","/dev/cu.usbmodem14101"]

Set up a task to read and write data:

u=y={};

task=SessionSubmit@ScheduledTask[

If[DeviceExecute[dev,"SerialReadyQ"],
AppendTo[y,DeviceReadBuffer[dev,"ReadTerminator"->ℳ["Serial"]["EndByte"]]];DeviceExecute[dev,"ReadFlush"];
AppendTo[u,RandomReal[]];
DeviceWrite[dev,ℳ["Serial"]["StartByte"]];
DeviceWrite[dev, ToString@u[[-1]]];
DeviceWrite[dev,ℳ["Serial"]["EndByte"]]
],

0.5];

Remove the task and close the device after some time:

TaskRemove[task];
DeviceClose[dev]

Compare the sent and received values:

ListLinePlot[{Most@u,ToExpression[FromCharacterCode[Rest@#]]&/@Rest[y]}]

Specify the real-time baudrate to use:

ℳ=MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"Serial","Outputs"->"Serial","Serial"->"BaudRate"->38400|>,"/dev/cu.usbmodem14101"]

ℳ["Serial"]["BaudRate"]

OpenersExampleSubsection6(Spacer1)Serial Peripheral Interface (SPI) Communication  (1)

The MAX6675 thermocouple is a SPI sensor whose data can to be read in two bytes: (WebLink[», https://datasheets.maximintegrated.com/en/ds/MAX6675.pdf])

exchangeSequence={"SELECT","READ","READ","DESELECT"};

The conversion from bytes to obtain the temperature in ° C:

bConversion[b1_,b2_]:={CDeclare["uint16_t",CAssign["sxtnBtValue",COperator[BitOr,{COperator[BitShiftLeft,{b1,8}],b2}]]],COperator[Times,{0.25,COperator[BitShiftRight,{"sxtnBtValue",3}]}]}

ToCCodeString[bConversion["byte1","byte2"]]//StringTrim

Specify the sensor as a SPI slave device with pin 7 as the slave select pin:

ic=12-><|"Type"-> "SPI", "SlaveSelect"->7,"Conversion"->bConversion|>;

The output is sent over the serial (RS-232) channel:

oc="Serial";

Embed code to read the sensor and transmit the result:

ℳ=MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->ic,"Outputs"->oc|>,"/dev/cu.usbmodem14101",<|"SPI"->{7->exchangeSequence}|>]

Open a connection to the Arduino:

y={};
dv =DeviceOpen["Serial","/dev/cu.usbmodem14101"]

Set up a scheduled task to read the data:

task=SessionSubmit@ScheduledTask[

If[DeviceExecute[dv,"SerialReadyQ"],
AppendTo[y,DeviceReadBuffer[dv,"ReadTerminator"->ℳ["Serial"]["EndByte"]]];DeviceExecute[dv,"ReadFlush"];
],

1
];

Remove the task and close the device after some time:

TaskRemove[task];
DeviceClose[dv];

The temperature plot:

ListLinePlot[(ToExpression@FromCharacterCode@Rest@#&/@y)[[2;;-1]],PlotRange->All]

OpenersExampleSubsection7(Spacer1)Inter-Integrated Circuit (I2C) Communication  (1)

Measure the temperature using a MPU6050 sensor. The data is in two registers beginning at 0x41: (WebLink[», http://www6.in.tum.de/pub/Main/TeachingWs2015SeminarAuonomousFahren/RM-MPU-6000A.pdf])

i2cread={"START","0x41","START","READ","ACK","READ","STOP"};

The sensor address is 0x68 and its power management register at 0x6B needs to be initialized:

i2cinit="Initialization"->{"START","0x6B","0x0","STOP"};

i2cspecs="0x68"->{i2cread,i2cinit};

A function to assemble the two bytes that are received:

fn=COperator[BitOr,{COperator[BitShiftLeft,{#1,8}],#2}]&;

ToCCodeString[fn["hbyte","lbyte"]]

The systems model to convert it to degrees C:

sys1=NonlinearStateSpaceModel[{{},36.53+𝓊/340},{},𝓊,SamplingPeriod->0.5]

The model to output a high value if the temperature is over 25° C:

sys2=NonlinearStateSpaceModel[{{}, {Which[𝓊>24, 1, True, 0]}}, {}, {𝓊},SamplingPeriod ->0.5]

The complete model which reads the temperature and outputs a high or low value:

sys=SystemsConnectionsModel[{sys1,sys2},{{1,1}\[DirectedEdge]{2,1}},{{1,1}},{{2,1}}]

The embedded code will turn on an LED at pin 4 if the temperature is greater than 25° C:

MicrocontrollerEmbedCode[sys,<|"Target"->"ArduinoUno","Inputs"->"A4"-><|"Type"->"I2C","SlaveAddress"->"0x68", "Conversion"->fn|>,"Outputs"->4->"Digital"|>,"/dev/cu.usbmodem14101",<|"I2C"->i2cspecs|>]

OpenersExampleSubsection8(Spacer1)External Libraries  (1)

Use an external library that is at a known location:

lib=FileNameJoin[{$HomeDirectory,"Documents","Arduino","libraries","MAX6675","max6675.cpp"}];

Specify the header file along with the code that needs to be invoked at various stages:

i="max6675.h";
u="MAX6675 thermocouple(13, 7, 12)";
l="thermocouple.readCelsius()";

ic=<|"Type"->"ExternalLibrary","IncludeFiles"-> i,"Utilities"-> u,"Loop"->l|>;

Embed code that utilizes the library:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->ic,"Outputs"->"Serial"|>,"/dev/cu.usbmodem14101",<|"Libraries"->lib|>]

OpenersExampleSubsection9(Spacer1)Systems Models  (6)

Embed a system with two inputs and one output:

ics={"A0"->"Analog",2->"RisingEdgeCount"};
ocs={3->"Analog"};

MicrocontrollerEmbedCode[(TransferFunctionModelFull[1, 1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->ics,"Outputs"->ocs|>,"/dev/cu.usbmodem14101"]

Embed a system with two inputs and two outputs:

ics={"A0"->"Analog",0->"Serial"};
ocs={1->"Serial",1->"Serial"};

ℳ=MicrocontrollerEmbedCode[(TransferFunctionModelFull[1, 1, 1, -1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->ics,"Outputs"->ocs|>,"/dev/cu.usbmodem14101"]

The serial start, delimiter, and end bytes:

{sb,db,eb}=Lookup[ℳ["Serial"],{"StartByte","DelimiterByte","EndByte"}]

Open a connection to the microcontroller:

dv=DeviceOpen["Serial","/dev/cu.usbmodem14101"]

Set up a task that sends one input signal and receives two output signals every time period:

y={};

task=SessionSubmit@ScheduledTask[

If[DeviceExecute[dv,"SerialReadyQ"],
AppendTo[y,DeviceReadBuffer[dv,"ReadTerminator"->eb]];DeviceExecute[dv,"ReadFlush"];
DeviceWrite[dv,sb];
DeviceWrite[dv, ToString@RandomReal[]];
DeviceWrite[dv,eb]
],

1];

A function to parse the actual and reference speed values coming though the serial connection:

parseData[{sb,val1__,db,val2__}]:=ToExpression@FromCharacterCode@#&/@{{val1},{val2}}
parseData[___]:=Sequence[]

Remove the task and close the device after some time:

TaskRemove[task];
DeviceClose[dv]

Plot the outputs:

ListLinePlot[Transpose[parseData/@y],PlotLegends->Automatic]

Specify a state-space model directly as a difference equation:

ssm=StateSpaceModel[𝓎[𝓀]==(1/3)(𝓊[𝓀]+𝓊[𝓀-1]+𝓊[𝓀-2]),𝓎[𝓀],𝓊[𝓀],𝓎[𝓀],𝓀,SamplingPeriod->1]

Embed the system:

MicrocontrollerEmbedCode[ssm,<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101"]

Embed a nonlinear model of a simple pendulum:

eqns=m (L^2)θ''[t]+κ θ'[t]+m g L Sin[θ[t]]==τ[t]/.{m->0.5,g->9.8,L->1,κ->0.1};

pend=AffineStateSpaceModel[eqns,θ[t],τ[t],θ[t],t]

Discretize the system:

(Subscript[pend, d])=ToDiscreteTimeModel[pend,0.01]

Embed the system:

MicrocontrollerEmbedCode[(Subscript[pend, d]),<|"Target"->"ArduinoUno","Inputs"->"A2"->"Analog","Outputs"->1->"Serial"|>,"/dev/cu.usbmodem14101"]

Embed a (RefLink[SystemsConnectionsModel, paclet:ref/SystemsConnectionsModel]) of a simple pendulum:

(Subscript[pend, d])=(AffineStateSpaceModelFull[(Subscript[x, 1])+0.01` (Subscript[x, 2]), 0.02` (-4.9` Sin[(Subscript[x, 1])]-0.1` (Subscript[x, 2]))+(Subscript[x, 2]), 0, 0.02`, (Subscript[x, 1]), 0, (Subscript[x, 1]), (Subscript[x, 2]), \[Null], 0.01`, {{τ,0}}Automatic, ]);

The input torque is simulated with a joystick which has an equilibrium value of 2.525:

jstick=NonlinearStateSpaceModel[{{},((𝓊-2.525)/2.525)},{},{{𝓊,2.525}},SamplingPeriod->0.01]

The complete system:

sys=SystemsConnectionsModel[{jstick,(Subscript[pend, d])},{{1,1}\[DirectedEdge]{2,1}},{{1,1}},{{2,1}}]

Embed the system:

ℳ=MicrocontrollerEmbedCode[sys,<|"Target"->"ArduinoUno","Inputs"->"A2"->"Analog","Outputs"->1->"Serial"|>,"/dev/cu.usbmodem14101"]

Open a serial connection to the target:

dv=DeviceOpen["Serial","/dev/cu.usbmodem14101"]

A function to parse the data and a variable to store the data:

parseData[{ℳ["Serial"]["StartByte"],val__}]:=ToExpression@FromCharacterCode@#&/@{{val}}
parseData[___]:={}
y={};

Set up a scheduled task to read the values every 0.01 seconds:

task=SessionSubmit@ScheduledTask[

If[DeviceExecute[dv,"SerialReadyQ"],
y=Join[y,parseData@DeviceReadBuffer[dv,"ReadTerminator"->ℳ["Serial"]["EndByte"]]];
DeviceExecute[dv,"ReadFlush"];
],

0.01
];

Remove the task and close the connection after some time:

TaskRemove[task];
DeviceClose[dv];

The simulated result:

ListLinePlot[Rest[y],PlotRange->All]

Embed a model having bitwise operations:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[BitXor[1,x], x, x, \[Null], 1, {}Automatic, ]),<|"Target"->"ArduinoUno","Outputs"->13->"Digital"|>,"/dev/cu.usbmodem14101",<|ExternalTypeSignature->"int"|>]

OpenersExampleSubsection10(Spacer1)Targets  (2)

ATmega328P_28PDIP microcontroller with an external 20 MHz clock and pin 15 set as output:

μc=<|"Target"->"ATmega328P_28PDIP","Outputs"->15->"Digital","ClockFrequency"->20 (10^6)|>;

Use an Arduino as the programmer:

p=<|"ExternalProgrammer"->"avrisp","ConnectionPort"->"/dev/cu.usbmodem14101","BaudRate"->19200|>;

Embed code to blink an LED connected to pin 15:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[Which[x>0,0,True,1], x, x, \[Null], 0.5`, {}Automatic, ]),μc,p]

ATmega168A_28PDIP microcontroller with factory settings:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[Which[x>0,0,True,1], x, x, \[Null], 0.5`, {}Automatic, ]),<|"Target"->"ATmega168A_28PDIP","Outputs"->15->"Digital"|>,
<|"ExternalProgrammer"->"avrisp","ConnectionPort"->"/dev/cu.usbmodem14101","BaudRate"->19200|>]

OpenersExampleSubsection11(Spacer1)Missing Physical Target  (1)

If no hardware is connected, the code cannot be embedded:

ℳ=MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[Which[x>0,0,True,1], x, x, \[Null], 2, {}Automatic, ]),<|"Target"-> "ArduinoUno","Outputs"->13->"Digital"|>,"/dev/cu.usbmodem14101"]

Framed[StringTrim[ℳ["StandardError"]],Background->ColorData["HTML","MintCream"]]

The code is not embedded if the connection port is specified as (RefLink[None, paclet:ref/None]):

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[Which[x>0,0,True,1], x, x, \[Null], 2, {}Automatic, ]),<|"Target"-> "ArduinoUno","Outputs"->13->"Digital"|>,<|"ConnectionPort"->None|>]

%["StandardError"]

OpenersExampleSubsection12(Spacer1)External Programmer  (1)

Program the microcontroller using an (WebLink[usbasp, http://www.fischl.de/usbasp/]) programmer:

MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[Which[x>0,0,True,1], x, x, \[Null], 1, {}Automatic, ]),<|"Target"->"ATmega168A_28PDIP","Outputs"->15->"Digital"|>,<|"ExternalProgrammer"->"usbasp"|>]

OpenersExampleSubsection13(Spacer1)Timer  (1)

Specify which timer the microcontroller should use:

ℳ=MicrocontrollerEmbedCode[(NonlinearStateSpaceModelFull[Which[x>0,0,True,1], x, x, \[Null], 2, {}Automatic, ]),<|"Target"->"ArduinoUno","Outputs"->13->"Digital","Timer"->2|>,"/dev/cu.usbmodem14101"]

ℳ["Timer"]

OpenersExampleSubsection14(Spacer1)Properties  (1)

Obtain a list of available properties from the MicrocontrollerCodeData object:

ℳ=MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"-> 2->"RisingEdgeCount","Outputs"->"Serial"|>,"/dev/cu.usbmodem14101"]

allP=ℳ["Properties"]

Extract a property value:

ℳ["Microcontroller"]

All property values related to serial communication:

Dataset[ℳ["Serial"]]

OpenersExampleSubsection15(Spacer1)Additional Specifications  (4)

Create a directory to preserve intermediate files:

wdir=CreateDirectory[];

Deploy the code and preserve the intermediate files:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101",<|"WorkingDirectory"->wdir,"CleanIntermediate"->False|>]

The intermediate files:

FileNameTake/@(ifiles=FileNames["*",wdir])

Delete them:

DeleteFile[ifiles]

By default, intermediate files are deleted:

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101",<|"WorkingDirectory"->wdir|>]

FileNameTake/@FileNames["*",wdir]

Delete the directory:

DeleteDirectory[wdir]

Specify the path to the compiler:

cmpIn=FileNameJoin[{$RootDirectory,"usr","local","bin"}];

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101",<|"CompilerInstallation"->cmpIn|>]

Specify the compiler's name:

cmpIn=FileNameJoin[{$RootDirectory,"usr","local","bin"}];

MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),
<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101",<|"CompilerInstallation"->cmpIn,"CompilerName"->"avr-gcc"|>]

Typically the generated source code is in C:

ℳ𝒸=MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101"];

Pane[Framed[ℳ𝒸["SourceCode"],Background->ColorData["HTML","AliceBlue"]],{Automatic,300},Scrollbars->{False,True}]

Use the Wiring language (used by Arduino sketches):

ℳ𝓌=MicrocontrollerEmbedCode[(StateSpaceModelFull[1, 1, , ]),<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->3->"Analog"|>,"/dev/cu.usbmodem14101",<|"Language"->"Wiring"|>];

Pane[Framed[ℳ𝓌["SourceCode"],Background->ColorData["HTML","AliceBlue"]],{Automatic,300},Scrollbars->{False,True}]

OpenersExampleSection2(Spacer1)Generalizations & Extensions  (1)

A system specified as an expression:

sys1={{u1,u2},{u1+u2,u1-u2}};

Its equivalent systems model specification:

sys2=NonlinearStateSpaceModel[{{},{u1+u2,u1-u2}},{},{u1,u2},SamplingPeriod->1]

The data objects:

{ℳ1,ℳ2}=Table[MicrocontrollerEmbedCode[sys,<|"Target"->"ArduinoUno","Inputs"->{"A0"->"Analog","A1"->"Analog"},"Outputs"->{"Serial","Serial"}|>,"/dev/cu.usbmodem14101"],{sys,{sys1,sys2}}]

The generated code is the same in both cases:

sC[sCode_]:=With[{pos=StringPosition[sCode,"double u[2]"|"u[1];\n}"]},StringTake[sCode,{Min[pos],Max[pos]}]]

Text[Grid[{Table[sC[M["SourceCode"]],{M,{ℳ1,ℳ2}}]},Frame->True,Background->{{,}}]]

OpenersExampleSection5(Spacer1)Possible Issues  (2)

Only discrete-time systems models can be embedded:

tfm=(TransferFunctionModelFull[(5/(1+10 s)), , , ]);

MicrocontrollerEmbedCode[tfm,<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->10->"Analog"|>,"/dev/cu.usbmodem14101"]

Discretize the system:

(Subscript[tfm, d])=ToDiscreteTimeModel[tfm,0.5]

Embed the discrete-time system:

MicrocontrollerEmbedCode[(Subscript[tfm, d]),<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->10->"Analog"|>,"/dev/cu.usbmodem14101"]

In a connections model, all the subsystems must have the same sampling period:

(Subscript[sys, 1])=(StateSpaceModelFull[1/2, 1, -4, 0, 1, , ]);
(Subscript[sys, 2])=(StateSpaceModelFull[3/5, 1, 5, 0, 0.1, , ]);

SystemsConnectionsModel[{(Subscript[sys, 1]),(Subscript[sys, 2])},{{1,1}\[DirectedEdge]{2,1}},{{1,1}},{{2,1}}]

MicrocontrollerEmbedCode[%,<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->10->"Analog"|>,"/dev/tty.usbmodemfa1311"]

Resample one system to match the other:

(Subscript[sys, 1])=ToDiscreteTimeModel[ToContinuousTimeModel[(Subscript[sys, 1])],0.1]

SystemsConnectionsModel[{(Subscript[sys, 1]),(Subscript[sys, 2])},{{1,1}\[DirectedEdge]{2,1}},{{1,1}},{{2,1}}]

Embed the system with subsystems all having the same sampling period:

MicrocontrollerEmbedCode[%,<|"Target"->"ArduinoUno","Inputs"->"A0"->"Analog","Outputs"->10->"Analog"|>,"/dev/cu.usbmodem14101"]

({{DocumentationFooterIconsRelatedFunction.png, ({{See Also}, {(PackageLink[MicrocontrollerCodeData, paclet:MicrocontrollerKit/ref/MicrocontrollerCodeData, MicrocontrollerKit Package Symbol])}})}})

({{DocumentationFooterIconsRelatedTutorial.png, ({{Tutorials}, {▪(RefLinkPlain[Microcontroller Kit, paclet:MicrocontrollerKit/tutorial/Overview])}})}})

({{DocumentationFooterIconsRelatedGuide.png, ({{Related Guides}, {▪(RefLinkPlain[Microcontroller Kit, paclet:MicrocontrollerKit/guide/MicrocontrollerKit])}, {▪(RefLinkPlain[Control Systems, paclet:guide/ControlSystems])}, {▪(RefLinkPlain[Signal Processing, paclet:guide/SignalProcessing])}, {▪(RefLinkPlain[Connected Devices, paclet:guide/UsingConnectedDevices])}})}})