Agent Skills: Code Review Facilitator

Automated code review for Arduino/ESP32/RP2040 projects focusing on best practices, memory safety, and common pitfalls. Use when user wants code feedback, says "review my code", needs help improving code quality, or before finalizing a project. Generates actionable checklists and specific improvement suggestions.

UncategorizedID: wedsamuel1230/arduino-skills/code-review-facilitator

Skill Files

Browse the full folder contents for code-review-facilitator.

Download Skill

Loading file tree…

skills/code-review-facilitator/SKILL.md

Skill Metadata

Name
code-review-facilitator
Description
Automated code review for Arduino/ESP32/RP2040 projects focusing on best practices, memory safety, and common pitfalls. Use when user wants code feedback, says "review my code", needs help improving code quality, or before finalizing a project. Generates actionable checklists and specific improvement suggestions.

Code Review Facilitator

Provides systematic code review for microcontroller projects.

Resources

This skill includes bundled tools:

  • scripts/analyze_code.py - Static analyzer detecting 15+ common Arduino issues

Quick Start

Analyze a file:

uv run scripts/analyze_code.py sketch.ino

Analyze entire project:

uv run scripts/analyze_code.py --dir /path/to/project

Interactive mode (paste code):

uv run scripts/analyze_code.py --interactive

Filter by severity:

uv run scripts/analyze_code.py sketch.ino --severity warning

When to Use

  • "Review my code"
  • "Is this code okay?"
  • "How can I improve this?"
  • Before publishing to GitHub
  • After completing a feature
  • When code "works but feels wrong"

Review Categories

1. πŸ—οΈ Structure & Organization

Check For:

β–‘ Single responsibility - each function does ONE thing
β–‘ File organization - separate concerns (config, sensors, display, network)
β–‘ Consistent naming convention (camelCase for variables, UPPER_CASE for constants)
β–‘ Reasonable function length (< 50 lines ideally)
β–‘ Header comments explaining purpose

Common Issues:

| Issue | Bad | Good | |-------|-----|------| | God function | 200-line loop() | Split into readSensors(), updateDisplay(), etc. | | Mixed concerns | WiFi code in sensor file | Separate network.cpp/h | | Unclear names | int x, temp1, val; | int sensorReading, temperatureC; |

Example Refactoring:

// ❌ Bad: Everything in loop()
void loop() {
  // 50 lines of sensor reading
  // 30 lines of display update
  // 40 lines of network code
}

// βœ… Good: Organized functions
void loop() {
  SensorData data = readAllSensors();
  updateDisplay(data);
  if (shouldTransmit()) {
    sendToServer(data);
  }
  handleSleep();
}

2. πŸ’Ύ Memory Safety

Critical Checks:

β–‘ No String class in time-critical code (use char arrays)
β–‘ Buffer sizes declared as constants
β–‘ Array bounds checking
β–‘ No dynamic memory allocation in loop()
β–‘ Static buffers for frequently used strings

Memory Issues Table:

| Issue | Problem | Solution | |-------|---------|----------| | String fragmentation | Heap corruption over time | Use char arrays, snprintf() | | Stack overflow | Large local arrays | Use static/global, reduce size | | Buffer overflow | strcpy without bounds | Use strncpy, snprintf | | Memory leak | malloc without free | Avoid dynamic allocation |

Safe String Handling:

// ❌ Dangerous: String class in loop
void loop() {
  String msg = "Temp: " + String(temp) + "C";  // Fragments heap
  Serial.println(msg);
}

// βœ… Safe: Static buffer with snprintf
void loop() {
  static char msg[32];
  snprintf(msg, sizeof(msg), "Temp: %.1fC", temp);
  Serial.println(msg);
}

// βœ… Safe: F() macro for flash strings
Serial.println(F("This string is in flash, not RAM"));

Memory Monitoring:

// Add to setup() for debugging
Serial.print(F("Free heap: "));
Serial.println(ESP.getFreeHeap());

// Periodic check in loop()
if (ESP.getFreeHeap() < 10000) {
  Serial.println(F("WARNING: Low memory!"));
}

3. πŸ”’ Magic Numbers & Constants

Check For:

β–‘ No unexplained numbers in code
β–‘ Pin assignments in config.h
β–‘ Timing values named
β–‘ Threshold values documented

Examples:

// ❌ Bad: Magic numbers everywhere
if (analogRead(A0) > 512) {
  digitalWrite(4, HIGH);
  delay(1500);
}

// βœ… Good: Named constants
// config.h
#define MOISTURE_SENSOR_PIN     A0
#define PUMP_RELAY_PIN          4
#define MOISTURE_THRESHOLD      512   // ~50% soil moisture
#define PUMP_RUN_TIME_MS        1500  // 1.5 second watering

// main.ino
if (analogRead(MOISTURE_SENSOR_PIN) > MOISTURE_THRESHOLD) {
  digitalWrite(PUMP_RELAY_PIN, HIGH);
  delay(PUMP_RUN_TIME_MS);
}

4. ⚠️ Error Handling

Check For:

β–‘ Sensor initialization verified
β–‘ Network connections have timeouts
β–‘ File operations check return values
β–‘ Graceful degradation when components fail
β–‘ User feedback for errors (LED, serial, display)

Error Handling Patterns:

// ❌ Bad: Assume everything works
void setup() {
  bme.begin(0x76);  // What if it fails?
}

// βœ… Good: Check and handle failures
void setup() {
  Serial.begin(115200);
  
  if (!bme.begin(0x76)) {
    Serial.println(F("BME280 not found!"));
    errorBlink(ERROR_SENSOR);  // Visual feedback
    // Either halt or continue without sensor
    sensorAvailable = false;
  }
  
  // WiFi with timeout
  WiFi.begin(SSID, PASSWORD);
  unsigned long startAttempt = millis();
  while (WiFi.status() != WL_CONNECTED) {
    if (millis() - startAttempt > WIFI_TIMEOUT_MS) {
      Serial.println(F("WiFi failed - continuing offline"));
      wifiAvailable = false;
      break;
    }
    delay(500);
  }
}

5. ⏱️ Timing & Delays

Check For:

β–‘ No blocking delay() in main loop (except simple projects)
β–‘ millis() overflow handled (after 49 days)
β–‘ Debouncing for buttons/switches
β–‘ Rate limiting for sensors/network

Non-Blocking Pattern:

// ❌ Bad: Blocking delays
void loop() {
  readSensor();
  delay(1000);  // Blocks everything for 1 second
}

// βœ… Good: Non-blocking with millis()
unsigned long previousMillis = 0;
const unsigned long INTERVAL = 1000;

void loop() {
  unsigned long currentMillis = millis();
  
  // Handle button immediately (responsive)
  checkButton();
  
  // Sensor reading at interval
  if (currentMillis - previousMillis >= INTERVAL) {
    previousMillis = currentMillis;
    readSensor();
  }
}

// βœ… millis() overflow safe (works after 49 days)
// The subtraction handles overflow automatically with unsigned math

Debouncing:

// Button debouncing
const unsigned long DEBOUNCE_MS = 50;
unsigned long lastDebounce = 0;
int lastButtonState = HIGH;
int buttonState = HIGH;

void checkButton() {
  int reading = digitalRead(BUTTON_PIN);
  
  if (reading != lastButtonState) {
    lastDebounce = millis();
  }
  
  if ((millis() - lastDebounce) > DEBOUNCE_MS) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == LOW) {
        handleButtonPress();
      }
    }
  }
  lastButtonState = reading;
}

6. πŸ”Œ Hardware Interactions

Check For:

β–‘ Pin modes set in setup()
β–‘ Pull-up/pull-down resistors considered
β–‘ Voltage levels compatible (3.3V vs 5V)
β–‘ Current limits respected
β–‘ Proper power sequencing

Pin Configuration:

// ❌ Bad: Missing or incorrect pin modes
digitalWrite(LED_PIN, HIGH);  // Works by accident on some boards

// βœ… Good: Explicit configuration
void setup() {
  // Outputs
  pinMode(LED_PIN, OUTPUT);
  pinMode(RELAY_PIN, OUTPUT);
  
  // Inputs with pull-up (button connects to GND)
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  
  // Analog input (no pinMode needed but document it)
  // SENSOR_PIN is analog input - no pinMode required
  
  // Set safe initial states
  digitalWrite(RELAY_PIN, LOW);  // Relay off at start
}

7. πŸ“‘ Network & Communication

Check For:

β–‘ Credentials not hardcoded (use config file)
β–‘ Connection retry logic
β–‘ Timeout handling
β–‘ Secure connections (HTTPS where possible)
β–‘ Data validation

Secure Credential Handling:

// ❌ Bad: Credentials in main code
WiFi.begin("MyNetwork", "password123");

// βœ… Good: Separate config file (add to .gitignore)
// config.h
#ifndef CONFIG_H
#define CONFIG_H

#define WIFI_SSID     "your-ssid"
#define WIFI_PASSWORD "your-password"
#define API_KEY       "your-api-key"

#endif

// .gitignore
config.h

8. πŸ”‹ Power Efficiency

Check For:

β–‘ Unused peripherals disabled
β–‘ Appropriate sleep modes used
β–‘ WiFi off when not needed
β–‘ LED brightness reduced (PWM)
β–‘ Sensor power controlled

Power Optimization:

// ESP32 power management
void goToSleep(int seconds) {
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  btStop();
  
  esp_sleep_enable_timer_wakeup(seconds * 1000000ULL);
  esp_deep_sleep_start();
}

// Sensor power control
#define SENSOR_POWER_PIN 25

void readSensorWithPowerControl() {
  digitalWrite(SENSOR_POWER_PIN, HIGH);  // Power on
  delay(100);  // Stabilization time
  
  int value = analogRead(SENSOR_PIN);
  
  digitalWrite(SENSOR_POWER_PIN, LOW);   // Power off
  return value;
}

Review Checklist Generator

Generate project-specific checklist:

## Code Review Checklist for [Project Name]

### Critical (Must Fix)
- [ ] Memory: No String in loop()
- [ ] Safety: All array accesses bounds-checked
- [ ] Error: Sensor init failures handled

### Important (Should Fix)
- [ ] No magic numbers
- [ ] Non-blocking delays where possible
- [ ] Timeouts on all network operations

### Nice to Have
- [ ] F() macro for string literals
- [ ] Consistent naming convention
- [ ] Comments for complex logic

### Platform-Specific (ESP32)
- [ ] WiFi reconnection logic
- [ ] Brownout detector consideration
- [ ] Deep sleep properly configured

Code Smell Detection

Automatic Red Flags

| Pattern | Severity | Action | |---------|----------|--------| | String + in loop() | πŸ”΄ Critical | Replace with snprintf | | delay(>100) in loop() | 🟑 Warning | Consider millis() | | while(1) without yield() | πŸ”΄ Critical | Add yield() or refactor | | Hardcoded credentials | πŸ”΄ Critical | Move to config.h | | malloc/new without free/delete | πŸ”΄ Critical | Track allocations | | sprintf (not snprintf) | 🟑 Warning | Use snprintf for safety | | Global variables without volatile for ISR | πŸ”΄ Critical | Add volatile keyword |


Review Response Template

## Code Review Summary

**Overall Assessment:** β­β­β­β˜†β˜† (3/5)

### πŸ”΄ Critical Issues (Fix Before Use)
1. **Memory leak in line 45** - String concatenation in loop()
   - Current: `String msg = "Value: " + String(val);`
   - Fix: Use `snprintf(buffer, sizeof(buffer), "Value: %d", val);`

### 🟑 Important Issues (Fix Soon)
1. **Missing error handling** - BME280 init not checked
2. **Magic number** - `delay(1500)` unexplained

### 🟒 Suggestions (Nice to Have)
1. Consider adding F() macro to Serial.print strings
2. Function `readAllSensors()` could be split

### βœ… Good Practices Found
- Clear variable naming
- Consistent formatting
- Good use of constants in config.h

### Recommended Next Steps
1. Fix critical memory issue first
2. Add sensor error handling
3. Run memory monitoring to verify fix

Quick Reference Commands

// Memory debugging
Serial.printf("Free heap: %d bytes\n", ESP.getFreeHeap());
Serial.printf("Min free heap: %d bytes\n", ESP.getMinFreeHeap());

// Stack high water mark (FreeRTOS)
Serial.printf("Stack remaining: %d bytes\n", uxTaskGetStackHighWaterMark(NULL));

// Find I2C devices
void scanI2C() {
  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      Serial.printf("Found device at 0x%02X\n", addr);
    }
  }
}