If you’ve ever made an Arduino-based project involving a large number of text strings, you’ll quickly find the standard-issue Arduino’s 2K of SRAM very limiting. One solution is to save the text in program memory instead, which at 32Kb is much more plentiful.
In the Spring of 2011, I worked on a project with artists Steve Hanson and Elliot Clapp that was part of Apexart’s “Let it end like this” group show, curated by Todd Zuniga. Steve created a generative questionnaire that polled gallery-goers and presented them with their Last Words. An interactive push-button console and LCD was built by Elliot (pictured above) and we built the thing using an inexpensive Arduino variant from Modern Device. The complete project is documented in the Make: Projects wiki.
Steve’s poetic Last Words took up almost 16K of memory, which was stored in the program memory of the Arduino. Below is an excerpt from the Arduino Cookbook by Michael Margolis that explains how to save and access a large table of data in program memory.
17.3 Storing and Retrieving Numeric Values in Program Memory
Problem
You have a lot of constant numeric data and don’t want to allocate this to RAM.
Solution
Store numeric variables in program memory (the flash memory used to store Arduino programs).
This sketch adjusts a fading LED for the nonlinear sensitivity of human vision. It stores the values to use in a table of 256 values in program memory rather than RAM.
The sketch is based on Recipe 7.2; see Chapter 7 for a wiring diagram and discussion on driving LEDs. Running this sketch results in a smooth change in brightness with the LED on pin 5 compared to the LED on pin 3:
Discussion
When you need to use a complex expression to calculate a range of values that regularly repeat, it is often better to precalculate the values and include them in a table of values (usually as an array) in the code. This saves the time needed to calculate the values repeatedly when the code runs. The disadvantage concerns the memory needed to place these values in RAM. RAM is limited on Arduino and the much larger program memory space can be used to store constant values. This is particularly helpful for sketches that have large arrays of numbers.
At the top of the sketch, the table is defined with the following expression:
const byte table[]PROGMEM = { 0, . . .
PROGMEM tells the compiler that the values are to be stored in program memory rather than RAM. The remainder of the expression is similar to defining a conventional array (see Chapter 2).
The low-level definitions needed to use PROGMEM are contained in a file named pgmspace.h and the sketch includes this as follows:
#include <avr/pgmspace.h>
To adjust the brightness to make the fade look uniform, this recipe adds the following lines to the LED output code used in Recipe 7.2:
int adjustedBrightness = pgm_read_byte(&table[brightness]); analogWrite(adjustedLedPin, adjustedBrightness);
The variable adjustedBrightness is set from a value read from program memory. The expression pgm_read_byte(&table[brightness]); means to return the address of the entry in the table array at the index position given by brightness. Each entry in the table is one byte, so another way to write this expression is:
pgm_read_byte(table + brightness);
If it is not clear why &table[brightness] is equivalent to table + brightness, don’t worry; use whichever expression makes more sense to you.
Another example is from Recipe 6.5, which used a table for converting an infrared sensor reading into distance. Here is the sketch from that recipe converted to use a table in program memory instead of RAM:
/* ir-distance_Progmem sketch * prints distance & changes LED flash rate * depending on distance from IR sensor * uses progmem for table */ #include <avr/pgmspace.h> // needed when using Progmem // table entries are distances in steps of 250 millivolts const int TABLE_ENTRIES = 12; const int firstElement = 250; // first entry is 250 mV const int interval = 250; // millivolts between each element // the following is the definition of the table in Program Memory const int distanceP[TABLE_ENTRIES] PROGMEM = { 150,140,130,100,60,50, 40,35,30,25,20,15 }; // This function reads from Program Memory at the given index int getTableEntry(int index) { int value = pgm_read_word(&distanceP[index]); return value; }
The remaining code is similar to Recipe 6.5, except that the getTableEntry function is used to get the value from program memory instead of accessing a table in RAM. Here is the revised getDistance function from that recipe:
int getDistance(int mV) { if( mV > interval * TABLE_ENTRIES ) return getTableEntry(TABLE_ENTRIES-1); // the minimum distance else { int index = mV / interval; float frac = (mV % 250) / (float)interval; return getTableEntry(index) - ((getTableEntry(index) - getTableEntry(index+1)) * frac); } }
In the Maker Shed:
Arduino Cookbook
Create your own robots, toys, remote controllers, alarms, detectors, and more with Arduino and this guide. Arduino lets artists and designers build a variety of amazing objects and prototypes that interact with the physical world. With this Cookbook, you can dive right in and experiment, thanks to more than a hundred tips and techniques, no matter what your skill level. Here you’ll find the examples and advice you need to begin, expand, and enhance your projects right away.
ADVERTISEMENT