0% found this document useful (0 votes)
16 views58 pages

3 Variables

Uploaded by

thembelihle.mng
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views58 pages

3 Variables

Uploaded by

thembelihle.mng
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 58

Arduino

Arduino Uno ATmega328 Arduino Mega ATmega2560

Variables
Created by: Dr. Daniel van Niekerk
Constants
• Description
̶ Constants are predefined expressions in the Arduino language.
̶ Constants are used to make programs easier to read.
̶ Constants are classify in the following groups:
> Logical Levels: true and false (Boolean Constants)
> Pin Levels: HIGH and LOW
> Digital Pin modes: INPUT, INPUT_PULLUP and OUTPUT
> Built-in LED constant: LED_BUILTIN
Logical Levels: true and false
• Description
̶ There are two constants used to represent true and false in the Arduino language.
• false
̶ false is always defined as “0” (zero).
• true
̶ true is often defined as “1” which is correct, but it has a wider definition.
̶ In the boolean sense any integer which is non-zero is true.
̶ For example: -3, -1, 4 and 250 are all defined as true.
̶ Note that true and false constants are typed in lowercase unlike HIGH, LOW, INPUT,
INPUT_PULLUP, OUTPUT and LED_BUILTIN.
Pin Levels: HIGH and LOW
• Description
̶ When reading or writing to a digital pin there are only two possible values a digital pin
can be at or set to, either HIGH or LOW.
• HIGH
̶ The meaning of HIGH in reference to a port pin is different depending on whether it is
configured to an INPUT or OUTPUT port pin.
̶ When a port pin is configured as an INPUT with pinMode() and read with digitalRead(),
the Arduino port pin will report HIGH if:
> a voltage > 3 volts is present at the input pin (5 V Arduino boards).
> a voltage > 2 volts is present at the input pin (3.3 V Arduino boards).
̶ When a port pin is configured as an OUTPUT with pinMode() and set to HIGH with
digitalWrite(), the Arduino port pin is set to:
> 5 volts for 5 V boards, or 3.3 volts for 3.3 V boards.
> In this state the port pin can source current.
> For example, light an LED that is connected through a series resistor to ground.
Pin Levels: HIGH and LOW
• LOW
̶ The meaning of LOW in reference to a port pin is also different depending on whether it
is configured as an INPUT or OUTPUT port pin.
̶ When a port pin is configured as an INPUT with pinMode() and read with digitalRead(),
the Arduino port pin will report LOW if:
> a voltage < 1.5 volts is present at the input pin (5 V Arduino boards).
> a voltage < 1 volts is present at the input pin (3.3 V Arduino boards).
̶ When a port pin is configured as an OUTPUT with pinMode() and set to LOW with
digitalWrite(), the Arduino port pin is set to:
> 0 volts for 5 V boards, or 3.3 V boards.
> In this state the port pin can sink current.
> For example, light an LED that is connected through a series resistor to +5 volts or
+3.3 volts.
• Internal pull-up
̶ To enable the internal 20 KΩ pull-up resistor on the Atmega chip, a port pin can be
configured as an INPUT with pinMode() and then made HIGH with digitalWrite().
̶ Or a port pin can also be configured as an INPUT_PULLUP with pinMode() to enable
the internal 20 KΩ pull-up resistor on the Atmega chip.
̶ The internal 20 KΩ will pull-up the configured input pin to a HIGH level, unless it is
pulled LOW by external circuitry.
Digital Pin modes: INPUT, INPUT_PULLUP & OUTPUT
• Description
̶ Digital pins can be used as INPUT, INPUT_PULLUP or OUTPUT.
̶ Changing a pin with pinMode() changes the electrical behavior of the pin.

• Pins Configured as INPUT


̶ To make it useful for reading a sensor, the Arduino port pins configured as INPUT with
pinMode() are in a high-impedance state.
̶ This means that an INPUT pin draws very little current from the sampled circuit because
it has an equivalent series resistor of 100 MΩ in front of the INPUT pin.

̶ When an INPUT pin is reading a switch and it is in an open state, the input pin voltage
will be "floating", resulting in unpredictable results.
̶ In order to assure a proper reading when the switch is open, a pull-up or pull-down
resistor must be used to pull the pin to a known state when the switch is open.
̶ A 10 to 20 kΩ resistor is usually chosen, as it has a low enough value to reliably prevent
a floating input and at the same time it has a high enough value not to draw too much
current when the switch is closed (5 V / 10 kΩ = 0.5 mA).
̶ If a pull-down resistor is used with the switch connected to 5 V or 3.3 V, the input pin
will be LOW when the switch is open and HIGH when the switch is closed.
̶ If a pull-up resistor is used with the switch connected to ground, the input pin will be
HIGH when the switch is open and LOW when the switch is closed.
Digital Pin modes: INPUT, INPUT_PULLUP & OUTPUT
• Pins Configured as INPUT with PULLUP enabled
̶ The Arduino microcontroller has internal pull-up resistors that are connect to Vcc which
is either 5 V or 3.3 V, depending on which Arduino board is used.
̶ To use these internal resistors instead of connecting external pull-up resistors, use the
INPUT_PULLUP argument in pinMode().
̶ Note that pins configured as inputs with either INPUT or INPUT_PULLUP can be
damaged or destroyed if they are connected to voltages below ground (negative
voltages) or above the positive power rail (5 V or 3.3 V).
̶ The input voltage range must be between: -0.5 V to Vcc + 0.5 V

• Pins Configured as Outputs


̶ Pins configured as OUTPUT with pinMode() are said to be in a low-impedance state.
̶ This means that they can provide a substantial amount of current to external circuits.
̶ The Arduino output port pins can source (supply) or sink (absorb) up to 40 mA of
current, to or from external connected circuits.
̶ This makes them useful for powering LEDs, because LEDs typically use less than 40 mA.
̶ Loads greater than 40 mA (e.g. motors, solenoids, servos etc…) will require a transistor
or other interface driver circuit.
̶ Note that pins configured as outputs can be damaged if they are connected to ground
and set HIGH or when connected to Vcc and set LOW, without a limiting resistor.
Built-in LED constant: LED_BUILTIN
• Description
̶ Most Arduino boards have a pin connected to a buffer Op-Amp that is then connect to a
resistor in series with an on-board grounded LED.
̶ The constant LED_BUILTIN is the number of the pin to which the on-board LED is
connected.
̶ Most Arduino boards have this LED connected to digital pin 13.
Integer constants
• Description
̶ Integer (int) constants are numbers used directly in a sketch code, like 123 or 452.
̶ By default, these numbers are treated as int, but can be change with U and L modifiers.
̶ Normally, integer constants are treated as decimal (base 10) integers.
̶ Special notation (formatters) may be used to enter numbers in other bases.
Base Formatter Example Character range
10 (decimal) none 123 Valid 0-9 characters
2 (binary) leading 'B' B11101011 0 & 1, only 8 bit numbers
8 (octal) leading "0" 0173 Valid 0-7 characters
16 (hexadecimal) leading "0x" 0x7B Valid 0-9, A-F or a-f characters

• Decimal
̶ Decimal (base 10) is the common-sense math we are acquainted with.
̶ Constants without other prefixes are assumed to be in decimal format such as:
Var = 101; // same as 101 decimal -> ((1*10^2) + (0*10^1) + 1)
Integer constants
• Binary
̶ For binary (base two) only characters 0 and 1 are valid:
Var = B101; // same as 5 decimal -> ((1*2^2) + (0*2^1) + 1)
̶ Binary values are indicated by the prefix "B“ and this binary formatter only works on
bytes (8 bits), between 0 (B0) and 255 (B11111111).
̶ If it is convenient to input an integer (16 bits) in binary form, use the following:
myInt = (B11001100 * 256) + B10101010; //B11001100 is the high byte
• Octal
̶ For octal (base eight) only characters 0 through 7 are valid.
̶ Octal values are indicated by the prefix "0" such as:
Var = 0101; // same as 65 decimal -> ((1*8^2) + (0*8^1) + 1)
̶ Note that it is possible to generate a hard-to-find bug by unintentionally including a
leading zero before any integer constant.
̶ Therefore, the compiler will unintentionally interpret the constant as an octal number.
• Hexadecimal
̶ For hexadecimal or hex (base sixteen), valid characters are 0 to 9 and letters A to F.
̶ Note that: ‘A’ = 10, ‘B’ = 11, ‘C’ = 12, ‘D’ = 13, ‘E’ = 14 and ‘F’ = 15.
̶ Hex values are indicated by the prefix "0x" and "A-F" may be upper or lower case (“a-f”):
Var = 0x101; // same as 257 decimal -> ((1*16^2) + (0*16^1) + 1)
Integer constants
• U & L formatters
̶ By default, an integer constant is treated as an int, with intended limitations in values.
̶ To specify an integer constant with another data type, follow it with:
> a 'u' or 'U' to force the constant into an unsigned data format (example: 33u).
> a 'l' or 'L' to force the constant into a long data format (example: 100000L).
> a 'ul' or 'UL' to force the constant into an unsigned long data format
(example: 65537UL).
Floating point constants
• Description
̶ Similar to integer constants, floating point constants can also be used.
̶ Floating point constants can be expressed in a variety of scientific notation.
̶ 'E' and 'e' are both accepted as valid exponent indicators.

floating-point constant evaluates to: also evaluates to:

10.0 10 10
2.34E5 2.34 * 10^5 234000
67e-4 67.0 * 10^-4 0.000067
Data Type: void
• Description
̶ The void keyword is used only in function declarations.
̶ It indicates that the function is expected to return nothing to the calling function.
̶ It can also indicate that it receives no information in the parameters to process.
• Example:
// actions are performed in the functions "setup" and "loop"
// but no information is reported back to the C control program
// and no information is received in the parameters to process.
void setup() // or void setup(void)
{
// statements...
}

void loop() // or void loop(void)


{
// statements...
}
Data Type: boolean or bool
• Description
̶ A boolean holds one of two values, true or false and occupies one byte of memory.
• Example
int pinLED = 5; // pin 5 to limiting resistor & grounded LED
int pinSwitch = 10; // momentary switch on 10, other side grounded
boolean bRunning = false; // or bool bRunning = true;

void setup(){
pinMode(pinLED, OUTPUT);
// next two lines same as: pinMode(pinSwitch, INPUT_PULLUP)
pinMode(pinSwitch, INPUT); // make the pin an input
digitalWrite(pinSwitch, HIGH); // turn on internal 20 kΩ pull-up
}

void loop(){
if(digitalRead(pinSwitch) == LOW){ // check if switch pressed
delay(10); // delay to debounce switch
if(digitalRead(pinSwitch) == LOW){ // check switch again
bRunning = !bRunning; // toggle bRunning variable
digitalWrite(pinLED, bRunning); // indicate state via LED
}
}
}
Data Type: char
• Description
̶ A char takes up one byte of memory and is used to store a character value.
̶ Character literals are written in single quotes: 'A'
̶ For multiple characters such as in strings use double quotes: "ABC".
̶ Characters are stored as numbers and have a specific encoding as listed on ASCII chart.
̶ It is possible to do arithmetic calculations with characters, where ASCII code number
value of the character is used.
̶ For example: 'A' + 1 = 66, because the ASCII code value of capital letter A is 65.
̶ char data type is a signed type and encodes numbers from -128 to 127.
̶ For an unsigned or one-byte (8 bit) data type, use the byte data type.
• Example
char cMyChar = 'A';
char cMyChar = 65;
̶ both are equivalent.
Data Type: unsigned char
• Description
̶ An unsigned char occupies one byte of memory, same as the byte data type.
̶ The unsigned char data type encodes numbers from 0 to 255.
̶ Note that for consistency with the Arduino programming style, the byte data type is
preferred.
• Example
unsigned char ucMyChar = 240;

0 0
255 -1

unsigned char range: signed char range:


192 0 to 2^8-1 = 0 to 255 63 -63 0 to 2^7-1 and -2^7 to 0 63
0 to 127 and -128 to 0

127 (2^7 -1)


127 -128
(-2^7)
Data Type: byte
• Description
̶ A byte stores an 8-bit unsigned number, from 0 to 255.
• Example
byte bMyByte = B10010; // note: (2^4 + 2^1) = (16 + 2) = 18
̶ Note that ‘B’ is a binary formatter and therefore, B10010 = 18 decimal.
̶ Maximum value that bMyByte can store is B11111111 or 255.

0
255

byte range:
192 0 to 2^8-1 = 0 to 255 63

127
Data Type: int
• Description
̶ Integers are the most often used data type for number storage.
̶ On the Arduino ATmega based boards, an int stores a 16-bit (2-byte) value.
̶ This yields a range of -32,768 to 32,767.
̶ Minimum value of (-2^15) and a maximum value of ((2^15) - 1).
̶ On the Arduino Due, an int stores a 32-bit (4-byte) value.
̶ This yields a range of -2,147,483,648 to 2,147,483,647
̶ Minimum value of (-2^31) and a maximum value of ((2^31) - 1).
̶ Integers store negative numbers with a technique called 2's complement math.
̶ The highest bit is called the "sign" bit, if set to one the number store is negative.
̶ If negative, inverted all bits and add 1, the resulting value is the negative number.
̶ Note that the Arduino compiler takes care of dealing with negative numbers.
̶ Therefore, code arithmetic operations work transparently in the expected manner.
̶ However, there can be an unexpected complication when dealing with bit-shift right “>>”.
• Example
int iData = 32767;
• Syntax
̶ int iVar = value;
• Note
̶ iVar - is the integer variable name.
̶ value - is the value assigned to the integer variable name.
Data Type: int
• Note
̶ When variables exceed maximum capacity, "roll over" back to minimum occurs.
̶ This happens in both increment and decrement directions.
• Example
int iVar; // declare 16-bit integer variable
iVar = -32768; // iVar = B1000 0000 0000 0000
iVar = iVar - 1; // iVar now contains +32,767 roll-over occurred
... // now iVar = B0111 1111 1111 1111
iVar = 32767; // iVar = B0111 1111 1111 1111
iVar = iVar + 1; // iVar now contains -32,768 roll-over occurred
// now iVar = B1000 0000 0000 0000
0
-1

signed int range:


-16383 0 to 2^15-1 & -2^15 to 0 16383
0 to 32767 & -32767 to 0

32767 (2^15-1)
-32768
(-2^15)
Data Type: short
• Description
̶ A short is a 16-bit data-type, same as an int.
̶ On all Arduino ATmega based boards, a short stores a 16-bit (2-byte) value.
̶ This yields a range of -32,768 to 32,767.
̶ Minimum value of -2^15 and a maximum value of (2^15) - 1.
• Example
short sData = -32768;
• Syntax
̶ short sVar = value;
• Note
̶ sVar - is the short variable name.
̶ value - is the value assigned to short variable name.
Data Type: unsigned int
• Description
̶ An unsigned int stores only a positive 16-bit (2 byte) value.
̶ An unsigned int yields a range of 0 to 65,535 (or 0 to (2^16) - 1).
̶ The Arduino Due stores a 4 byte (32-bit) value, ranging 0 to 4,294,967,295 (2^32 - 1).
̶ The difference between unsigned int and (signed) int, lies in the way the highest bit
called the "sign" bit, is interpreted.
̶ For an int type, if the highest bit is a "1", the number is interpreted as a negative
number and the bits are interpreted by using 2's complement math.
• Example
unsigned int uiData = 65535; // or iVar = B1111 1111 1111 1111
• Syntax
̶ unsigned int uiVar = value;
• Note
̶ uiVar - is the unsigned int variable name.
̶ value - is the value assigned to the unsigned int variable name.
Data Type: unsigned int
• Coding Tip
̶ When variables exceed maximum capacity, "roll over" back to minimum occurs.
̶ This happens in both increment and decrement directions.
• Example
unsigned int uiVar;
...
uiVar = 0;
uiVar = uiVar - 1; // uiVar now contains 65535 - rolls over
uiVar = uiVar + 1; // uiVar now contains 0 - rolls over

0
65535

unsigned int range:


49150 0 to 2^16-1 = 0 to 65535 16383

32767
Data Type: word
• Description
̶ A word stores a 16-bit unsigned number from 0 to 65535, same as an unsigned int.
• Example
word wMyWord = 0xFFFF; // same as 65535 decimal

0
65535

word range:
49150 0 to 2^16-1 = 0 to 65535 16383

32767
Data Type: long
• Description
̶ A long variable is an extended size variable for number storage.
̶ It stores 32 bits (4 bytes), from -2,147,483,648 (-2^31) to 2,147,483,647 ((2^31) - 1).
̶ When doing math with integers or literals, at least one of the integers must be type case
as a long or the literal must be followed by an L, if the result is to be saved into a long
variable.
• Example
// 'L' forces constant into a long data format
long lspeed = 200000000L;
• Syntax
̶ long lVar = value;
• Note
̶ lVar - is the long variable name.
̶ value - is the value assigned to the long variable name.
Data Type: unsigned long
• Description
̶ An unsigned long stores only a positive 32-bit (4 byte) value.
̶ An unsigned long yields a range of 0 to 4,294,967,295 (2^32 - 1).
• Example
// ‘UL' forces constant into an unsigned long data format
unsigned long ulspeed = 4000000000UL;
• Syntax
̶ unsigned long ulVar = value;
• Note
̶ ulVar - is the unsigned long variable name.
̶ value - is the value assigned to unsigned long variable name.
Example: unsigned long
// tip: use “ul” in front of variable name to indicate the data size
unsigned long ulTime;

void setup()
{
Serial.begin(9600);
}

void loop()
{
Serial.print("Time: ");
ulTime = millis();
// prints time since program started, then moves to the next line
Serial.println(ulTime);
// wait a second, limits the amount of transmitted serial data
delay(1000);
}
Data Type: float
• Description
̶ A number that has a decimal point is a floating-point data type number.
̶ Floating-point numbers are often used to approximate analog or continuous real
number values because they have greater resolution than integers.
̶ Floats can be as large as 3.4028235E+38 and as small as -3.4028235E+38.
̶ They are stored as 32 bits (4 bytes) of information.
̶ Floats only have a total number of 6 to 7 digits of precision after the decimal point.
̶ Unlike other platforms where you can get more precision by using a double data type
(e.g. up to 15 digits after decimal point), on the Arduino Uno the double is the same
size as a float.
̶ Floating point numbers are not always exact and may yield strange results.
̶ Floating point math is also much slower than integer math in performing calculations.
̶ Avoid using float math, if a loop has to run at top speed for a critical timing function.
̶ Programmers often go to some lengths to convert floating point calculations into integer
math to increase speed.
̶ When doing math with a float, add a decimal point to the literal constant number
otherwise it will be treated as an integer.
Data Type: float
• Examples
float fMyfloat;
float fSensCalbrate = 1.117;
• Syntax
̶ float fVar = value;
• Note
̶ fvar - is the float variable name.
̶ value - the value assign to the float variable name.
• Example Code
int iX;
int iY;
float fZ;

iX = 1;
iY = iX / 2; // iY now contains 0, can't hold fractions
fZ = (float)iX / 2.0; // fZ now contains 0.5 (must use 2.0 & not 2)
// also type case integer ‘iX’ to a float
Data Type: double
• Description
̶ A double is a double precision floating point number.
̶ However, on Arduino ATmega based boards a double still occupies only 4 bytes.
̶ Therefore, double is exactly the same as the float with no gain in precision.
̶ However, on the Arduino Due a double is a 8 bytes (64-bit) resulting in double precision.
• Tip
̶ Borrowed code from other sources that includes double variables may require further
examination.
̶ See if the implied precision is different from what can actually be achieved on the
Arduino ATmega based boards.
Data Type Summary
̶ When declaring variables, “typedef” can also be used instead of typing full data type.
Data type typedef Size Numeric range
boolean or None 8-bits or false (0) and
bool 1-byte true (1 to 255)
signed char or int8_t 8-bits or -128 to 127 or
char 1-byte -2^7 to 2^7 - 1
unsigned char or uint8_t 8-bits or 0 to 255 or
byte 1-byte 0 to 2^8 - 1
signed int or int16_t 16-bits or -32768 to 32767 or
int or short 2-bytes -2^15 to 2^15 – 1
unsigned int or uint16_t 16-bits or 0 to 65535 or
word 2-bytes 0 to 2^16 – 1
signed long or int32_t 32-bits or -2147483648 to 2147483647 or
long 4-bytes -2^31 to 2^31 – 1
unsigned long uint32_t 32-bits or 0 to 4294967295 or
4-bytes 0 to 2^32 -1
float or none 32-bits or -3.4028235E+38 to 3.4028235E+38
double 4-bytes
Data Type: array
• Description
̶ An array is a collection of variables that are accessed with an index number.
• Creating or Declaring an Array
̶ All of the methods below are valid ways to create or declare an array:
int iData[7];
int iPins[] = {2, 4, 8, 3, 6};
int iSensVals[5] = {2, 4, -8, 3, 2};
char cMsg[6] = "hello"; //5 characters plus a NULL ('\0')
• Note
̶ You can declare an array without initializing it (iData).
̶ You can declare an array without explicitly choosing a size (iPins).
̶ The compiler counts the elements and creates an array of the appropriate size.
̶ Finally you can both initialize and size an array (iSensVals).
̶ Note that when declaring an array of type char to store a string, one more element is
required to hold the null character or '\0' (cMsg).
• Accessing an Array
̶ Arrays are zero indexed, that is the first element of the array is at index 0, therefore:
iSensVals[0] == 2,
iSensVals[1] == 4,
iSensVals[2] == -8, and so on.
Data Type: array
• To assign a value to an array:
iSensVals[0] = 10;
• To retrieve a value from an array:
iValue = iSensVals[4];
• Arrays in loops
̶ Arrays are often manipulated inside for or while loops, where the loop counter is used
as the index for each array element.
• Example
// code to print the elements of an array via the serial port
char cMsg[6] = "hello";
...
for(int i=0; i<5; i++)
{
Serial.write(cMsg[i]); // serially send array elements
delay(250); // send each char every 250 ms
}
...
Data Type: array
̶ Index nine is the last element in an array with ten elements, therefore in:

int iArray[10] = {9, 3, 2, 4, 3, 2, 7, 8, 9, 11};

̶ The iArray[9], contains the data value of 11.


̶ The iArray[10], is invalid and contains random data information from another
memory address.
̶ For this reason, be careful in accessing past the end of an array.
̶ Using an index number greater than the declared array of size – 1, is reading from
memory that is in use for other purposes.
̶ Reading from these locations is probably not going to do much except yield invalid data.
̶ Writing to random memory locations is definitely bad and can often lead to
unpredictable results such as crashes or program malfunction.
̶ This can be a difficult bug or error to track down.
̶ Unlike BASIC or JAVA, the C compiler does no checking to see if array element access is
within legal bounds of the array size, that was declared.
Utilities: sizeof()
• Description
̶ The sizeof operator returns the number of bytes in a variable data type, or the number
of bytes occupied by an array.
• Syntax
̶ sizeof(variable)
• Parameters
̶ variable: any variable data type or array (e.g. char, int, long, float)
• Note
̶ The sizeof operator is useful for determining the size of declared and initialized arrays.
• Example
// use sizeof() to determine byte array size
unsigned char ucPWM = {50, 100, 150, 200, 250};
...
for(int i=0; i<sizeof(ucPWM); i++)
{
analog.write(ucPWM[i]);
delay(1000);
}
...
Example: sizeof()
/* use sizeof() to determine size of any data type declared array */

#define ARR_LEN(x) (sizeof(x)/sizeof(x[0])) // macro to find array size


char cStr[] = "Code tests sizeof() operator"; // 1-byte elements
int iNum[] = {1500, 2500, 3500, 4500, 5500, 6500); // 2-byte elements
long lNum[] = {75000, 85000, 95000, 105000}; // 4-byte elements
void setup()
{
Serial.begin(9600);

for(int i=0; i<ARR_LEN(cStr)-1; i++) // array size of 28, less null


Serial.write(cStr[i]); // prints cStr on one line

for(int i=0; i<ARR_LEN(iNum); i++) // array size of 6 elements


Serial.println(cNum[i]); // prints iNum on each new line

for(int i=0; i<ARR_LEN(lNum); i++) // array size of 4 elements


Serial.println(lNum[i]); // prints lNum on each new line
}
void loop(){
// empty
}
Data Type: char array or String
• Description
̶ Character text strings can be represented in two ways:
> Make a string out of an array of type char and null-terminate it ('\0' or 0x00).
> Or use the String data type.
̶ However, String data type gives more functionality at the cost of using more memory.
• Examples
̶ All of the following are valid declarations of strings:
char cStr1[20];
char cStr2[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o'};
char cStr3[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o', '\0'};
char cStr4[] = "arduino";
char cStr5[8] = "arduino";
char cStr6[15] = "arduino";
• Possibilities for declaring strings
̶ Declare array of chars without initializing it (cStr1).
̶ Declare array of chars with 1 extra char, compiler adds required null character (cStr2)
̶ Explicitly add the null character (cStr3)
̶ Initialize with a string constant in double quotation marks, compiler will size array to fit
the string constant with terminating null character (cStr4)
̶ Initialize the array with an explicit size and string constant (cStr5)
̶ Initialize the array leaving extra space to later store a larger string (cStr6)
Data Type: char array string
• Null termination
̶ Generally strings are terminated with a null character (ASCII code 0x00).
̶ This allows functions like “Serial.print()” to know where the end of a string is.
̶ Otherwise, it would continue reading subsequent bytes of memory that aren't actually
part of the string.
̶ Therefore, a string needs to have space for one more character than the text it contains.
̶ That is why cStr2 and cStr5 needs to be eight characters, even though "arduino" is
only seven characters long, the last position is automatically filled with a null character.
̶ cStr4 will be automatically sized to eight characters, one for the extra null.
̶ cStr3 explicitly includes the null character '\0'.
̶ Note that it's possible to have a string without a final null character (if the specified
length of cStr2 is seven instead of eight).
̶ This will break most functions that use strings, so don't forget the null character.
̶ If something is behaving strangely, the problem could be that the characters operated
on, are not part of the string.
Data Type: char array string
• Single quotes or double quotes?
̶ Strings are always defined inside double quotes ("Abc" ) and characters are always
defined inside single quotes ('A').
• Wrapping long strings
̶ Long strings can be carried over to the next line or wrapped as follows:
char strMessage[] = "This is the first line, "
"this is the second line, "
"et-cetera”;
• Array of strings
̶ It is convenient when working with a large amount of char array text strings (example:
for an LCD display), to setup an array of strings.
̶ Strings themselves are arrays therefore, this is an example of a two-dimensional array.
̶ In the code to follow, the asterisk after the character data type (char*), indicates that
this is an array of pointers.
̶ All array names are actually pointers that holds the address of the first element of the
array, even string constants represent the address of the first element of the string.
̶ Pointers are one of the more difficult parts of C for beginners to understand, but it isn't
necessary to understand pointers in detail to use them effectively here.
Example: Array or strings
// declare array of pointers to literal char array text strings
char* strArray[]={"string 1", "string 2", "string 3","string 4"};

void setup()
{
Serial.begin(9600); // enable serial port at 9600 bits per second
}

void loop()
{
for(int i = 0; i < 4; i++)
{
Serial.println(strArray[i]); // print each string on a new line
delay(500); // every 500 ms
}
while(1); // loop continuously, stops repeating the code above
}
Conversion: char()
• Description
̶ Converts a value to the char data type.
• Syntax
̶ char(val)
• Parameters
̶ val: a value of any data type.
• Returns
̶ A char
Conversion: byte()
• Description
̶ Converts a value to the byte data type.
• Syntax
̶ byte(val)
• Parameters
̶ val: a value of any data type.
• Returns
̶ A byte
Conversion: int()
• Description
̶ Converts a value to the int data type.
• Syntax
̶ int(val)
• Parameters
̶ val: a value of any data type.
• Returns
̶ An int
Conversion: word()
• Description
̶ Convert a value to the word data type or creates a word from two bytes.
• Syntax
̶ word(val)
̶ word(high, low)
• Parameters
̶ val: a value of any data type.
̶ high: the high-order (leftmost) byte of the word.
̶ low: the low-order (rightmost) byte of the word.
• Returns
̶ A word
Conversion: long()
• Description
̶ Converts a value to the long data type.
• Syntax
̶ long(val)
• Parameters
̶ val: a value of any data type.
• Returns
̶ A long
Conversion: float()
• Description
̶ Converts a value to the float data type.
• Syntax
̶ float(val)
• Parameters
̶ val: a value of any data type.
• Returns
̶ A float
Variable Scope
• Description
̶ Variables in the C programming language have a property called scope.
̶ In contrast to early versions of languages such as BASIC where every variable is a global.
̶ A global variable is one that can be seen by every function in a C program.
̶ Local variables are only visible to the function in which they are declared.
̶ In the Arduino C programing environment any variable declared outside of a function
such as setup(), loop(), etc…, is a global variable.
̶ When programs start to get larger and more complex, local variables are a useful way
to insure that only one function has access to its own variables.
̶ This prevents one function inadvertently modifies variables used by another function
thereby avoiding programming errors.
̶ It is also sometimes handy to declare and initialize a variable inside a for loop.
̶ This creates a variable that can only be accessed from inside the for loop brackets.
Example: Variable Scope
int giPWMval; // any function will see this global variable

void setup()
{
giPWMval = 20; // can use “giPWMval” in setup()function
}

void loop()
{
int iData; // "iData" is only "visible" inside of loop()function
float fData; // "fData" is only "visible" inside of loop()function

for(int j = 0; j <10; j++)


{
// variable j & k can only be accessed inside for-loop brackets
int k;
k = giPWMval * j; //can use “giPWMval” in loop() and for-loop
analogWrite(k);
delay(500);
}
}
Qualifier: static
• Description
̶ The static keyword is also used to create variables that are visible to only one function.
̶ However, local variables get created every time a function is called and destroyed every
time a function is exited.
̶ Variables declared as static will only be created and initialized the first time a function
is called.
̶ A static variable persist beyond the function call, preserving their data value between
function calls.
Example: static
// using "static" to preserve a variable value between function calls
int UpdateCnt();
int iNum = 0;

void setup(){
Serial.begin(9600);
}
void loop() {
if(iNum <= 60)
{
iNum = UpdateCnt();
Serial.print(":");
Serial.print(iNum); // only prints ":10:20:30:40:50:60"
}
}
// iCnt declared as static, value is preserved between function calls.
// Without static keyword, value of iCnt would be reset to 0 each time,
// and when UpdateCnt() is called, return value will always be 10.
int UpdateCnt()
{
static int iCnt = 0;
iCnt += 10;
return count;
}
Qualifier: volatile
• Description
̶ The volatile keyword is known as a variable qualifier.
̶ It is used before the data type of a variable, in order to modify the way in which the
compiler and subsequent program treats the variable.
̶ Declaring a variable volatile is a instruction or directive to the compiler.
̶ A compiler is software that translates C/C++ code into machine code which is the real
executable machine instructions of the Atmega microcontroller on the Arduino board.
̶ The volatile keyword directs the compiler to load the variable from RAM and not from
a storage stack register, which is a temporary memory location where program and
function variables are stored and manipulated.
̶ Under certain conditions the value for a variable stored in registers can be inaccurate.
̶ A variable should be declared volatile whenever its value can be changed by something
beyond the control of normal code execution in which it appears, such as a
concurrently executing thread or an interrupt routine.
̶ In the Arduino the only place that this is likely to occur is in sections of code associated
with interrupts, called an interrupt service routine (ISR).
Example: volatile
// toggles LED when interrupt pin changes state
const int pinLED = 10;
volatile bool vbState = LOW;

void setup()
{
pinMode(pinLED, OUTPUT);
attachInterrupt(0, blink, CHANGE); // attach to interrupt 0 pin
} // this is pin 2 of Arduino UNO

void loop()
{
digitalWrite(pinLED, vbState); // update LED with current state
delay(20);
}

void blink()
{
vbState = !vbState; // change state when interrupt 0 state changes
}
Qualifier: const
• Description
̶ The const keyword stands for constant and is a variable qualifier that modifies the
behavior of the variable by making it "read-only".
̶ This means that a variable can be used just as any other variable of its data type but its
value cannot be changed.
̶ A compiler error occurs if a value is assigned to a const declared variable data type.
̶ The benefit of using const variables is that they obey the rules of variable scoping that
govern other variables.
̶ The pitfalls of using #define, makes the const keyword a superior method for defining
constants which is generally preferred over using #define.
• Example
const float pi = 3.14;
float fx;
float fy = pi; //not allowed when initializing declared variables
...
fx = pi * 2; //it's fine to use const's in math
pi = 7; //illegal, can't write to or modify a constant
• #define or const?
̶ Either const or #define can be used for creating numeric or string constants.
̶ However, for arrays you will need to use const.
Utilities: PROGMEM
• Description
̶ Stores data in flash (program) memory instead of SRAM.
̶ The PROGMEM keyword is a variable modifier used only with the data types defined in
“avr/pgmspace.h”.
̶ It tells the compiler “to put this information into flash memory" instead of into SRAM,
where it would normally be stored.
̶ PROGMEM is part of the “avr/pgmspace.h” library.
̶ So to use PROGMEM, include the following library at the top of the sketch, as follows:
#include <avr/pgmspace.h>

̶ While PROGMEM can be used on a single variable, it is really only worth the fuss if a
larger block of data needs to be stored, which is usually in an array.
̶ Using PROGMEM is a two-step procedure because after saving data into Flash memory,
it requires special methods (functions) defined in the “avr/pgmspace.h” library, to read
the data from program memory back into SRAM, so that it can be used.
• Syntax
̶ const dataType variableName[] PROGMEM = {data0, data1, data3...};
̶ dataType - any valid variable data type
̶ variableName - the name to identify the array of data
Utilities: PROGMEM
̶ Note that because PROGMEM is a variable modifier there is no hard and fast rule about
where it should go.
̶ However, experiments have indicated that in various versions of Arduino (having to do
with GCC version), PROGMEM may work in one location and not in another.
̶ Earlier versions of IDE work better if PROGMEM is included after the variable name.
• Example
const dataType variableName[] PROGMEM = {}; //use this form
const PROGMEM dataType variableName[] = {}; //or this form
const dataType PROGMEM variableName[] = {}; //not this one
• Note
̶ Please note that variables must be either globally defined, or defined with the static
keyword in order to work with PROGMEM variable modifier.

̶ The following code will NOT work when it is inside a function:


const char Str[] PROGMEM = "Hi, let us talk.\n";

̶ The following code WILL work, even if locally defined within a function:
const static char Str[] PROGMEM = "Hi, let us talk.\n";
Utilities: PROGMEM
// Code to read and write bytes and integers from flash (program) memory.
#include <avr/pgmspace.h>
const unsigned int Nums[] PROGMEM = {65000, 32796, 512}; // save ints to flash
const char Msg[] PROGMEM = "I am a hard working student"; // save chars to flash
unsigned int uiDisplay;
char cByte;

void setup(){
Serial.begin(9600);
}

void loop(){
for(int i=0; i<3; i++)
{
uiDisplay = pgm_read_word_near(Nums + i); // read unsigned int from PROGMEM
Serial.println(uiDisplay); // print each number on new line
}

int iLen = strlen_P(Msg); // get number of chars in PROGMEM string


for(int i=0; i<(iLen-1); i++)
{
cByte = pgm_read_byte_near(Msg + i); // read a char from PROGMEM
Serial.write(cByte); // serially send all chars, except null
}
delay(1000); // delay for 1s and then repeat
}
Utilities: PROGMEM
// Code to store a table of strings in program memory flash and to retrieve them.
#include <avr/pgmspace.h>
const char Str0[] PROGMEM = "Hello, I am Dr. van Niekerk";
const char Str1[] PROGMEM = "What is your name?";
const char Str2[] PROGMEM = "I am a hard worker";
const char Str3[] PROGMEM = "Are you a hard worker?";

// Then set up a table to refer to your strings.


char* const StrTable[] PROGMEM = {Str0, Str1, Str2, Str3};
char cStrBuf[30]; // must be large enough for largest string that it must hold

void setup()
{
Serial.begin(9600);
// Using string table in program memory requires the use of "strcpy_P" function
// to copy a string from program space to a string in RAM (“cStrBuf").
for(int i=0; i<4; i++)
{
//necessary casts and dereferencing, just use as is
strcpy_P( cStrBuf, (char*)pgm_read_byte_near( &(StrTable[i]) ) );
Serial.println(StrBuf); // print each string on a new line
delay(1000);
}
}

void loop(){}
EXERCISE
1. Explain the meaning of a “HIGH” in reference to a port pin when it is configured as an
“INPUT” or an “OUTPUT” on the Arduino board?
2. Explain the meaning of a “LOW” in reference to a port pin when it is configured as an
“INPUT” or an “OUTPUT” on the Arduino board?
3. Describe how the internal 20 KΩ pull-up resistor on the Atmega port pins, can be enabled
and explain what will it do to an input port pin?
4. Elaborate on what makes an Arduino port pin configured as an INPUT, useful in reading an
input sensor?
5. Discuss why an input port pin should use an external 10 KΩ pull-up or pull-down resistor
when reading the state of a connected open switch?
6. Explain the difference between using an external pull-up or pull-down resistor?
7. What is the state of Arduino configured output port pins and what can they provide to
other external circuits?
8. Explain what the Arduino constant “LED_BUILTIN” represents.
9. Generate a table showing the base, special notation or formatters, example and character
range for a decimal, a binary and a hexadecimal integer constant.
10. Where is the “void” keyword used and what does it indicate?
11. Generate a table showing the bit size and numeric value range of both a signed and
unsigned “char”, “int” and “long” data type.
EXERCISE
12. Generate a table showing the bit size and numeric value range of a Boolean, byte, word
and short data type.
13. Design an Arduino C program without comments to serially print continuously every half
second on each line the following strings: “First line”, “Second line” and “Third line”. Use
serial function at 9600 bit/s, an array of strings and a for-loop to iterate through the array.
14. Describe and show the syntax with associated function parameters and return value of
the “word()” conversion function.
15. Explain what the difference is between a global and local declared variable in C?
16. Discuss why local variables are useful and how they can prevent programing errors?
17. What does the “static” keyword create and how it is different to a function local variable?
18. Describe the “volatile” keyword, what it does and when it should be use?
19. Describe the “const” keyword, what it does and the benefit of using it?
20. Create an Arduino C program to serially print out on each line, the array index equal to the
associated character of an unknown string by using the “sizeof()” operator at 19200 bit/s.
21. Develop an Arduino C program that initially saves the follow string data to the flash
program memory, “I love to program an Arduino UNO” and then serially prints out the
string data once at 9600 bits/s.
22. Design an Arduino C program that initially saves the follow data strings to the flash
program memory, “Hello, I love to program”, “Do you love to program?”, “I use a table to
refer to strings”. Then serially print out every half second on each line the data strings
once at 9600 bits/s.

You might also like