This article is the third in a series:
- Sensor Introduction - DHT11 and MH-Z14A
- Data Communication - UART
- Arduino Pitfalls
- (Unreleased) WiFi Part: To save debugging time, I purchased an ESP32 development board, which already has WiFi and Bluetooth capabilities.
- (Unreleased) MQTT Part: To send data to other devices, I used the lightweight and compact MQTT communication protocol.
- (Unreleased) Grafana / Web Part: When data is stored in a database, it should be displayed in a fancy way! Here, Grafana + Prometheus and Svelte are used to display the data.
This article will discuss some pitfalls encountered in Arduino and programming languages.
Different CPU Data Reading Orders (Big Endian vs Little Endian)
Due to different CPU instruction set architectures, the order of reading data may also differ. This is known as byte order or endianness. Reading data starting from the most significant byte is called Big-Endian, while reading data starting from the least significant byte is called Little Endian. The following diagram explains it clearly:
Arduino uses Little Endian as the data reading order, and you can find the answer here. Apart from memory, the terms Big Endian and Little Endian can be used to describe any difference in data reading order.
For the sake of memory management, the operating system divides memory into fixed sizes. So, if there is not enough space to store continuous data, it will be stored in the next available space and added up when accessing the values.
The reason for encountering this issue is that I cleverly assigned ppm
as an unsigned short
pointer, which should automatically calculate the correct value without manually writing bit shifts. An example is shown below:
struct Co2Result {
byte startByte;
byte command;
byte high;
byte low;
unsigned short ppm;
};
Co2Result *result = malloc(sizeof(Co2Result));
result->startByte = response[0];
result->command = response[1];
result->high = response[2];
result->low = response[3];
result->ppm = &result->high;
Since the size of a short is 2 bytes, pointing the pointer to result->high
would read 2 bytes from that position. The effect should be equivalent to result->high<<8 + result->low
.
After testing, it was found that the result was a very strange number, which I believe was due to the difference in bit reading order. After using bit shift, I was able to obtain the correct value:
result->ppm = (result->high << 8) + result->low;
C Data Structures Vary by Platform
For example, the size of int
can be either 2 bytes or 4 bytes, and a regular long
can be either 8 bytes or 4 bytes. In languages like Java, data types do not have different results on different platforms because their definitions come from the language implementation itself. In C/C++, you may encounter types like uint8_t
that allow platforms to define data types of different sizes as needed.
Although it may be familiar to those who regularly write C, when calculating the size of data structures, sizeof
should be used instead of assuming that data type sizes are fixed on every platform.
Make Good Use of the byte
Data Type
Arduino provides a data type called byte
, which is defined the same as unsigned char
. However, there is often confusion between uint8_t
, char
, and int
. Let's clarify here:
byte
andunsigned char
are the same, both assigning 8 bits of memory to the variable. The only difference is thatunsigned char
has a broader meaning to the user, whilebyte
clearly expresses that the number is between 0 and 255 (unsigned). Arduino recommends using byte for consistency.uint8_t
,byte
, andunsigned char
are conceptually similar, like aliases. In C/C++,int
may be defined as 8 bits or 16 bits on different platforms, souint8_t
is usually defined to express a more precise meaning.
Split Functions Using Header Files
When working on Arduino projects, the code structure is relatively small, and putting all the functionality in the loop
and setup
functions does not make the code too difficult to read. However, once the circuit becomes more complex, putting everything in a single file becomes messy.
Although the file extension in the Arduino IDE is .ino
, it is essentially C++
and supports C++ syntax, except for using standard libraries. To write modules and use Arduino functions, simply include Arduino.h
.
#include "Arduino.h"
#include <string.h>
If you are using VS Code, you may notice that the built-in Arduino library cannot be included. Although the code will compile without errors, red lines will appear in the editor. In this case, you can add .vscode/c_cpp_properties.json
to let VS Code find the correct header file. Here's an example: (using macOS version, for Linux or Windows, you may need to find the Arduino installation location)
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"/Applications/Arduino.app/Contents/Java/hardware/**",
"/Applications/Arduino.app/Contents/Java/hardware/arduino/**"
],
"defines": [],
"macFrameworkPath": [
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}
Other
noInterrupet()
is the same ascli()
. InArduino.h
, they are defined as follows:
#define interrupts() sei()
#define noInterrupts() cli()
- Using
std::string
directly in Arduino may cause issues. It is better to avoid usingstd
on Arduino, as all those wonderful C++ features are not available. You can refer to the discussion on the forum, where someone seems to be trying to make the std library work on Arduino. I'm not sure about the progress.
Through this experiment, I found that Arduino is a good way to learn various low-level knowledge. It provides a built-in Serial Port for debugging, and with a USB connection and IDE installation, uploading code becomes easy. The implementation details can also be found on GitHub. It also makes me want to brush up on my knowledge of C/C++ (I only had a course on it in university).
Furthermore, in this article, due to my unfamiliarity with Arduino, although I have tried my best to gather information and improve the explanations, there may still be incomplete or incorrect parts. I welcome any suggestions or corrections.