Getting Started

Arduino getting started

In this section, you will learn:

  • How to install and configure the Arduino IDE.
  • About the Arduino MKR1000 board.
  • How to use digital inputs/outputs and serial communication with Arduino.
  • How to use Arduino Carriers together with Arduino boards.
  • About the Arduino MKR Motor Carrier.
  • To move a motor by using the Arduino MKR Motor Carrier.

ARDUINO BOARDS FAMILY

Over the years, Arduino has developed a variety of boards , each one with different capabilities and functionalities. For instance, there is a different MKR board for each communication protocol, such as Wi-Fi, Bluetooth, LoRa, SigFox, GSM and so on. The most popular board for beginners is the Arduino UNO. Based on the ATmega328P processor from ATMEL, it is the most used board of the whole Arduino family, as well as the most robust, perfect for getting started at tinkering with the platform.

Boards released after the UNO up to 2017 have more advanced processors with more memory and enhanced input/output features, but for the most part they use the same pinout arrangements and will work with existing add-on boards, called Shields or Carriers, and various add-on components such as sensors, communication chipsets, and actuators.

 

In 2017, Arduino released the MKR1000 board, an entirely new design establishing a new standard in terms of pinout, shape, and operating voltage (3.3 V). After that, several boards have been released following this new standard to create a new subset of Arduino boards called the MKR family. As explained earlier, this family of boards is made for those who need a specific digital communication protocol (Wi-Fi, Lora, SigFox, etc.) while keeping the classic Arduino boards' ease of use.

ARDUINO MKR1000 BOARD

The Arduino MKR1000 is a powerful board based on the Atmel ATSAMW25 SoC, which has been designed to offer a practical and cost-effective solution for makers seeking to add WiFi wireless connectivity to their projects.

 

First of its family, the MKR1000 is a versatile board: it includes a LiPo charging circuit that allows it to run on batteries, has a WiFi module with a crypto-chip to ensure secure communications, and can be configured to constrain its power consumption to keep it running for long periods.

 

The WiFi connectivity makes it the perfect board for any project related to the Internet of Things. It allows communication with hundreds of online services that offer RESTful APIs, such as Twitter, GitHub, IFTTT, or Telegram, to name a few. The board can also be used as a WiFi access point, creating a local network that can run a small web server hosting websites and online interfaces.

The MKR1000 board has 28 pins, some of which have fixed functionalities and some of which have configurable functionalities. Fixed functionality pins include GND and 5V, which provide access to the board's ground and 5-volt reference voltage lines respectively. The pins labeled A0 to A6, are analog pins. These pins can transmit or receive voltage values between 0 and 3.3 volts, relative to GND. Finally, the pins labeled 0 to 14 are digital pins. When these pins transmit data, they can only transmit voltage values of 0 or 3.3 volts, relative to GND. When they receive data, the voltage is interpreted as HIGH or LOW, relative to some threshold between 0 and 3.3 volts. Note that you must never apply more than 3.3 volts to the board's digital and analog pins. Care must be taken when connecting sensors and actuators to assure that this limit of 3.3 volts is never exceeded. Connecting higher voltage signals, like the 5V commonly used with the other Arduino boards, will damage the MKR1000.

WORKING WITH DIGITAL OUTPUTS

The pins on the Arduino boards can be used to control and communicate with external components, such as LEDs, motors, or sensors. Pins can be configured either as INPUT or OUTPUT. Pins configured as INPUT enable the board to read values from external components attached to the pin, such as the light intensity from a phototransistor or the state of a button. When pins are configured as OUTPUT, the board will be able to control and communicate with actuators, lighting up an LED or playing sound on a buzzer.

 

Pins configured as OUTPUT are said to be in a low-impedance state. In this configuration, the pin can provide current to other circuits. Output pins in the MKR1000 can source up to 7 mA (milliamps) of current per I/O pin to other devices and circuits. This means that you cannot, for example, drive a motor directly from a pin because the current will not suffice. You will need to consider the use of drivers of different types to achieve different tasks like lighting up high power LEDs, driving motors, or activating relays.

 

Digital pins can only have two states, ON and OFF or, as labeled in the Arduino programming language, HIGH and LOW. We use the digitalWrite() function to set a pin to the desired state , indicating between the brackets which pin we want to act upon and the state we want it to be in.

INSTALL THE ARDUINO IDE

To test the MKR1000 board included in the kit, you need to install the Arduino IDE. There are two different versions of the IDE: the classic offline Java IDE, and the newer cloud-based Create IDE. At the time of writing, the interaction between MATLAB and Arduino is possible only through the classic IDE and therefore the following explanations will be about configuring this specific tool. The classic Arduino IDE can be downloaded from the following link. Choose the version corresponding to the operating system in your computer and follow the instructions. This page in the Arduino server will give you more information about this process.

 

The classic Arduino IDE is a relatively small download (about 100 MB) and is tailored to suit the needs of traditional Arduino users, who typically own an Arduino UNO, Nano, or Micro board. However, the Arduino MKR family needs a different toolchain, the set of tools to produce binary executables for Arduino from code, that is a lot heavier and that must be downloaded separately. This operation is very simple and can be done directly from within the IDE. The whole operation is described in Arduino's getting started guide to MKR1000, which you can find here.

 

By installing the necessary files to compile programs for the MKR1000, you will also install the drivers needed for your computer to identify the board and communicate with it. The three main operating systems, Windows, Linux, and Mac OSX, handle the connection of new USB devices differently. In Windows, Arduino boards are typically listed as serial ports and labeled with the term COM followed by a number. In Linux, the boards show up as a peripheral under the /dev/ folder, more specifically /dev/ttyACM and a number. Finally, Mac OSX detects Arduino boards under the /dev/tty.usbmodem descriptor (also followed by a number). For more in-depth explanations of how to troubleshoot possible issues arising when trying to detect the boards, please check the getting started guide for the MKR1000 once more, or contact the Arduino support line if you need more help.

CONNECT THE MKR1000 TO YOUR COMPUTER USING THE USB CABLE AND TRY IT OUT

Let's make sure the installation of the IDE and drivers was successful by trying out the simplest example included with the Arduino IDE. The so-called Blink example (open it at File>Examples>Basics>Blink), will be turning the on-board LED on and off in an endless loop.

All Arduino boards have an on-board LED connected to one of the digital pins, although the location may vary from board to board. On the MKR1000 the LED is connected to pin number 6, while on the classic Arduino UNO board it is connected to pin 13. The Arduino programming environment has a system constant called LED_BUILTIN that represents the LED pin for the specific board being programmed. Let's take a quick look at the code for this example:

The setup() function contains instructions that must be executed only once as a way to configure the board. In it, we set the pin attached to the LED as an OUTPUT using the function 'pinMode().'

The loop() function contains instructions that will be looped consecutively. As you can see, we first turn on the LED by setting the pin to the HIGH state and then turn it off by setting the pin to LOW.

 

Between those two pin-writing actions the delay() function will stop the program from being executed for the amount of milliseconds indicated in the brackets. This enables us to see the LED changing state, since otherwise the change would be too fast to be perceived by the human eye. Upload the Blink example to the board to see the result. Instead of using the built-in LED, you could connect an external LED and use whatever digital pin you want.

Note: Remember to separate the conductive foam from all the boards before you start experimenting with them.

WORKING WITH DIGITAL INPUTS

Arduino pins are configured as INPUT by default. Pins configured this way are said to be in a high-impedance state. Input pins could be modeled as if they had a 100 Mohm resistor in series in front of the pin. This means that it takes very little current to get the input pin to change from one state to another. Pins, when configured as inputs, are useful for reading values of buttons, switches, and more generally all those sensors that have only two states: ON or OFF.

 

To read values from a pin configured as INPUT we use the digitalRead() function, indicating between the brackets which pin we want to read values from. The function returns a value that can be either HIGH or LOW (which corresponds to the integers 1 or 0). Open the Button example in the Arduino IDE (located in the menu File>Examples>Digital>Button) and look at it. The following image shows the example opened in the IDE.

In this example, we first declare the variables we will use, such as buttonPin and ledPin; these are just human-readable names we can give to the pins connected to the button and to the LED. There is a third variable, called buttonState, that will be used to store the value returned from reading the pin connected to the button.

 

Note: To try the code, we will suggest that you build a circuit, and you will need to change the code to correspond to the circuit. In the illustration that follows, the button will be connected to pin number 6, so you will have to change the initial declaration of buttonPin to correspond to this case. We then read the state of the button and store it in buttonState as you can see in the following code snippet:

Using an if statement, we can then turn the LED ON or OFF according to the state of the button., ON if the button is pressed and OFF if it is not.

COMMUNICATING TO ARDUINO VIA SERIAL

Arduino boards communicate with computers and other devices via serial communication. All Arduino boards have at least one serial port (also known as a UART or USART). Serial data consists of a series of 1's and 0's sent over the USB cable at a certain speed. Data can be sent both from the board to the computer and vice-versa.

 

The Arduino IDE as well as the Create Editor hasve a feature called the Serial Monitor, that is of great help when debugging sketches. (Sketch is the way we call to Arduino programs.) The Serial Monitor is a pop-up window in the classic IDE acting as a separate terminal that communicates by receiving and sending serial data; you invoke it by pressing the magnifying glass icon at the right side of the toolbar on the IDE. The Create Editor has the Serial Monitor implemented as a layer that overlays part of the editor dedicated to displaying the file navigation.

To use serial communication with Arduino, we first need to initialize it by calling the function Serial.begin(). Between the brackets we type the baud rate, which is the data rate in bits per second (baud) for serial data transmission. The most common baud rate used is 9600. You should be aware that different devices and systems communicate at different speeds, so you need to configure both ends of the communication (emitter and receiver) to operate at the same ratio of bits per second.

 

Try out how the serial port works through an example. Open the DigitalReadSerial example ( File>Examples>Basics>DigitalReadSerial ) and check the code.

This example illustrates how to read values from a button connected to pin number 2 and print those values on the Serial Monitor using the Serial.println() function.

 

Depending on whether the button is pressed, a 0 or a 1 will be printed on the console. The circuit configuration is the same as the button example used earlier in the chapter.

ARDUINO SHIELDS/CARRIERS

Shields/Carriers are boards that can be plugged on top or underneath an Arduino board to extend its capabilities. They are generally used to simplify connecting sensors and actuators that either require a lot of electronics to be implemented, that exist only as surface mounted components, or that are used very often.

 

Since there are different Arduino boards with different pinouts and shapes, there are different Shields/Carriers as well. The main difference is between the old Arduino family (based on the UNO pinout) and the new MKR family. It is also possible to use the shields made for the older boards with the newer ones by using a simple adapter Shield. An example of a shield made for the MKR1000 family is the MKR relay proto shield that you can see in the following image:

The MKR Relay Shield can be plugged underneath any board of the MKR family, allowing you to easily control two relays without the effort of wiring them up. Other shields add communication capabilities to the boards. The GSM Shield, for instance, allows the Arduino board to make phone calls, send SMS messages, and connect to the internet.

Another kind of shield that sometimes comes in handy is the MKR Proto Shield. This is a shield that offers space to solder components onto it, making the project more compact and robust.

While some of the above-presented Shields add the possibility of connecting sensors or actuators in an easier way, others include complex devices that require extra blocks of code to handle them. Thus, some Shields (such as the GSM one) come with new libraries of code that must be added to the IDE. You can read more about how to find and add new libraries to your Arduino IDE at this link.

 

This section has introduced you to the idea of Shields; the next section will present the Carrier that was created especially for this kit, the MKR Motor Carrier.

ARDUINO MKR MOTOR CARRIER

The MKR Motor Carrier (also known as MKR motor carrier board) is an MKR add-on board designed to control servo, DC, and stepper motors. The Carrier can also be used to connect other actuators and sensors via a series of 3-pin male headers.

 

The summary of features is shown below:

 

  • Compatible with all the boards in the MKR family

 

  • Four simultaneous servo motor outputs

 

  • Up to four simultaneous DC motors (two high performance + two standard performance)

 

  • Two simultaneous stepper motors (alternative to the DC motors)

 

  • Sensing of current feedback for the high-performance motors

 

  • Two inputs for encoder sensors

 

  • Four inputs for analog sensors (3-pin compatible)

 

  • Possibility to read the status of the batteries

 

  • ON-OFF switch with Power ON LED

 

  • LiPo battery connector (2S or 3S compatible) and power terminal block for alternative power source

 

  • LEDs to visually indicate the direction of the rotation of the DC motors

 

  • On-board processor for automated control of some of the outputs

 

  • I2C connector as a 4-pin male header

The MKR Motor Carrier features two MC33926 motor drivers for high-performance DC motor control with direct connection to the MKR1000, current feedback, and capacity for up to 5 Amps (peak). In addition, there are two DRV8871 drivers that are controlled from a SAMD11 microcontroller that communicates with the MKR1000 via I2C (SPI optional). The SAMD11 is also used to control the servos, read the encoders, and read the battery voltage. There is an interrupt line connecting the SAMD11 (on PA27) and the MKR board.

 

Electrical characteristics:

  • Reverse current protection in the power pins

 

  • Max current MC33926: 5 Amps Peak, RMS current depending on the degree of heatsink provided

 

  • Max current DRV8871: 3 Amps peak, current limited by current sense resistor

 

  • Rated voltage: 6.5V to 20V

 

Note that for extended use or high-current motors, an extra heatsink (and eventually a fan) might be required for the drivers. For the examples and projects included in this kit, it is not needed. There will be specific explanations provided at different points in the documentation regarding how to safely operate the Carrier, not following them will put the Carrier, the motors, and eventually the MKR1000 at risk.

When plugging the MKR1000 and the Motor Carrier, some of the pins will stop being available for you to use in your code, as they will be needed to control some of the features of the Carrier. For example, the current feedback from the two MC33926 drivers is connected directly to some of the analog pins on the MKR1000. The following list explains which pins of the MKR1000 are used to control the Carrier:

 

  • Analog pin A3 for current feedback from Motor3

 

  • Analog pin A4 for current feedback from Motor4

 

  • Digital pin D2 for IN2 signal for Motor3

 

  • Digital pin D3 for IN1 signal for Motor3

 

  • Digital pin D4 for IN2 signal for Motor4

 

  • Digital pin D5 for IN1 signal for Motor4

 

  • Digital pin D6 for Interrupt signal from the SAMD11 to the MKR1000

 

  • Digital pin D11 for the SDA signal (I2C)

 

  • Digital pin D12 for the SCL signal (I2C)

 

Also, some pins can optionally be connected via a soldering jumper or a 0 Ohm resistor. These pins are:

 

  • Digital pin D1 for the SF signal from the MC33926 drivers (optional)

 

  • Digital pin D7 for the SPI SS signal (optional)

 

  • Digital pin D8 for the SPI MOSI signal (optional)

 

  • Digital pin D9 for the SPI SCK signal (optional)

 

  • Digital pin D10 for the SPI MISO signal (optional)

To use the Carrier, you will need to plug an MKR board (the MKR1000, MKR Zero, or other) on the headers at the center of the board. Make sure the MKR board is connected in the proper direction. You can do this by making sure the info printed on the side of the headers are matching for both the MKR board and the Motor Carrier.

 

Once the board is properly connected to the Motor Carrier, you can start programming the board. To control the motors, you will need to import the MKR Motor Carrier library. You can find more information about the library here. If you want to read more about how to install a library, follow this link.

 

When working with motors, you will need an external source to feed the motor drivers and power up the motors. You can do this by connecting a LiPo Battery to the battery connector or using an external power source and connecting it to the VIN input on the terminal block. It is recommended to do these operations with the power switch at the OFF position. Once the external power is connected to the board, the power switch can be turned on.

 

In the projects of this kit, we will be using the Motor Carrier with an MKR1000 because of the Wi-Fi capabilities of that board. Thanks to a special configuration within MATLAB, the board can communicate directly to the software as long as they are on the same wireless network.

The chosen encoder hardware provides a resolution of 12 counts per motor shaft revolution (see technical specifications at this link). Thus, you can convert the motor shaft's encoder count to the physical angle of the motor shaft in degrees as follows:

>> shaftAngle = readCount(enc)*360/12

This DC motor has a gear ratio of 100.37:1 (see technical specifications at this link) between the motor shaft and the output shaft that attaches to the device you're driving, such as a wheel. Use the following commands to get the angle of the output shaft in degrees, and then normalize it to the range of 0 to 360 degrees:

>> axleAngle = readCount(enc)360/12/100.37
>> axleAngleNorm = mod(axleAngle,360

Now let's displace the output shaft angle using the DC motor rather than manual rotation:

>> dcm.DutyCycle = 0.5;
>> start(dcm)
>> readCount(enc)
>> readCount(enc)
>> readCount(enc)
>> stop(dcm)

Now you're able to determine the angle of the motor shaft and output shaft, and thus changes in angle over some interval of time. To get the angular speed, you need to also measure the duration of that interval. In MATLAB, you can measure elapsed time using the tic and toc functions. The first command, tic, will start a timer when issued; the second one, toc, will read the elapsed time since the last time tic was invoked. Try entering the commands below to understand how tic and toc work:

>> tic
>> toc
>> toc
>> dt = toc
>> tic
>> toc
>> toc

Now let's try the following: while we're measuring the change in encoder count, let the motor run. It is important that we measure the starting angle and start the timer simultaneously. To do this, we'll start the motor, and then do both actions in a single MATLAB command:

dcm.DutyCycle = 0.5;
start(dcm)
startCount = readCount(enc);tic

Next, read the encoder count again while simultaneously measuring the elapsed time, and then stop the motor.

stopCount = readCount(enc); dt = toc
stop(dcm)

Now let's determine the speed for the output shaft, first in counts per second, and then in degrees per second. Use the following commands:

>> speed = (stopCount - startCount)/dt
>> convSpeed = (stopCount - startCount)*360/dt/12/100.37

CHARACTERIZING A DC GEAR MOTOR IN MATLAB

In this section, you will write a MATLAB program that automatically measures the motor speed while issuing many different PWM commands to characterize the response of the 100:1 DC gearhead motor. Later, you will use these observations to determine the required PWM command to rotate the motor at a desired rotational speed. Using the Live Editor, you will compose a MATLAB live script that performs the following tasks:

In this section, you will learn:

  • How to automate processes in MATLAB using a script.
  • How to use code sections to partition scripts into smaller parts.
  • How to use for-loops to repeat blocks of code.
  • How to use text labels and comments to organize and document MATLAB scripts.
  • How to create and call a MATLAB function.

WRITING MATLAB SCRIPTS

Up to this point, you have been typing commands directly on MATLAB's command line. If you perform certain operations many times, it might be useful to group those lines of code into scripts that you can easily call when needed. These are called live scripts in MATLAB's jargon. Begin by opening the Live Editor window. In the MATLAB Toolstrip , click New Live Script.

In the Live Editor window, you can type the same MATLAB commands as you would in the Command Window, and then execute them in batches. Start your live script by typing a few simple lines of code:

Execute your live script by clicking Run:

a = 17
b = -2
x = 0:0.5:4
y = sqrt(x)

Execute your live script by clicking Run:

Your commands execute sequentially, and assignment expressions modify variables in the MATLAB Workspace. Examine the Workspace and locate the new variables. As you can see, the results of expressions with unsuppressed output are displayed in the right column:

Now suppress the output (which consists of adding a semi-colon at the end of the command), and plot y against x by adding the code in bold:

a = 17;

b = -2;

x = 0:0.5:4;

y = sqrt(x);

plot(x,y)

title('Square roots')

Run the script and examine the embedded figure in the output column. Then clear your Workspace in the Command Window:

**>> clear**

Now run your live script again:

Examine the Workspace . You can see which information is being sent back to the command line and which isn't, depending on whether the variables are suppressed or not. Now that you have learned how to use live scripts to execute series of commands multiple times, we can use that knowledge to work with DC motors and characterize their behavior in terms of the PWM signal applied to them.

SETTING UP ANALYSIS DATA

Let's begin writing our motor characterization program. Delete the existing code in your live script, and type the following lines to define your test PWM command values:

maxPWM = 1.00;
incrPWM = 0.05;
PWMcmdRaw = (-maxPWM:incrPWM:maxPWM)';

Run your live script and examine your Workspace. You have created a vector containing a series of numbers ranging from -1 to 1 with 0.05 increments. That will be used to generate the PWM signal we will apply to the DC motor to characterize its behavior.

CREATING ARDUINO AND EXTERNAL DEVICE OBJECTS

Now let's add some code to create and/or initialize the four device objects that we will need for this experiment: Arduino ( a ), the DC motor (dcm), the Carrier (carrier), and the encoder (enc). Add the following bold lines of code to your live script:

maxPWM = 1.00;
incrPWM = 0.05;
PWMcmdRaw = (-maxPWM:incrPWM:maxPWM)';
clear a dcm carrier enc
a = arduino;
carrier = addon(a,'Arduino/MKRMotorCarrier');
dcm = dcmotor(carrier,3);
enc = rotaryEncoder(carrier,1);
resetCount(enc);

Rerun your live script to ensure correct behavior; you can check the results by looking at the Workspace. If everything went as expected, your live script now includes all the objects needed to perform the test.

USING SECTIONS, TEXT, AND COMMENTS

Before we proceed with the testing, let's make the script easier to understand for somebody less familiar with the program. First, you can break down the program into sections, to divide it into logical steps. To partition your code, place the text cursor immediately before the clear command, and click Section Break.

Next, let's add some labels to the sections of the live script to explain what each one does. Place your text cursor at the beginning of the document and click Text:

Now you can add a title and explanation for each section. Using plain text in the Live Editor, add the following labels. Use the formatting buttons in the Text panel of the toolstrip:

  1. Create test data
clear a dcm carrier enc% delete existing device objects

maxPWM = 1.00;
incrPWM = 0.05;
PWMcmdRaw = (-maxPWM:incrPWM:maxPWM)';
  1. Create and initialize device objects
clear a dcm carrier enc

a = arduino;
carrier = addon(a,'Arduino/MKRMotorCarrier');
dcm = dcmotor(carrier,3);
enc = rotaryEncoder(carrier,1);
resetCount(enc);

Any portions of your live script that are written in plain text have no impact on how the script executes.

 

When you partition a live script into sections, you can execute each section one at a time. This can be useful if you want to just test one section without running the whole live script. You may also want to step through your code section by section, so that you can view and analyze intermediate results. Place your text cursor in the first section and click Run and Advance.

Click Run and Advance once more (or click Run Section or Run to End) to complete execution of the live script. Now clear your Workspace, and run your live script section by section, examining the Workspace after each section executes. You can also add comments to individual lines of code, if you want to add further explanation at a lower level. You can begin a comment directly in the MATLAB code by using the percent (%) character. Anything that follows % will not be executed in the live script. Add the following comments to existing lines of code:

  1. Create test data
maxPWM = 1.00;% maximum duty cycle
incrPWM = 0.05;% PWM increment
PWMcmdRaw = (-maxPWM:incrPWM:maxPWM)';% column vector of duty cycles from -1 to 1
  1. Create and initialize device objects
clear a dcm carrier enc% delete existing device objects

a = arduino;
carrier = addon(a,'Arduino/MKRMotorCarrier');
dcm = dcmotor(carrier,3);
enc = rotaryEncoder(carrier,1);
resetCount(enc);

AUTOMATING MOTOR SPEED MEASUREMENT

After this short break where you learned how to better comment your code to make it easier to remember what each part of the code is doing, it is time to test the motor's behavior. MATLAB helps in automating tests. Previously, you have prepared the code by creating the objects that connect to Arduino, the Motor Carrier , and the encoder.

Now you will add a new section of code that starts the motor with a PWM value, reads the encoder count at two different times, derives the speed, and stops the motor. Add the following section to your live script to measure the speed for the first PWM value:

  1. Measure raw motor speed for each PWM command
dcm.DutyCycle = 0;
start(dcm) % turn on motor
resetCount(enc);
startCount = readCount(enc);
tic; 
dcm.DutyCycle = PWMcmdRaw(1);
pause(1) % wait for steady state
stop(dcm);% turn off motor
dt = toc;
endCount = readCount(enc);
speedRaw(1) = (endCount - startCount) / dt; % calculate speed (cts/s)
dcm.DutyCycle = 0;

Try running this section with different index values of PWMcmdRaw and speedRaw. In the example code, the index is 1; we take the first index of PWMcmdRaw, send it to the motor, make two readings, and store the speed resulting from the calculation in the first element of speedRaw. Examine the results in the MATLAB Workspace.

ITERATING OVER MULTIPLE MEASUREMENTS

You now have a live script that you can use to get speed measurements for any PWM command value. Let's generalize this section of the script so that it measures the speed for all the values in the vector PWMcmdRaw. You can repeat parts of your code using a for-loop. A for-loop is a block of code that executes a known number of times, called iterations. To create a for-loop, you need a header (which defines the number of iterations), and the end keyword (which defines the end of the repeating code). A for-loop checks the value of an index variable to decide whether the condition to leave the loop has been met. The index indicates the current iteration. Here is a simple example of a for-loop:

 
**for** idx = 1:10
> y(idx) = (2 + idx) / idx;
**end**
 

This for-loop executes for 10 iterations, and the index variable idx takes a new scalar value from 1 to 10 during each iteration.

In our case, you want to iterate through all elements of PWMcmdRaw, and take a new speed measurement during each iteration. The result should be a vector that is the same size as PWMcmdRaw. Add a for-loop header and an end keyword to your code, as shown below. Remember to update the indices in PWMcmdRaw and speedRaw to the index variable, ii.

  1. Measure raw motor speed for each PWM command
dcm.DutyCycle = 0;
start(dcm)% turn on motor
**for** ii = **1:length(PWMcmdRaw)**
> resetCount(enc);
> startCount = readCount(enc);
> tic;  
> dcm.DutyCycle = PWMcmdRaw(ii);  
> **start(dcm)**% turn on motor  
> pause(1)% wait for steady state  
> stop(dcm); % turn off motor
> dt = toc;
> endCount = readCount(enc);
> speedRaw(ii) = (endCount - startCount) / dt;% calculate speed (cts/s)
> **pause(1)**;
**end**
**stop(dcm)** % turn off motor
dcm.DutyCycle = 0;

Run the section and ensure that all possible PWM values in PWMcmdRaw are tested. At this point, the Live Editor window should be showing a warning on the assignment to speedRaw(ii). Hover over the speedRaw variable in the Live Editor window to see details about the warning:

The warning appears because the script is assigning values to increasing indices of speedRaw, and as a result speedRaw needs to increase its size on every iteration to accommodate the new element. In some cases, this can lead to performance issues because the variable's memory may need to be reallocated many times. You can solve this problem if you know how large the array is going to get by defining the vector in advance.

Before assigning values to an array inside a for-loop, you should allocate memory for the array, so that it will not get resized inside the loop. The zeros function is often used for this purpose. Add the following bold line of code before the for-loop to allocate space for speedRaw before populating it with values:

 

  1. Measure raw motor speed for each PWM command
**speedRaw = zeros(size(PWMcmdRaw));** % Pre-allocate vector for speed measurements

dcm.DutyCycle = 0;

start(dcm)% turn on motor

**for** ii = 1:length(PWMcmdRaw)

>resetCount(enc);

>startCount = readCount(enc);

>tic;

>dcm.DutyCycle = PWMcmdRaw(**ii**);

>start(dcm)% turn on motor

>pause(1)% wait for steady state

>stop(dcm);% turn off motor

>dt = toc;

>endCount = readCount(enc);

>speedRaw(ii) = (endCount - startCount) / dt;% calculate speed (cts/s)

>pause(1);

**end**

stop(dcm)% turn off motor

dcm.DutyCycle = 0;

VISUALIZING MEASURED SPEED VS. PWM DUTY CYCLE

Now you have some real speed measurements in your Workspace. Let's visualize them against their corresponding PWM command values using the plot command we learned about earlier. Add the following section to your live script to plot and annotate the raw data:

 

  1. Graph raw data
plot(PWMcmdRaw,speedRaw)% raw speed measurements
title('100:1 Gearbox Motor Steady State Response')
xlabel('PWM Command')
ylabel('Measured Speed (counts/sec)')
 

Run the section and examine the figure in the output column of the Live Editor:

Notice some interesting features of the speed-PWM relationship that you can deduct from studying the graph. There is a "dead zone" around PWM=0, where there is zero rotational speed for non-zero PWM commands. Furthermore, there may be some non-monotonic portions of the curve, where the measured speed does not increase with increasing PWM command. This typically happens around PWM = +/-1, but could also result from experimental error.

POST-PROCESSING SPEED MEASUREMENTS

Collecting this data will be relevant later when you want to model how a more complex machine that uses several motors behaves. Later you will create a motor control system in Simulink, in which a user will request a motor speed in counts per second. The system will calculate the required PWM command to run the motor at that speed and send it to the Arduino board. To perform this operation correctly, the controller needs a one-to-one mapping between motor speeds and PWM commands. This means that there can only be one PWM command per speed, so you must remove repeated values and nonincreasing values from the data. First you must identify which values of speedRaw are not in increasing order. Examine speedRaw in the Command Window:

>> speedRaw

Locate the multiple zeros in the speed measurements and look for nonincreasing values.

 

It is not easy to see where the nonincreasing values are, so let's make a first-order difference. Enter the following command:

>> diff(speedRaw)

This will answer with yet another vector showing the differences between pairs of values in the array. To remove non-increasing values in speedRaw, you need to know the indices where diff(speedRaw) is nonpositive. To do this, you can use relational operators to create a logical condition. Enter the following logical expressions, and interpret the result:

>> pi > 3

>> pi >= 3

>> pi == 3

>> pi < 3

>> x = 1:5

>> x == 3

>> x < 3

>> y = x < 3

For the first half of the previous commands (all the ones related to pi), the operations will produce a different result based on the level of truth of the statement.

For the second set of operations (the ones related to the variables x and y ), the results are different, since x is an array containing the numbers 1 to 5. Examine x and y in your Workspace. The numeric vector x is the same size as the logical index vector y. You can use a logical index expression or variable to index into a variable of the same size. Try the following commands:

>> x(y)

>> x(~y)

>> z = rand(1,5)

>> z(y)

Now let's get the logical indices and values of speedRaw where it is strictly increasing. Enter the following commands:

>> idx = diff(speedRaw) > 0

>> speedMono = speedRaw(idx);**

How does the size of speedMono compare to that of speedRaw? They are different; think about why this is the case. Note: To get help with built-in MATLAB functions like diff, rand, and tic, use the doc command to access the documentation. For example:

>> doc diff

The documentation for each function will show you the various ways the function can be called, and examples for each syntax.

Now you have made a monotonic version of the speed measurements, but there are some problems. First, how do you now align the PWM command values to the new speed vector if they are different sizes? You can use the same logical index to isolate the corresponding PWM values. Use the following command to filter the PWM values, and plot both the raw and filtered values:


>> PWMcmdMono = PWMcmdRaw(idx);
>> plot(PWMcmdRaw,speedRaw,PWMcmdMono,speedMono)

To illustrate a second issue that arises when post-processing the information obtained from the motor, enter the following commands:


**>> speedMono == 0**
**>> PWMcmdMono(speedMono == 0)**

Due to the way we filtered out the non-monotonic points, a speed of zero is to be driven by a non-zero PWM. Although commanding a small enough PWM value will result in zero speed due to friction, it would waste power, especially if the system commands zero speed for a long time. Therefore, you should change that value of PWMcmdMono to 0, which will have the same result. Enter the following command:

&nbsp:

**>> PWMcmdMono(speedMono == 0) = 0;**

&nbsp;

Let's incorporate the post-processing into the live script. Add a new section before the section labeled Graph Raw Data , and type the following code:

 

  1. Post-process and save data
idx = (diff(speedRaw) > 0);% find indices where vector is increasing

speedMono = speedRaw(idx);% Keep only increasing values of speed

PWMcmdMono = PWMcmdRaw(idx);% Keep only corresponding PWM values

PWMcmdMono(speedMono == 0) = 0;% enforce zero power for zero speed

save motorResponse PWMcmdMono speedMono% save post-processed measurements

Now update the Graph Raw Data section to include both the raw data and post-processed data. Add the bold lines of code as follows:

  1. Graph raw and post-processed data
plot(PWMcmdRaw,speedRaw)% raw speed measurements

**hold on**

**plot(PWMcmdMono,speedMono)%** non-monotonic measurements filtered out

title('100:1 Gearbox Motor Steady State Response')

xlabel('PWM Command')

ylabel('Measured Speed (counts/sec)')

**legend('Raw Data','Monotonic Data')**

Finally, delete all device objects to release the MKR1000 board to be used in other processes. Add the following section at the end of your live script:

6. Delete device objects

 

clear a dcm carrier enc

SAVING AND RERUNNING SCRIPTS

You now have a fully functional live script that automatically performs a series of measurements, post-processes those measurements, saves the experimental data to disk, and plots the results. You may want to repeat this analysis multiple times, for the same motor or perhaps other DC motors. You can save your live script as a .mlx file so that you can run it again later or share it with other MATLAB users. Save your live script as myMotorCharacterization.mlx. A .mlx file contains not only your code and text annotations, but also any numeric or graphical results that were displayed in the output column. Thus, your live script is an active report of your analysis and results. Recall that you can run your live script in its entirety using the Run button in the Live Editor window:

You can also run the live script directly in the Command Window using its name as the command. Try running your live script:

 

>> myMotorCharacterization

CREATING MATLAB FUNCTIONS

Look at your Workspace. Which of the existing variables are useful to you? Which variables are intermediate values from your live script that you no longer need?

 

You can compartmentalize parts of your MATLAB program into functions, so that the intermediate calculations are hidden and you only need to manage inputs and outputs.

 

Looking at your live script, the first two sections are specific to this DC motor, encoder, and test case (PWMcmdRaw). The following three sections (measure raw motor speed for each PWM command, post-process and save data, and graph raw and post-processed data) can be generalized for any DC motor, encoder, and test case. Let's create a MATLAB function for that code. Start in the Live Editor window by clicking New>Live Function.

Next, you need to determine what the inputs and outputs to your function are. Section 3 of your live script requires the test vector PWMcmdRaw, the DC motor object dcm, and the encoder object enc.

 

The next line of code is an example of a possible function header. Place the inputs in parentheses ( () ) with the three variables listed above:

function z = Untitled(PWMcmdRaw,dcm,enc)

Now consider what variables you want to access from the motor characterization algorithm. The only meaningful variables created in these sections are PWMcmdMono and speedMono. In the function header, list these two variables as outputs in square brackets ([ ]).

function [PWMcmdMono,speedMono] = Untitled(PWMcmdRaw,dcm,enc)

Finally, let's name the function so that we know how to call it in MATLAB code. Replace the function name with myMotorFunction:

**function** [PWMcmdMono,speedMono] = myMotorFunction(PWMcmdRaw,dcm,enc)

Now let's fill in the algorithm. Cut and paste sections 3, 4, and 5 from your live script to your live function, between the function header and the end keyword.

**function** [PWMcmdMono,speedMono] = myMotorFunction (PWMcmdRaw,dcm,enc)

3. Measure raw motor speed for each PWM command

speedRaw = zeros(size(PWMcmdRaw));% Preallocate vector for speed measurements

dcm.DutyCycle = 0;

start(dcm)% turn on motor

**for** ii = 1:length(PWMcmdRaw)

> resetCount(enc);

> startCount = readCount(enc);

> tic;

> dcm.DutyCycle = PWMcmdRaw(ii);

> start(dcm)% turn on motor

> pause(1)% wait for steady state

> stop(dcm);% turn off motor

> dt = toc;

> endCount = readCount(enc);

> speedRaw(ii) = (endCount - startCount) / dt;% calculate speed (cts/s)

> pause(1);

**end**

stop(dcm)% turn off motor

dcm.DutyCycle = 0;

4. Post-process and save data

idx = (diff(speedRaw) > 0);% find indices where vector is increasing

speedMono = speedRaw(idx);% Keep only increasing values of speed

PWMcmdMono = PWMcmdRaw(idx);% Keep only corresponding PWM values

PWMcmdMono(speedMono == 0) = 0;% enforce zero power for zero speed

save motorResponse PWMcmdMono speedMono% save post-processed measurements

5. Graph raw and post-processed data

plot(PWMcmdRaw,speedRaw)% raw speed measurements

hold on

plot(PWMcmdMono,speedMono)% non-monotonic measurements filtered out

title('100:1 Gearbox Motor Steady State Response')

xlabel('PWM Command')

ylabel('Measured Speed (counts/sec)')

legend('Raw Data','Monotonic Data')

**end**

Save the file as myMotorFunction.mlx. Now you have a working MATLAB function that you can call from any MATLAB code environment. Let's try calling it from your live script. In the empty section of your live script, enter the call to your function:

1. Create test data

maxPWM = 1.00;% maximum duty cycle
incrPWM = 0.05;% PWM increment
PWMcmdRaw = (-maxPWM:incrPWM:maxPWM)';% column vector of duty cycles from -1 to 
1

2. Create and initialize device objects

clear a dcm carrier enc% delete existing device object
a = arduino;
carrier = addon(a,'Arduino/MKRMotorCarrier');
dcm = dcmotor(carrier,3);
enc = rotaryEncoder(carrier,1);
resetCount(enc);

3-5. Call motor characterization function

[PWMcmdMono,speedMono] = myMotorFunction(PWMcmdRaw,dcm,enc);

Now run your live script and ensure it still works as before.

To further understand the benefits of using MATLAB functions, clear your Workspace in the Command Window , and run your live script again.

>> clear
>> myMotorCharacterization

Examine your Workspace. The list of variables should be more concise now. There is one more thing you may want to generalize in this function, and that is the name of the MAT-File that we use in the save command. As of now, the analysis data gets saved to the same MAT-File, motorResponse.mat, every time you call the function. This is not very useful if you want to characterize multiple DC motors. Let's add a new input to the function for the file name. In the live function, add the new input as shown below:

function [PWMcmdMono,speedMono] = myMotorFunction(PWMcmdRaw,dcm,enc,filename)

Now let's use the new input in the save command. Rewrite the save command as follows :

 save(filename,'PWMcmdMono','speedMono')% save post-processed measurements 

Finally, we need to modify the function call, since the function header has changed. In the live script, change the function call as follows:

[PWMcmdMono,speedMono] = myMotorFunction(PWMcmdRaw,dcm,enc,'motorResponse');

Now you can save the analysis data to any MAT-file you want each time you call myMotorFunction. Try calling your live script once more:

>> myMotorCharacterization