The Wolfram Language Microcontroller Kit: Automating deployment of microcontroller code

Suba Thomas 
Wolfram Research, Inc.

Join the Conversation #WolframTechConf

A basic example

Needs["MicrocontrollerKit`"]

port="port name"; (*replace*)

ℳ=EmbedMicrocontrollerCode[
(StateSpaceModelFull[1, 0.25, , ])
,
{"ArduinoUno",{{"A0","Analog"}},{{"3","Analog"}}}
,
port (*\[LeftAssociation]"ConnectionPort"->port\[RightAssociation]*)
]

OutputResponse[(StateSpaceModelFull[1, 0.25, , ]),anything[k], k]

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

grid[ℳ["Properties"]]

A quick overview

Summary

Digital output

EmbedMicrocontrollerCode[(NonlinearStateSpaceModelFull[1, , 0.5, {}Automatic, ]), {"ArduinoUno", {}, {{"4", "Digital"}}}, port]

EmbedMicrocontrollerCode[(NonlinearStateSpaceModelFull[Which[x>0,0,True,1], x, x, , 0.5, {}Automatic, ]),{"ArduinoUno",{},{{"4","Digital"}}},port]

SPI master to read from slave

Schematics

Embed code

spiDevSpecs={"7"->{"SELECT","READ","READ","DESELECT"}};

ic={"12", "SPI", "SlaveSelect"->"7","Conversion"->bConversion};

bConversion[b1_,b2_]:="uint16_t sxtnBtValue = "<>b1<>" << 8 | "<>b2<>";\n0.25 * (sxtnBtValue >> 3)"

bConversion["byte1","byte2"]

oc={"1","Serial"};

ℳ=EmbedMicrocontrollerCode[(StateSpaceModelFull[1, 1, , ]),{"ArduinoUno",{ic},{oc}},port,"SPI"->spiDevSpecs]

Read data

dv =DeviceOpen["Serial",port]

Clear[parseData]
parseData[{ℳ["Serial"]["StartByte"],temp__}]:=ToExpression[FromCharacterCode[{temp}]]
parseData[___]:=Nothing

y={};
task=SessionSubmit[ScheduledTask[

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

1
]];

Dynamic[ListLinePlot[Rest@y]]

ListLinePlot[Rest@y]

TaskRemove[task];
DeviceClose[dv];

Targets

grid@MicrocontrollerTargets["Vendors"]

grid@MicrocontrollerTargets["Adafruit"]

grid@MicrocontrollerTargets["MicrocontrollerFamilies"]

grid@MicrocontrollerTargets["ATmega<f>8"]

A speed controller example

Models

tfm=(TransferFunctionModelFull[((100.23330719585297`)/(1+0.8` s)), , , ]);

ishield=
(NonlinearStateSpaceModelFull[ish=Which[0≤v<1.0699999999999998`,4.6728971962616805` v+4.367193641366059` (v^2),1.0699999999999998`≤v<3.3899999999999997`,7.186722777686323` +2.0986140154978954` v+0.4959037695265283` (v^2),3.3899999999999997`≤v<5.4`,28.00119908073134` -9.963927477053181` v+2.2509706179784903` (v^2),5.4`≤v<5.99`,117.01862664558661` -50.09895028544329` v+6.63583032239052` (v^2),5.99`≤v<6.4`,1295.1102686512763` -446.29611089560603` v+39.945163689167984` (v^2),6.4`≤v<6.66`,-13674.672489084016`+4117.308455736723` v-307.66177054389095` (v^2),6.66`≤v,100,True,0], , sp, {v}Automatic, ]);

Plot[ish,{v,0,7}, FrameLabel->{"voltage", "pwm"}]

Controller design

PIDTune[tfm,Automatic,"Feedback"];
pid=ToDiscreteTimeModel[%,sp=0.25]

comparator=(StateSpaceModelFull[1, -1, sp, , ]);

potScaler=(StateSpaceModelFull[(500/5), sp, , ]);

encoderScaler=StateSpaceModel[{{},{},{},{{((60 )/sp)(1/(12  9.68))}}},SamplingPeriod->sp]

controller=SystemsConnectionsModel[{potScaler,comparator,pid,ishield,encoderScaler},{{1,1}\[DirectedEdge]{2,1},{2,1}\[DirectedEdge]{3,1},{3,1}\[DirectedEdge]{4,1},{5,1}\[DirectedEdge]{2,2}},{{1,1},{5,1}},{{4,1},{1,1},{5,1}}]

Embed the controller

ics= {{"A5","Analog"}, {2,"RisingEdgeCount"}};

ocs={{11,"Analog","MaxValue"->100}, {1,"Serial"}, {1,"Serial"}};

ℳ=EmbedMicrocontrollerCode[controller, {"ArduinoUno",ics, ocs}, port]

Read the speeds

dv=DeviceOpen["Serial",port]

parseData[{ℳ["Serial"]["StartByte"],refs__,ℳ["Serial"]["DelimiterByte"],acts__}]:=ToExpression@FromCharacterCode@#&/@{{refs},{acts}}
parseData[___]:=Nothing

y={};
task=SessionSubmit[ScheduledTask[
If[DeviceExecute[dv,"SerialReadyQ"],
AppendTo[y,parseData@DeviceReadBuffer[dv,"ReadTerminator"->ℳ["Serial"]["EndByte"]]];DeviceExecute[dv,"ReadFlush"];
],
0.25
]];

Dynamic[ListLinePlot[y\[Transpose],PlotRange->{All,{0,550}},PlotLegends->{"reference speed","actual speed"},Filling->{1->{{2},ColorData[97,"ColorList"][[1;;2]]}}]]

ListLinePlot[y\[Transpose],PlotRange->{All,{0,550}},PlotLegends->{"reference speed","actual speed"},Filling->{1->{{2},ColorData[97,"ColorList"][[1;;2]]}}]

TaskRemove[task];
DeviceClose[dv];

Why use the Microcontroller Kit?

Automatically generate, compile, and download code

Assist in the product development cycle

Deploy real-time code

In closing ...

The microcontroller kit enables a streamlined and uniform workflow to rapidly iterate and deploy code to microcontrollers.

It has three symbols: EmbedMicrocontrollerCode, MicrocontrollerCodeData, and MicrocontrollerTargets.

The tutorial has more detailed information and additional examples.

MicrocontrollerKit`Utilities`ProjectsListing[]

Init

(*With[{defPlotOpts={PlotTheme->"Scientific",PlotStyle->Thickness[0.005],ImageSize->Large}},
SetOptions[Plot,defPlotOpts];
SetOptions[ListLinePlot,defPlotOpts];
]*)

grid[ls_]:=With[{twoCol=Partition[ls,2,2,1,""]},Grid[twoCol,Frame->All,FrameStyle->LightGray,Background->{None,None,Table[{i,j}->Opacity[0.25,RandomChoice[ColorData[106,"ColorList"]]],{i,Length@twoCol},{j,2}]//Flatten},Spacings->{1,1}]]