This document provides detailed performance analysis of the PVRouter system, including timing measurements, memory usage, and optimization strategies.
The PVRouter achieves excellent performance characteristics for a real-time embedded power management system:
- Real-time processing: 100% success rate with 9.6 kHz sampling
- Memory efficiency: Uses only 26.3% RAM and 27.8% Flash on Arduino Uno
- Accuracy: <1% error in power measurements with proper calibration
- Response time: <1 mains cycle for load switching decisions
- Reliability: Zero missed interrupts under normal operating conditions
| Operation | Typical Time | Max Time | Notes |
|---|---|---|---|
| ADC sample processing | 25μs | 35μs | Per sample (V or I) |
| Power calculation | 15μs | 25μs | 16-bit integer math |
| Energy bucket update | 8μs | 12μs | Simple addition |
| Load state decision | 5μs | 10μs | Threshold comparison |
| Total ISR time | 53μs | 82μs | Well within 104μs budget |
| Operation | Frequency | Typical Time | Notes |
|---|---|---|---|
| Temperature reading | 1/second | 750ms | DS18B20 conversion time |
| Serial output | 5 seconds | 2ms | Data logging period |
| User input processing | Continuous | <1ms | Pin state checking |
| Display update | 1 second | 5ms | If enabled |
Mains Cycle (50Hz): 20ms
├── Half-cycle: 10ms
├── ADC samples per cycle: ~192 (9.6kHz / 50Hz)
├── Zero-crossing detection: <500μs
└── TRIAC switching window: <100μs
| Component | Bytes | Percentage | Description |
|---|---|---|---|
| Shared Variables | 156 | 29.0% | ISR-main communication |
| Processing State | 128 | 23.8% | Power calculations, energy buckets |
| Temperature Data | 80 | 14.9% | Sensor buffers and readings |
| Configuration | 72 | 13.4% | Load states, priorities |
| Serial Buffers | 64 | 11.9% | Output formatting |
| Stack | 38 | 7.1% | Function call overhead |
// Critical shared data between ISR and main loop
volatile int32_t copyOf_sumP_atSupplyPoint[3]; // 12 bytes
volatile int32_t copyOf_sum_Vsquared[3]; // 12 bytes
volatile float copyOf_energyInBucket_main; // 4 bytes
volatile uint16_t copyOf_countLoadON[2]; // 4 bytes
volatile bool flags[6]; // 6 bytes
// Total: ~40 bytes for shared state| Component | Bytes | Percentage | Description |
|---|---|---|---|
| Processing Engine | 3200 | 35.7% | Core power calculation algorithms |
| Utility Functions | 2400 | 26.8% | Temperature, relay, pin control |
| Main Application | 1800 | 20.1% | Main loop and initialization |
| Configuration | 800 | 8.9% | Compile-time constants and validation |
| Debug/Logging | 500 | 5.6% | Serial output and debugging |
| Libraries | 252 | 2.8% | OneWire, ArduinoJson dependencies |
Test Setup: Arduino Uno, 16MHz, standard 3-phase configuration Measurement Method: Digital pin toggling with oscilloscope
| Metric | Value | Unit | Notes |
|---|---|---|---|
| ADC conversion time | 13 | μs | Hardware limit (104 ADC clock cycles) |
| Sample processing time | 25 | μs | Software processing per sample |
| Maximum samples/second | 9,615 | Hz | Theoretical maximum |
| Actual sampling rate | 9,600 | Hz | Practical sustained rate |
| ISR overhead | 8 | μs | Context switching |
| Total cycle time | 104 | μs | Meets real-time requirements |
Test Setup: Calibrated with professional power meter (Sentron PAC 4200)
| Load Type | Power Range | Accuracy | Notes |
|---|---|---|---|
| Resistive (heater) | 100W - 3000W | ±0.5% | Ideal load type |
| Inductive (motor) | 500W - 2000W | ±1.2% | With power factor correction |
| Mixed loads | 200W - 2500W | ±0.8% | Typical household |
| Low power | 10W - 100W | ±2.0% | Limited by ADC resolution |
Test Setup: Multiple DS18B20 sensors, controlled temperature chamber
| Metric | Value | Unit | Notes |
|---|---|---|---|
| Resolution | 0.01 | °C | 12-bit conversion |
| Accuracy | ±0.5 | °C | Sensor specification |
| Conversion time | 750 | ms | Maximum for 12-bit |
| Multiple sensors | 5 | count | Tested configuration |
| Read cycle time | 850 | ms | Including CRC validation |
-
Template Metaprogramming
// Compile-time bounds checking - zero runtime cost template<uint8_t IDX> int16_t readTemperature() const { static_assert(IDX < N, "Index out of bounds"); return readTemperature(IDX); }
-
Constexpr Calculations
// Pre-calculated at compile time inline constexpr float invSUPPLY_FREQUENCY{ 1.0F / SUPPLY_FREQUENCY }; inline constexpr float invDATALOG_PERIOD{ 1.0F / DATALOG_PERIOD_IN_MAINS_CYCLES };
-
Static Configuration Validation
// Catch errors at compile time, not runtime static_assert(check_pins(), "Duplicate pin definition!"); static_assert(NO_OF_DUMPLOADS > 0, "Must have at least one load");
-
Integer Arithmetic
// Use 16-bit integers instead of floating point where possible int16_t power_x100 = (voltage_raw * current_raw) >> 8;
-
Direct Port Manipulation
// Faster than digitalWrite() inline void setPinHigh(uint8_t pin) { *portOutputRegister(digitalPinToPort(pin)) |= digitalPinToBitMask(pin); }
-
Minimal ISR Processing
// Keep ISR lean - defer non-critical work to main loop ISR(ADC_vect) { processRawSamples(currentPhase); // Essential only // Heavy lifting done in main loop }
-
Stack vs Heap
// Use stack allocation exclusively - no malloc/free static ScratchPad buffer; // Reusable static buffer
-
Efficient Data Types
// Use smallest suitable type uint8_t loadCount; // Instead of int uint16_t powerReading; // Instead of uint32_t when sufficient
-
Shared Buffers
// Reuse buffers for temporary data static uint8_t tempBuffer[9]; // Shared for all temperature sensors
| Load Count | RAM Impact | Flash Impact | Performance Impact |
|---|---|---|---|
| 2 loads (current) | Baseline | Baseline | Baseline |
| 4 loads | +24 bytes | +200 bytes | +5μs ISR time |
| 8 loads | +56 bytes | +500 bytes | +12μs ISR time |
| 16 loads | +120 bytes | +1.2KB | +25μs ISR time |
Limit: ~16 loads before ISR time exceeds budget on Arduino Uno
| Sensor Count | RAM Impact | Flash Impact | Conversion Time |
|---|---|---|---|
| 5 sensors (current) | Baseline | Baseline | 4.25s total |
| 10 sensors | +40 bytes | +100 bytes | 8.5s total |
| 20 sensors | +120 bytes | +200 bytes | 17s total |
Practical Limit: ~10 sensors for reasonable update rates
- Clock: 16MHz
- RAM: 2KB (26.3% used)
- Flash: 32KB (27.8% used)
- Performance: Baseline
- Suitability: ✅ Excellent for standard installations
- Clock: 16MHz
- RAM: 8KB (6.7% used)
- Flash: 256KB (3.5% used)
- Performance: Same as Uno
- Suitability: ✅ Overkill but allows many expansions
- Clock: 48MHz
- RAM: 32KB (~25% used)
- Flash: 256KB (~35% used)
- ADC: 12-bit with superior noise characteristics and much faster conversion
- Sampling Rate: >100kHz (10x+ faster than Uno's 9.6kHz)
- Performance: 3x faster CPU + 10x+ faster ADC = dramatically improved capabilities
- Language: Planned rewrite in Rust for memory safety and performance
- Suitability: ✅ Future platform for next-generation PVRouter
- Arduino Uno is sufficient and cost-effective
- Focus on proper calibration for accuracy
- Use compile-time optimizations
- Future SAMD21 + Rust implementation will provide:
- Dramatically superior ADC performance (>10x sampling rate)
- Much lower ADC noise for better power measurement accuracy
- Memory safety guarantees from Rust's ownership system
- Zero-cost abstractions for optimal performance
- Advanced compile-time error checking
- Ideal for installations requiring maximum precision and reliability
- Use Arduino Mega for >8 loads
- Implement load grouping algorithms
- Consider distributed processing
// Free RAM monitoring
inline int freeRam() {
extern int __heap_start, *__brkval;
int v;
return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
}
// ISR timing validation
volatile uint8_t lowestNoOfSampleSetsPerMainsCycle; // Should remain stable- Stress Test: Run for 24+ hours monitoring missed cycles
- Load Test: Add maximum loads and verify response times
- Temperature Test: Operate across temperature range -10°C to +60°C
- Accuracy Test: Compare with calibrated power meter
This performance analysis demonstrates that the PVRouter achieves excellent real-time performance while maintaining efficient resource usage suitable for embedded applications.