Real-Time Data Visualization with JavaScript and HTML5 Canvas: Interactive Dashboard Development
Extending our JavaScript Canvas applications and paint app development, this guide explores advanced techniques for …
Building upon the foundational concepts from our ATmega8 RC constant measurement and LCD interfacing tutorials, this comprehensive guide explores advanced Arduino programming techniques that transform hobbyist sketches into professional-grade firmware.
Arduino microcontrollers have limited memory, making efficient memory management crucial for professional applications.
1#include <avr/pgmspace.h>
2
3// Store large constant data in flash memory
4const char errorMessages[] PROGMEM = {
5 "System OK\0"
6 "Sensor Error\0"
7 "Communication Failed\0"
8 "Memory Full\0"
9 "Invalid Parameter\0"
10};
11
12const uint16_t sensorCalibration[] PROGMEM = {
13 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192
14};
15
16// Function to read from PROGMEM
17void printErrorMessage(uint8_t errorCode) {
18 char buffer[32];
19 strcpy_P(buffer, errorMessages + (errorCode * 20));
20 Serial.println(buffer);
21}
22
23uint16_t getCalibrationValue(uint8_t index) {
24 return pgm_read_word(&sensorCalibration[index]);
25}
1// Monitor available memory
2int getFreeRAM() {
3 extern int __heap_start, *__brkval;
4 int v;
5 return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
6}
7
8// Memory pool for dynamic allocation
9class MemoryPool {
10private:
11 uint8_t pool[512]; // Fixed size pool
12 bool used[512];
13
14public:
15 void* allocate(size_t size) {
16 for (int i = 0; i <= 512 - size; i++) {
17 bool canAllocate = true;
18 for (size_t j = 0; j < size; j++) {
19 if (used[i + j]) {
20 canAllocate = false;
21 break;
22 }
23 }
24 if (canAllocate) {
25 for (size_t j = 0; j < size; j++) {
26 used[i + j] = true;
27 }
28 return &pool[i];
29 }
30 }
31 return nullptr;
32 }
33
34 void deallocate(void* ptr, size_t size) {
35 if (ptr >= pool && ptr < pool + 512) {
36 int offset = (uint8_t*)ptr - pool;
37 for (size_t i = 0; i < size; i++) {
38 used[offset + i] = false;
39 }
40 }
41 }
42};
43
44MemoryPool memPool;
Professional Arduino firmware requires sophisticated interrupt management for real-time responsiveness.
1#include <Arduino.h>
2
3// Interrupt priority levels
4enum InterruptPriority {
5 CRITICAL = 0, // Emergency stop, safety
6 HIGH = 1, // Time-critical sensors
7 MEDIUM = 2, // User interface
8 LOW = 3 // Background tasks
9};
10
11class InterruptManager {
12private:
13 struct InterruptHandler {
14 void (*handler)();
15 InterruptPriority priority;
16 uint32_t lastExecution;
17 uint16_t minInterval; // Minimum interval in microseconds
18 };
19
20 InterruptHandler handlers[8];
21 uint8_t handlerCount = 0;
22 volatile uint8_t pendingInterrupts = 0;
23
24public:
25 void registerHandler(void (*handler)(), InterruptPriority priority, uint16_t minInterval = 0) {
26 if (handlerCount < 8) {
27 handlers[handlerCount] = {handler, priority, 0, minInterval};
28 handlerCount++;
29 }
30 }
31
32 void processInterrupts() {
33 for (uint8_t i = 0; i < handlerCount; i++) {
34 if (pendingInterrupts & (1 << i)) {
35 uint32_t now = micros();
36 if (now - handlers[i].lastExecution >= handlers[i].minInterval) {
37 handlers[i].handler();
38 handlers[i].lastExecution = now;
39 pendingInterrupts &= ~(1 << i);
40 }
41 }
42 }
43 }
44
45 void triggerInterrupt(uint8_t handlerIndex) {
46 if (handlerIndex < handlerCount) {
47 pendingInterrupts |= (1 << handlerIndex);
48 }
49 }
50};
51
52InterruptManager intManager;
53
54// Critical safety interrupt
55void emergencyStopHandler() {
56 // Disable all outputs immediately
57 PORTB = 0;
58 PORTC = 0;
59 PORTD = 0;
60}
61
62// High-priority sensor interrupt
63void sensorReadHandler() {
64 // Read time-critical sensor data
65 static uint16_t sensorBuffer[16];
66 static uint8_t bufferIndex = 0;
67
68 sensorBuffer[bufferIndex] = analogRead(A0);
69 bufferIndex = (bufferIndex + 1) % 16;
70}
71
72void setup() {
73 intManager.registerHandler(emergencyStopHandler, CRITICAL, 0);
74 intManager.registerHandler(sensorReadHandler, HIGH, 1000); // Max 1kHz
75
76 // Configure external interrupts
77 attachInterrupt(digitalPinToInterrupt(2), []() {
78 intManager.triggerInterrupt(0); // Emergency stop
79 }, FALLING);
80
81 attachInterrupt(digitalPinToInterrupt(3), []() {
82 intManager.triggerInterrupt(1); // Sensor read
83 }, RISING);
84}
85
86void loop() {
87 intManager.processInterrupts();
88 // Main application logic
89}
Create reusable, maintainable code through proper abstraction.
1// Generic sensor interface
2class ISensor {
3public:
4 virtual ~ISensor() {}
5 virtual bool initialize() = 0;
6 virtual float readValue() = 0;
7 virtual bool isValid() = 0;
8 virtual const char* getErrorMessage() = 0;
9};
10
11// Temperature sensor implementation
12class DS18B20Sensor : public ISensor {
13private:
14 uint8_t pin;
15 bool initialized;
16 float lastReading;
17 uint32_t lastReadTime;
18 char errorMsg[32];
19
20public:
21 DS18B20Sensor(uint8_t sensorPin) : pin(sensorPin), initialized(false) {}
22
23 bool initialize() override {
24 pinMode(pin, INPUT_PULLUP);
25 // DS18B20 initialization sequence
26 digitalWrite(pin, LOW);
27 pinMode(pin, OUTPUT);
28 delayMicroseconds(480);
29 pinMode(pin, INPUT_PULLUP);
30 delayMicroseconds(70);
31
32 bool presence = !digitalRead(pin);
33 delayMicroseconds(410);
34
35 if (presence) {
36 initialized = true;
37 strcpy_P(errorMsg, PSTR("OK"));
38 return true;
39 } else {
40 strcpy_P(errorMsg, PSTR("No device found"));
41 return false;
42 }
43 }
44
45 float readValue() override {
46 if (!initialized) return -999.0;
47
48 uint32_t now = millis();
49 if (now - lastReadTime < 750) return lastReading; // DS18B20 conversion time
50
51 // Start conversion
52 sendCommand(0xCC); // Skip ROM
53 sendCommand(0x44); // Convert T
54
55 delay(750); // Wait for conversion
56
57 // Read scratchpad
58 sendCommand(0xCC); // Skip ROM
59 sendCommand(0xBE); // Read scratchpad
60
61 uint16_t rawTemp = readByte() | (readByte() << 8);
62 lastReading = rawTemp * 0.0625; // Convert to Celsius
63 lastReadTime = now;
64
65 return lastReading;
66 }
67
68 bool isValid() override {
69 return initialized && (lastReading > -55 && lastReading < 125);
70 }
71
72 const char* getErrorMessage() override {
73 return errorMsg;
74 }
75
76private:
77 void sendCommand(uint8_t cmd) {
78 for (uint8_t i = 0; i < 8; i++) {
79 writeBit(cmd & 1);
80 cmd >>= 1;
81 }
82 }
83
84 void writeBit(bool bit) {
85 digitalWrite(pin, LOW);
86 pinMode(pin, OUTPUT);
87 delayMicroseconds(bit ? 6 : 60);
88 pinMode(pin, INPUT_PULLUP);
89 delayMicroseconds(bit ? 64 : 10);
90 }
91
92 uint8_t readByte() {
93 uint8_t result = 0;
94 for (uint8_t i = 0; i < 8; i++) {
95 result >>= 1;
96 if (readBit()) result |= 0x80;
97 }
98 return result;
99 }
100
101 bool readBit() {
102 digitalWrite(pin, LOW);
103 pinMode(pin, OUTPUT);
104 delayMicroseconds(3);
105 pinMode(pin, INPUT_PULLUP);
106 delayMicroseconds(10);
107 bool bit = digitalRead(pin);
108 delayMicroseconds(53);
109 return bit;
110 }
111};
112
113// Sensor manager for multiple sensors
114class SensorManager {
115private:
116 ISensor* sensors[8];
117 uint8_t sensorCount = 0;
118
119public:
120 void addSensor(ISensor* sensor) {
121 if (sensorCount < 8 && sensor->initialize()) {
122 sensors[sensorCount++] = sensor;
123 }
124 }
125
126 float readSensor(uint8_t index) {
127 if (index < sensorCount) {
128 return sensors[index]->readValue();
129 }
130 return -999.0;
131 }
132
133 void updateAllSensors() {
134 for (uint8_t i = 0; i < sensorCount; i++) {
135 float value = sensors[i]->readValue();
136 if (sensors[i]->isValid()) {
137 processSensorData(i, value);
138 } else {
139 Serial.print("Sensor ");
140 Serial.print(i);
141 Serial.print(" error: ");
142 Serial.println(sensors[i]->getErrorMessage());
143 }
144 }
145 }
146
147private:
148 void processSensorData(uint8_t sensorIndex, float value) {
149 // Process valid sensor data
150 Serial.print("Sensor ");
151 Serial.print(sensorIndex);
152 Serial.print(": ");
153 Serial.println(value);
154 }
155};
156
157// Usage example
158SensorManager sensorMgr;
159DS18B20Sensor tempSensor1(2);
160DS18B20Sensor tempSensor2(3);
161
162void setup() {
163 Serial.begin(115200);
164 sensorMgr.addSensor(&tempSensor1);
165 sensorMgr.addSensor(&tempSensor2);
166}
167
168void loop() {
169 sensorMgr.updateAllSensors();
170 delay(1000);
171}
This advanced Arduino programming guide provides professional techniques for building robust, maintainable embedded systems. The patterns shown here enable scalable firmware development suitable for production applications.
For related embedded programming concepts, explore our MSP430 LCD interfacing and AVR programming tutorials.