If you have any questions or feedback, pleasefill out this form
This post is translated by ChatGPT and originally written in Mandarin, so there may be some inaccuracies or mistakes.
This article is the third in a series:
- Introduction to Sensors - DHT11 and MH-Z14A
- Data Communication - UART
- Arduino Pitfalls
- (Upcoming) WiFi Section: To save time on debugging, I purchased an ESP32 development board, which already includes both WiFi and Bluetooth functionality.
- (Upcoming) MQTT Section: To transmit data to other devices, I employed the lightweight MQTT communication protocol.
- (Upcoming) Grafana / Web Section: Since the data is stored in a database, it must be displayed in a visually appealing way! Here, I used Grafana + Prometheus and Svelte to present the data.
In this article, I will discuss some common pitfalls encountered due to unfamiliarity with Arduino and the language.
Different Data Reading Orders in CPU (Big Endian vs. Little Endian)
Due to differences in CPU instruction set architectures, the order in which data is read can vary, a concept known in Chinese as 位元組順序 (Endianness). If reading starts from the most significant byte, it is called Big Endian; if it starts from the least significant byte, it is known as Little Endian. The following illustration clarifies this further:
Arduino uses Little Endian as its reading order, which can be found in this forum post. Aside from the memory mentioned above, any difference in data reading order can be described using Big Endian and Little Endian.
Memory management requires the OS to divide memory spaces into fixed sizes, so when there isn't enough room to store contiguous data, the data will be placed in the next available space. When retrieving values, these must be combined.
This issue arose because I cleverly set ppm
as an unsigned short
pointer; thus, it should automatically calculate the correct number without manual bit shifting. 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 short is 2 bytes in size, pointing the pointer to result->high
would read 2 bytes backward, which should be equivalent to result->high << 8 + result->low
.
After testing, I found the result to be a very strange number, likely due to the differing byte reading order. By using bit shifting, I was able to obtain the correct number:
result->ppm = (result->high << 8) + result->low;
C Data Structures Vary by Platform
For example, an int
could be 2 bytes or 4 bytes, and a typical long
could be either 8 bytes or 4 bytes. In languages like Java, data types do not have different sizes across platforms because the data type definitions come from the language implementation itself. Therefore, in C/C++, types like uint8_t
exist to allow platforms to define different sizes for data types based on need.
While this is common knowledge for those accustomed to writing in C, when calculating the size of data structures, one should use sizeof
rather than assuming that the size of data types is fixed across all platforms.
Effectively Using the byte
Data Type
Arduino provides a data type called byte
. Its definition is the same as unsigned char
, yet many find the differences between uint8_t
, char
, and int
confusing. Let's clarify:
byte
is equivalent tounsigned char
, both allocating 8 bits of memory for a variable. The only difference is thatunsigned char
feels broader to the user, whilebyte
explicitly indicates that the number is between 0 and 255 (unsigned). Arduino recommends using byte for consistency.uint8_t
,byte
, andunsigned char
are essentially similar concepts, akin to aliases. In C/C++, different platforms may defineint
as either 8-bit or 16-bit, souint8_t
is typically defined for more precise meaning.
Utilizing Header Files to Organize Functions
When working on Arduino projects, the code structure is generally simpler, and cramming all functionalities into loop
and setup
does not make the code overly complicated. However, as circuits become more complex, having everything in a single file can get messy.
Although Arduino IDE files have the extension .ino
, they are fundamentally C++ files, supporting C++ syntax but not the standard library. To write modules and use Arduino functions, simply include Arduino.h
.
#include "Arduino.h"
#include <string.h>
If coding with VSCode
, you may notice that Arduino's built-in libraries cannot be included directly. While compilation may succeed, red lines will appear in the editor. To resolve this, you can add .vscode/c_cpp_properties.json
to help VSCode locate the correct headers. Here’s an example (using macOS; Linux or Windows may require finding the Arduino installation path):
{
"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
}
Additional Notes
noInterrupts()
is equivalent tocli()
. This is defined inArduino.h
as follows:
#define interrupts() sei()
#define noInterrupts() cli()
- Using
std::string
directly in Arduino can lead to issues; it's advisable to avoid usingstd
in Arduino as all those wonderful C++ features are not available. You might refer to this forum post where community members have attempted to get the std library working on Arduino. It's unclear how that project is progressing.
From this experimentation, I've discovered that learning various lower-level concepts through Arduino is a great method. With a built-in Serial Port for debugging and straightforward USB connection for IDE installations, uploading code becomes hassle-free. Additionally, the underlying implementations can be found on GitHub. I also feel the need to brush up on my C/C++ knowledge, as my only exposure was during university courses.
Finally, due to my unfamiliarity with Arduino, I have strived to gather information to improve this article. However, there may still be gaps or errors, so I welcome any constructive feedback from readers.
If you found this article helpful, please consider buying me a coffee ☕ It'll make my ordinary day shine ✨
☕Buy me a coffee