In this project, we'll use an Arduino development board and an 8x8 LED matrix display to display temperature data collected by the LM35 temperature sensor in a clear and easy-to-understand way. Picture the temperature value scrolling on the display, updating as the ambient temperature changes, like a small screen showing real-time environmental information. This isn't just a technical task, but also a fun way to bring technology into daily life and give objects new functionality through code. We don't need complex external devices; just basic circuit connections and a few lines of code will allow us to create a practical and creative temperature display system. So, let's start this journey of combining the digital world with reality together!
COMPONENT LIST
HARDWARE
fig 1 Thermometer Circuit
Follow the hardware connections as shown in the wiring diagram, paying attention to the connection orientation of the temperature sensor and the LED matrix display.
CODE
Sample code:
//project-Thermometer
#define TEMP_SENSOR_PIN A4 // LM35 temperature sensor connected to A4 pin
const int rowPins[8] = {6, 11, 5, 9, A0, 4, A1, 2}; // LED matrix row pins (cathode)
const int colPins[8] = {10, A3, A2, 7, 3, 8, 12, 13};
// LED matrix column pins (anode)
// Define 8x8 dot matrix patterns for numbers 0-9
byte numbers[10][8] = {
// 0
{0b00111100,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b00111100},
// 1
{0b00011000, //1
0b00111000, //2
0b00011000, //3
0b00011000, //4
0b00011000, //5
0b00011000, //6
0b00011000, //7
0b11111110},//8
// 2
{0b00111100,
0b01100110,
0b00000110,
0b00001100,
0b00110000,
0b01100000,
0b01100110,
0b01111110},
// 3
{0b00111100,
0b01100110,
0b00000110,
0b00011100,
0b00000110,
0b00000110,
0b01100110,
0b00111100},
// 4
{0b00001100,
0b00011100,
0b00101100,
0b01001100,
0b01111110,
0b00001100,
0b00001100,
0b00001100},
// 5
{0b01111110,
0b01100000,
0b01100000,
0b01111100,
0b00000110,
0b00000110,
0b01100110,
0b00111100},
// 6
{0b00111100,
0b01100110,
0b01100000,
0b01111100,
0b01100110,
0b01100110,
0b01100110,
0b00111100},
// 7
{0b01111110,
0b01100110,
0b00001100,
0b00011000,
0b00110000,
0b00110000,
0b00110000,
0b00110000},
// 8
{0b00111100,
0b01100110,
0b01100110,
0b00111100,
0b01100110,
0b01100110,
0b01100110,
0b00111100},
// 9
{0b00111100,
0b01100110,
0b01100110,
0b00111110,
0b00000110,
0b00000110,
0b01100110,
0b00111100}
};
// Read the temperature sensor value and convert it to Celsius
float readTemperature() {
int reading = analogRead(TEMP_SENSOR_PIN);
float voltage = reading * 5.0 / 1023.0;
float temperature = voltage * 100.0;
// Convert voltage to temperature
return temperature;
}
void combineNumbers(byte first[], byte second[], byte combined[], int width) {
for (int i = 0; i < width; i++) {
combined[i] = first[i];
}
for (int i = 0; i < width; i++) {
combined[i + width] = second[i];
}
}
void displayScrollingNumber(byte number[], int combinedWidth) {
int width = 8; // Width of a single digit
/* The total scrolling steps are the combined width of the digits plus the width of the LED matrix,
allowing the two digits to scroll from fully visible on the right to fully invisible on the left.*/
for (int offset = width; offset >= -combinedWidth; offset--) {
for (int row = 0; row < width; row++) {
digitalWrite(rowPins[row], LOW);
// Activate the current row
for (int col = 0; col < width; col++) {
int colIndex = col - offset;
// Note the subtraction of offset here
if (colIndex >= 0 && colIndex < combinedWidth) {
/* Set the column value for the digit, note the change to read bits from left to right*/
digitalWrite(colPins[col], bitRead(number[row + ((colIndex / width) * width)], width - 1 - (colIndex % width)) ? HIGH : LOW);
} else {
/* If the column index is out of range, turn off the corresponding column*/
digitalWrite(colPins[col], LOW);
}
}
// Control scrolling speed
delay(8);
digitalWrite(rowPins[row], HIGH);
// Deactivate the current row, prepare to activate the next one
}
}
}
void setup() {
Serial.begin(9600);
for (int i = 0; i < 8; i++) {
pinMode(colPins[i], OUTPUT);
pinMode(rowPins[i], OUTPUT);
digitalWrite(rowPins[i], HIGH);
// Initialize row pins to HIGH (off)
}
}
void loop() {
byte combinedNumber[16];
// Assuming we only combine two digits, so the size is 16
int temp = readTemperature();
int tempDigits[3];
// Assuming the temperature will not exceed 999 degrees
Serial.println(temp);
tempDigits[0] = (temp / 10) % 10;
tempDigits[1] = temp % 10;
combineNumbers(numbers[tempDigits[0]], numbers[tempDigits[1]], combinedNumber, 8);
displayScrollingNumber(combinedNumber, 16);
// Pass the combined array and width
CODE REVIEW
The first three lines of code define the pins connected to the rows and columns of the temperature sensor and dot matrix screen.
#define TEMP_SENSOR_PIN A4
const int rowPins[8] = {6, 11, 5, 9, A0, 4, A1, 2};
const int colPins[8] = {10,A3, A2, 7, 3, 8, 12, 13};
In the two-dimensional array numbers[10][8], numbers is an array containing 10 elements, where each element is a one-dimensional array (also called a digit matrix) containing 8 elements. The digit matrix defines how each digit is displayed on a dot matrix screen, consisting of 8 elements corresponding to each row of an 8x8 dot matrix screen. Each element is an 8-bit binary array (where "0b" is a prefix used to explicitly indicate that the following number is in binary form, not counting towards the bit length), corresponding to each column in that row.
byte numbers[10][8] = {
// 0
{0b00111100,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b00111100},
......
// 9
{0b00111100,
0b01100110,
0b01100110,
0b00111110,
0b00000110,
0b00000110,
0b01100110,
0b00111100}
};
If you want to display the digit 0 on the dot matrix screen, you can access numbers[0] to obtain the digit matrix for this number (as shown below).
{0b00111100,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b00111100}
To display the digit 0 on the dot matrix screen, follow these steps:
1. Iterate through each row of the dot matrix screen.
2. For each row, activate all the columns of that row.
3. Iterate through each column, and based on the value at the bit index of each element in the one-dimensional array for the digit 0, set the LED of that column to be either on or off. (For example, in the 0th element of the array, the values at bit indices 2-5 are 1, and the others are 0, resulting in the LEDs in columns 3-6 of that row being lit up.)
4. Deactivate the current row and prepare to display the next row.
5. Repeat the above steps until all rows have been displayed once.
By continuously repeating this traversal process and leveraging the computer's operating speed and human persistence of vision, the digit 0 can be displayed on the dot matrix screen.
Now you have a basic understanding of how a dot matrix screen displays a single digit, but in this project, we need to display real-time temperature readings, which are often two-digit numbers representing the ambient temperature in Celsius. So, how can we represent two-digit numbers on such a small dot matrix screen?
Typically, a scrolling method is used to display multiple digits. Next, let's see how to combine multiple digits and make them scroll!
The function readTemperature() is used to read the current ambient temperature. The relevant calculation process has already been explained in the project [Temperature Alarm], so I won't repeat it here.
In the setup() function, a for loop is used to set the row and column pins to output mode, and all row pins are set to high level, which means all LED lights are off. (For more information on dot matrix screens, please refer to the project [Light Up Matrix].)
void setup() {
Serial.begin(9600);
for (int i = 0; i < 8; i++) {
pinMode(colPins[i], OUTPUT);
pinMode(rowPins[i], OUTPUT);
digitalWrite(rowPins[i], HIGH);
//Initialize row pins to HIGH (off)
}
}
In the loop function, display the temperature value in the Serial Monitor.
int temp = readTemperature();
Serial.println(temp);
Since the digit matrix array only stores the matrices for digits 0-9, when displaying the temperature, it needs to be split into the tens and units place for easy display on the dot matrix screen using the corresponding digit matrices. Therefore, an array is defined to store the digits for the tens and units place:
int tempDigits[2];
// Assuming the temperature will not exceed 99 Celsius degrees
tempDigits[0] = (temp / 10) % 10;
tempDigits[1] = temp % 10;
Next, we use the combineNumbers() function to merge the digit matrices of two numbers into a long digit matrix, and then scroll this long digit matrix on the dot matrix screen.
byte combinedNumber[16];
// Assuming we only combine two digits, so the size is 16
combineNumbers(numbers[tempDigits[0]], numbers[tempDigits[1]], combinedNumber, 8);
displayScrollingNumber(combinedNumber, 16);
The above is the entire content of the loop function. Next, we will focus on providing detailed explanations for the two functions: combineNumbers() and displayScrollingNumber().
void combineNumbers(byte first[], byte second[], byte combined[], int width) {
for (int i = 0; i < width; i++) {
combined[i] = first[i];
}
for (int i = 0; i < width; i++) {
combined[i + width] = second[i];
}
}
This code defines a function named combineNumbers() whose purpose is to horizontally combine the digit matrices (represented as byte arrays) of two numbers into a new byte array. This function is very useful for displaying two numbers side by side on a dot matrix display. The implementation is quite simple, using two parallel for loops to copy the elements from the two digit arrays into the combined[] array sequentially. For example, after combining the digit matrices for the numbers 0 and 1, the combined[] array would look like this:
{0b00111100,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b01100110,
0b00111100,
0b00011000,
0b00111000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b01111110}
Finally, we scroll the combined number display. The logic for scrolling display is as follows:
Outer Loop (Scrolling of the number):
for (int offset = width; offset >= -combinedWidth; offset--)
This loop controls the scrolling of the number. The offset variable starts from the width of the number (i.e., the number is completely outside the screen on the right) and decrements to combinedWidth (i.e., the number is completely outside the screen on the left). This way, the number scrolls from right to left.
Inner Loop (Row Scanning):
for (int row = 0; row < width; row++)
This loop iterates through each row of the dot matrix display. For each row, the code first activates the row (sets it to a low level) and then sets each column for that row.
Inner Loop (Column Setting):
int colIndex = col - offset;
Calculate the index of the current column relative to the starting position of the number. Since the number is scrolling, use the offset to adjust the column index.
if (colIndex >= 0 && colIndex < combinedWidth)
Check if the column index is within the valid range of the number.
If it is within the valid range, use:
bitRead(number[row + ((colIndex / width) * width)], width - 1 - (colIndex % width))
To read the bit value (0 or 1) at that position and set the column level (high or low) accordingly, thereby lighting up or turning off the LED.
If not, if the index is out of range, use digitalWrite(colPins[col], LOW) to turn off the corresponding column.
Finally, delay(8) and digitalWrite(rowPins[row], HIGH) are used to control the scrolling speed and deactivate the current row in preparation for activating the next one.
This concludes the code review for this project. If you want to use the dot matrix display to show other characters, you can try completing the exercises below.
EXERCISE
Apart from displaying scrolling numbers, the dot matrix screen can also display other types of characters. English letter seems to be a good choice. Have you tried making the dot matrix screen scroll and display words? Come and give it a try!