When evaluating projects using Raspberry Pi 3 hardware in 2026, the Model B+ remains a highly capable workhorse for headless IoT telemetry nodes. While the Pi 4 and Pi 5 dominate desktop-replacement and heavy-compute roles, the Pi 3 B+ draws significantly less idle current (~230mA vs ~600mA+), making it ideal for always-on environmental monitors running on solar or UPS-backed 5V rails. This guide details a complete, production-ready build: an I2C environmental sensor node that publishes temperature and humidity data via MQTT and triggers a 5V relay fallback if thresholds are breached.
Hardware Specifications and Pin Mapping
Before writing any code, we must establish the electrical boundaries. The Raspberry Pi 3 Model B+ GPIO header operates strictly at 3.3V logic. Feeding 5V back into these pins via a misconfigured relay module will instantly destroy the SoC. The parts list below assumes you are using standard hobbyist breakouts, but the electrical notes are non-negotiable.
Target Board: Raspberry Pi 3 Model B+ (1GB LPDDR2, BCM2837B0 SoC).
OS Requirement: Raspberry Pi OS (Bookworm or newer, 64-bit Lite recommended for headless operation).
Bill of Materials
- Compute: Raspberry Pi 3 Model B+ with 5V 2.5A power supply.
- Sensor: Bosch BME280 I2C Breakout (Adafruit 2652 or Pimoroni PIM285). Ensure it has onboard 3.3V voltage regulation and pull-ups.
- Actuator: 5V Opto-isolated Relay Module (Songle SRD-05VDC-SL-C based) with a removable JD-VCC jumper.
- Wiring: 22 AWG solid core wire or high-quality female-to-female Dupont jumpers.
Data-Dense Pin Mapping & Electrical Spec Table
This table defines the exact physical and BCM (Broadcom) pin mappings required for the Python script below. Pay close attention to the voltage levels and the relay isolation notes.
| Component Pin | RPi 3 B+ Physical Pin | BCM GPIO | Voltage Level | Critical Notes & Constraints |
|---|---|---|---|---|
| BME280 VIN | Pin 1 | N/A (3.3V Power) | 3.3V | Do not use 5V (Pin 2); the breakout expects 3.3V logic. |
| BME280 GND | Pin 6 | N/A (Ground) | 0V | Must share common ground with the Pi and Relay. |
| BME280 SDA | Pin 3 | GPIO 2 (SDA1) | 3.3V Logic | I2C Data. Requires 4.7kΩ pull-up if breakout lacks them. |
| BME280 SCL | Pin 5 | GPIO 3 (SCL1) | 3.3V Logic | I2C Clock. Max bus capacitance 400pF. |
| Relay IN | Pin 11 | GPIO 17 | 3.3V Logic | Active LOW on most opto-isolated modules. |
| Relay VCC | Pin 17 | N/A (3.3V Power) | 3.3V | Powers the optocoupler LED side. Must be 3.3V. |
| Relay JD-VCC | Pin 2 | N/A (5V Power) | 5V | Jumper Removed. Powers the relay coil side independently. |
| Relay GND | Pin 9 | N/A (Ground) | 0V | Common ground for both 3.3V and 5V sides. |
Wiring the I2C Bus and Relay Load
The most common point of failure in projects using Raspberry Pi 3 boards with 5V peripherals is the relay module wiring. Standard hobby relay modules feature a JD-VCC jumper designed to isolate the relay coil power from the optocoupler logic power. If you leave this jumper in place and wire VCC to 5V, you will backfeed 5V directly into the Pi's 3.3V GPIO pin, potentially frying the SoC.
- Isolate the Relay Power: Use pliers to carefully remove the plastic
JD-VCCjumper from the relay module header. - Wire the Coil Side: Connect the
JD-VCCpin to the Pi's 5V (Physical Pin 2). Connect the module'sGNDto the Pi's GND (Physical Pin 9). - Wire the Logic Side: Connect the module's
VCCpin to the Pi's 3.3V (Physical Pin 17). Connect theINpin to GPIO 17 (Physical Pin 11). - Wire the I2C Sensor: Connect the BME280
VINto 3.3V (Pin 1),GNDto GND (Pin 6),SDAto Pin 3, andSCLto Pin 5. - Enable I2C: Boot the Pi, open a terminal, and run
sudo raspi-config. Navigate to Interface Options > I2C and enable it. Reboot the system.
i2cdetect -y 1 in the terminal. You should see 76 or 77 in the grid. If the grid is empty, check your Dupont wire continuity with a multimeter.
Python Control Code with MQTT and Error Handling
The following Python 3.11+ script reads the BME280 sensor via the smbus2 and bme280 libraries, publishes the telemetry to an MQTT broker using paho-mqtt (the industry standard for Python MQTT clients), and toggles the relay if the temperature exceeds a defined threshold. Install the dependencies via pip install smbus2 RPi.bme280 paho-mqtt RPi.GPIO.
import time
import smbus2
import bme280
import paho.mqtt.client as mqtt
import RPi.GPIO as GPIO
# --- Hardware Pin Definitions (BCM Numbering) ---
RELAY_PIN = 17
I2C_BUS = 1
BME280_ADDRESS = 0x76 # Change to 0x77 if your breakout uses the alternate address
# --- MQTT Configuration ---
MQTT_BROKER = "192.168.1.100"
MQTT_PORT = 1883
MQTT_TOPIC_TEMP = "sensors/pi3_node/temperature"
MQTT_TOPIC_HUM = "sensors/pi3_node/humidity"
# --- Thresholds ---
TEMP_THRESHOLD_C = 28.5 # Trigger relay if temp exceeds this value
# --- GPIO Setup ---
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(RELAY_PIN, GPIO.OUT)
# Ensure relay starts in the OFF state (Active LOW modules require HIGH to turn off)
GPIO.output(RELAY_PIN, GPIO.HIGH)
def on_connect(client, userdata, flags, rc, properties=None):
if rc == 0:
print("Successfully connected to MQTT Broker.")
else:
print(f"MQTT Connection failed with result code {rc}")
def main():
# Initialize MQTT Client (Paho v2.0 API callback signature)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="RPi3_EnvNode")
client.on_connect = on_connect
try:
client.connect(MQTT_BROKER, MQTT_PORT, 60)
client.loop_start()
except ConnectionRefusedError as e:
print(f"Fatal: MQTT Broker unreachable at {MQTT_BROKER}. Error: {e}")
GPIO.cleanup()
return
# Initialize I2C and BME280 Calibration
try:
bus = smbus2.SMBus(I2C_BUS)
calibration_params = bme280.load_calibration_params(bus, BME280_ADDRESS)
print("BME280 initialized and calibration parameters loaded.")
except OSError as e:
print(f"Fatal: I2C Bus Error. Check wiring and i2cdetect. Error: {e}")
GPIO.cleanup()
return
print("Starting telemetry loop... (Ctrl+C to exit)")
try:
while True:
# Read Sensor Data
data = bme280.sample(bus, BME280_ADDRESS, calibration_params)
temp_c = round(data.temperature, 2)
humidity = round(data.humidity, 2)
print(f"Temp: {temp_c}°C | Humidity: {humidity}%")
# Publish to MQTT
client.publish(MQTT_TOPIC_TEMP, temp_c)
client.publish(MQTT_TOPIC_HUM, humidity)
# Relay Logic (Active LOW)
if temp_c > TEMP_THRESHOLD_C:
GPIO.output(RELAY_PIN, GPIO.LOW) # Turn Relay ON
print("[ALERT] Temp threshold exceeded. Relay ENGAGED.")
else:
GPIO.output(RELAY_PIN, GPIO.HIGH) # Turn Relay OFF
time.sleep(10) # 10-second telemetry interval
except KeyboardInterrupt:
print("\nShutting down gracefully...")
finally:
client.loop_stop()
GPIO.output(RELAY_PIN, GPIO.HIGH) # Ensure relay is off
GPIO.cleanup()
print("GPIO cleaned up. Exiting.")
if __name__ == "__main__":
main()
Debugging I2C and GPIO Failures
When building embedded projects, hardware and software boundaries blur. If your script crashes on startup, do not guess. Look at the exact traceback string and follow the ranked causes below.
Error 1: OSError: [Errno 121] Remote I/O error
This is the definitive I2C communication failure on the Raspberry Pi. The SoC attempted to clock data on the SCL line but received no ACK (acknowledge) bit from the sensor.
- Cause 1 (Most Likely): The I2C address is wrong. Some BME280 breakouts default to
0x77instead of0x76. Check the silkscreen on the PCB or runi2cdetect -y 1to verify. - Cause 2: Missing pull-up resistors. The Raspberry Pi's internal pull-ups are ~50kΩ, which is too weak for reliable I2C at 400kHz. If your breakout board lacks onboard 4.7kΩ pull-ups to 3.3V, the signal edges will be too rounded.
- Cause 3: I2C interface is disabled in the OS. Re-run
sudo raspi-configand verify the interface is active.
Error 2: ConnectionRefusedError: [Errno 111] Connection refused
This occurs during the client.connect() MQTT phase.
- Cause 1: The MQTT broker (e.g., Mosquitto) is not running on the target IP, or the firewall is blocking port 1883.
- Cause 2: If using Mosquitto 2.0+, anonymous connections are blocked by default. You must configure
allow_anonymous trueinmosquitto.conffor local testing, or implement username/password authentication in the Python script.
The First Three Things to Check When It Fails
If the node is entirely unresponsive or throwing erratic data, execute this triage sequence:
- Run
i2cdetect -y 1: If the sensor address appears, your hardware wiring is good, and the issue is in the Python library or MQTT configuration. If the grid is empty, you have a physical layer problem. - Measure the 3.3V Rail: Put a multimeter on Physical Pin 1 and Pin 6. You must read between 3.25V and 3.35V. If it reads lower, your power supply is sagging, or you have a short on the I2C bus.
- Verify Relay Isolation: Check that the
JD-VCCjumper is physically removed. A misplaced jumper here causes brownouts on the 3.3V rail every time the relay coil energizes, resetting the I2C bus and crashing the script.
Extending and Simplifying the Build
The beauty of using the Raspberry Pi 3 B+ for IoT projects is the flexibility to scale the compute load up or down based on your deployment environment.
How to Simplify (Offline / Local Logging)
If you do not have a network infrastructure or MQTT broker available, strip out the paho-mqtt dependencies entirely. Replace the MQTT publish lines with Python's built-in csv and datetime modules to append readings to a local /var/log/env_data.csv file. You can later extract the SD card and graph the data in Excel or Python Pandas. This reduces network overhead and eliminates broker-related crash vectors.
How to Extend (Edge AI and Visual Alerts)
To push this project into advanced territory, integrate a Raspberry Pi Camera Module V2 via the CSI ribbon cable. Using the libcamera Python bindings, you can program the script to capture a high-resolution image and push it to a Telegram bot API or local web server only when the temperature threshold is breached. Because the Pi 3 B+ has 1GB of RAM and a quad-core CPU, it can comfortably handle local image compression and HTTPS POST requests without dropping the 10-second I2C polling interval.
References and Further Reading:
1. Raspberry Pi Foundation: I2C Configuration Guide
2. Eclipse Paho MQTT Python Client Documentation
3. Bosch Sensortec BME280 Datasheet and Specifications






