When designing embedded systems for environmental control, robotics, or automated agriculture, makers frequently hit a wall: the Raspberry Pi lacks strict real-time hardware PWM, and the Arduino lacks the compute power for local machine learning or heavy database logging. The solution is combining them. In hybrid raspberry and arduino projects, the Pi acts as the high-level brain (running MQTT, computer vision, and web servers) while the Arduino handles sub-millisecond I/O, sensor polling, and relay switching.
The Architecture Decision: Which Board Does What?
Before wiring a single pin, you must decide if a hybrid architecture is actually necessary, or if a single board will suffice. Use this decision matrix to evaluate your project requirements.
| Criteria | Raspberry Pi 4 Only | Arduino Uno R4 Only | Hybrid (Pi + Arduino) |
|---|---|---|---|
| Real-Time PWM / I/O | Poor (Linux kernel interrupts cause jitter) | Excellent (Dedicated hardware timers) | Excellent (Arduino handles I/O) |
| Local ML / Computer Vision | Excellent (Quad-core ARM, VPU) | Impossible (Lacks RAM and NPU) | Excellent (Pi handles inference) |
| Power Consumption | High (~3W - 6W idle) | Low (~0.5W) | Moderate (~4W combined) |
| BOM Cost (2026 pricing) | ~$55 (4GB model) | ~$20 | ~$75 + peripherals |
Hardware Spec Sheet and Pin Mapping
For this build, we are constructing a hybrid greenhouse climate controller. The Arduino reads the DHT22 temperature/humidity sensor and drives the 5V relays for a fan and heater. The Pi reads this data over USB Serial, logs it, and makes high-level decisions.
| Component | Exact Variant | Approx. Cost | Role in System |
|---|---|---|---|
| Host Computer | Raspberry Pi 4 Model B (4GB RAM) | $55.00 | MQTT Broker, Data Logging, Logic |
| Microcontroller | Arduino Uno R4 Minima | $20.00 | Sensor Polling, Relay Switching |
| Sensor | Adafruit DHT22 (AM2302) w/ 10k pull-up | $10.00 | Temperature & Humidity Reading |
| Actuator Driver | HiLetgo 4-Channel 5V Relay Module (Optoisolated) | $6.00 | Switching 120V AC loads (Fan/Heater) |
Arduino Uno R4 Minima Pin Mapping
| Arduino Pin | Destination | Wire Color (Recommended) | Notes |
|---|---|---|---|
| 5V | DHT22 VCC & Relay VCC | Red | Ensure Pi USB port can supply 500mA+ |
| GND | DHT22 GND & Relay GND | Black | Common ground required |
| D2 | DHT22 Data Out | Yellow | Requires 10kΩ pull-up to 5V |
| D8 | Relay 1 (IN1 - Fan) | Blue | Active LOW logic |
| D9 | Relay 2 (IN2 - Heater) | Green | Active LOW logic |
| USB-C | Raspberry Pi USB 3.0 Port | N/A | Use a high-quality data-capable cable |
Firmware and Host Code
The following code targets the Arduino Uno R4 Minima (which uses the Renesas RA4M1 ARM Cortex-M4 processor, requiring the modern Arduino IDE 2.x board definitions) and a Raspberry Pi 4 running Raspberry Pi OS (Bookworm or later).
1. Arduino Firmware (C++)
This firmware polls the DHT22 every 2 seconds, validates the reading to prevent NaN (Not a Number) errors from propagating, and outputs a structured JSON string over the hardware UART (USB Serial).
// Target Board: Arduino Uno R4 Minima
// Library: DHT sensor library by Adafruit (v1.4.6+)
#include <DHT.h>
#define DHTPIN 2
#define DHTTYPE DHT22
#define RELAY_FAN 8
#define RELAY_HEATER 9
DHT dht(DHTPIN, DHTTYPE);
unsigned long lastRead = 0;
const unsigned long READ_INTERVAL = 2000;
int readErrors = 0;
void setup() {
Serial.begin(115200);
pinMode(RELAY_FAN, OUTPUT);
pinMode(RELAY_HEATER, OUTPUT);
// Relays are Active LOW; set HIGH to turn OFF initially
digitalWrite(RELAY_FAN, HIGH);
digitalWrite(RELAY_HEATER, HIGH);
dht.begin();
}
void loop() {
if (millis() - lastRead >= READ_INTERVAL) {
lastRead = millis();
float h = dht.readHumidity();
float t = dht.readTemperature();
// Error handling: Check if reads failed (NaN)
if (isnan(h) || isnan(t)) {
readErrors++;
Serial.print("{\"status\":\"error\",\"code\":");
Serial.print(readErrors);
Serial.println("}");
return;
}
readErrors = 0; // Reset on success
// Simple autonomous control logic on the MCU side
if (t > 28.0) {
digitalWrite(RELAY_FAN, LOW); // Turn ON fan
} else {
digitalWrite(RELAY_FAN, HIGH); // Turn OFF fan
}
// Output JSON for the Raspberry Pi to parse
Serial.print("{\"status\":\"ok\",\"temp\":");
Serial.print(t, 1);
Serial.print(",\"hum\":");
Serial.print(h, 1);
Serial.println("}");
}
// Check for incoming commands from Pi (e.g., manual heater override)
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
if (cmd == "HEATER_ON") digitalWrite(RELAY_HEATER, LOW);
if (cmd == "HEATER_OFF") digitalWrite(RELAY_HEATER, HIGH);
}
}
2. Raspberry Pi Host Script (Python)
This script uses pyserial to read the JSON stream, parse it, and handle malformed data gracefully. Install dependencies via pip install pyserial.
import serial
import json
import time
import sys
PORT = '/dev/ttyACM0'
BAUD = 115200
def main():
try:
ser = serial.Serial(PORT, BAUD, timeout=1)
print(f'Connected to Arduino on {PORT}')
except serial.SerialException as e:
print(f'FATAL: Could not open serial port. {e}')
sys.exit(1)
while True:
try:
raw_line = ser.readline().decode('utf-8').strip()
if not raw_line:
continue
data = json.loads(raw_line)
if data.get('status') == 'ok':
temp = data['temp']
hum = data['hum']
print(f'[LOG] Temp: {temp}C | Humidity: {hum}%')
# Example: Send command back to Arduino if too cold
if temp < 15.0:
ser.write(b'HEATER_ON\n')
else:
print(f'[WARN] Sensor read error code: {data.get("code")}')
except json.JSONDecodeError:
print(f'[ERR] Malformed JSON received: {raw_line}')
except KeyboardInterrupt:
print('\nShutting down...')
ser.close()
sys.exit(0)
if __name__ == '__main__':
main()
Debugging the Handshake: Fixing Serial Port Errors
The most common point of failure in raspberry and arduino projects is the USB serial handshake. If your Python script crashes immediately upon execution, you will likely see this exact error string:
serial.serialutil.SerialException: [Errno 13] could not open port /dev/ttyACM0: [Errno 13] Permission denied: '/dev/ttyACM0'
Ranked Causes and Fixes
- Cause 1: User lacks
dialoutgroup permissions (90% of cases). Linux restricts raw hardware access. Fix: Runsudo usermod -a -G dialout $USER, then log out and log back in (or reboot) for the group change to take effect. - Cause 2: Port is locked by another process. If the Arduino IDE Serial Monitor is open, or a previous Python script crashed without closing the port, the OS locks it. Fix: Run
lsof | grep ttyACM0to find the PID, thensudo kill -9 <PID>. - Cause 3: ModemManager is interfering. On some Ubuntu/Debian builds, the OS probes new serial devices to see if they are modems. Fix: Disable it via
sudo systemctl stop ModemManager.
The First Three Things to Check When It Fails
If the permission error is resolved but you still aren't getting data, run through this physical and logical checklist:
- Verify the USB cable is data-capable. Over 40% of micro-USB and USB-C cables bundled with cheap accessories are 'charge-only' and lack the internal D+/D- data lines. Swap to a known good data cable.
- Check the device enumeration path. The Uno R4 Minima usually mounts as
/dev/ttyACM0, but if you plug in a USB hub or another device first, it might shift to/dev/ttyACM1. Runls -l /dev/ttyACM*to verify. - Confirm baud rate parity. Ensure the
Serial.begin(115200)in the C++ sketch exactly matches theBAUD = 115200variable in the Python script. A mismatch will result in garbled text and constantJSONDecodeErrorexceptions.
Extending and Simplifying the Build
Once the baseline serial handshake is stable, you can scale the system up or down based on your deployment environment.
How to Extend (Adding Computer Vision)
To turn this from a simple data logger into an intelligent agricultural node, add a Raspberry Pi Camera Module 3 (~$30). Mount it inside the enclosure facing the plant canopy. Using the Pi's hardware video encoder, run a lightweight YOLOv8-nano inference script via the ultralytics Python library. If the model detects early signs of powdery mildew or blight with >80% confidence, the Pi can publish an MQTT alert to Home Assistant and send a serial command to the Arduino to trigger a UV-C sterilization relay.
How to Simplify (Reducing BOM and Power)
If your project does not require local camera inference or a heavy local database, the Raspberry Pi is overkill and draws too much power for solar/battery deployments. Simplify the build by dropping the Raspberry Pi entirely and replacing the Arduino Uno R4 with an ESP32-S3 DevKitC-1 (~$12). The ESP32-S3 features dual-core 240MHz processing, native Wi-Fi/BLE, and enough hardware PWM channels to handle both the sensor polling and the cloud telemetry via MQTT, cutting your BOM cost by 70% and idle power draw from 4W down to ~0.8W.
For further reading on serial configurations and board specifics, refer to the official Arduino Uno R4 Minima documentation and the PySerial API reference. For advanced Pi peripheral routing, consult the Raspberry Pi hardware configuration guides.






