iot


Orange Pi 3B  

I’ve never been a fan of the Raspberry Pi. In my opinion, it occupies an intermediate position where it is too underpowered for desktop use and too overpowered for IoT projects:

  • To use them as a desktop, there are great X86 alternatives available at about the same price than a RPi 5 but much more powerful, such as the Intel N100.
  • And for IoT projects, the ESP32 is the king, with amazing boards like the C6 featuring a RISC-V processor, Wifi 6, Zigbee, etc., all at a price of less than 10 euros.

So it’s place may be TV boxes (where I prefer a Chromecast with Android) or small servers where the power consumtion is important because they are always on.

I bought an Orange Pi 3B: 4 cores, 4GB RAM, 64GB eMMC (~50 euros in Aliexpress) to replace my old X86 home server (Intel N450: 2 cores, 2 GB RAM, 64GB SSD):

http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-3B.html

The Orange Pi 3B shares the form factor with the Raspberry Pi 3B but it is almost as powerful as the Raspberry Pi 4. Notably, the Orange Pi 3B comes with several advantages over the RPi 4:

  • Support for eMMC (much faster and reliable than SD cards)
  • A power button
  • A full-size HDMI port
  • External antenna
  • And it’s cheaper

I installed the Ubuntu Jammy server image in the eMMC following the OPi manual. It needs to use a USB-A male to USB-A male cable and the RKDevTool (it’s in Chinese) that runs only in Windows.

And, as this machine is going to be exposed to internet, I hardened a bit the security:

  • Changed the APT repositories to ports.ubuntu.com
  • Regenerated SSH server keys
  • Removed SSH root access
  • Changed passwords
  • Renamed the orangepi user
  • Removed the local autologin

To remove the local autologin we need to edit:

  • /lib/systemd/system/getty@.service.d/override.conf: For the display console autologin
  • /lib/systemd/system/serial-getty@.service.d/override.conf: For the serial console autologin
[Service]
ExecStartPre=/bin/sh -c 'exec /bin/sleep 10'
ExecStart=
ExecStart=-/sbin/agetty --noissue --autologin orangepi %I $TERM
Type=idle

Removing the “–autologin orangepi”. If you rename the orangepiuser but you want to keep the autologin, you’ll also need to change the username here.

Then I moved the docker containers and other services from my old X86 server:

  • Home Assistant (docker container)
  • ESPHome dashboard (docker container)
  • Pi-hole (docker container)
  • nginx (for certbot and DNS DoT for Pihole)
  • certbot (to maintain the SSL certificate for Home Assistant)
  • ddclient (dynamic DNS updater)
  • NAS (do not expect anything fancy, I access a USB disk via SSH, it’s enough for Kodi & backups)

Everything seems to work smoothly now.


Climate control with ESPHome and Home Assistant  

Two years ago I started to need controlling my home heating system while I’m not at home. I could go the easy way and buy a couple Nest thermostats, but I preferred the DIY way.

Connecting the boiler to the ESP32 via the relay module

ESP32 board with ESPHome

I connected the boiler to a ESP-WROOM-32 board via a relay module. The box and the cables were more expensive than the board (~10 EUR) and the relay module (~5 EUR).

The ESP32 board is running ESPHome: https://esphome.io/. I think it is a very nice project and very easy to setup. All the configuration is done via YAML files. The board is connected to the home WiFi and it has a fallback hotspot.

ESPHome has a nice web UI

My home heating system has two radiating floor zones with two independent pumps. I also decided to automate the boiler’s “Winter mode”, in this mode the boiler heats the water for the heating, and I wanted to disable it when the heating is not working.

In my case I needed to activate the winter mode when any pump is working and keep it working for a period of time after the pump is off.

This is my ESPHome YAML config:

esphome:
  name: heating
  platform: ESP32
  board: nodemcu-32s

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: ""

ota:
  password: ""

wifi:
  ssid: "xxx"
  password: "xxx"
  reboot_timeout: 90s
  manual_ip:
    static_ip: 192.168.1.30
    gateway: 192.168.1.1
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Heating Fallback Hotspot"
    password: "xxx"

captive_portal:

web_server:
  port: 80

debug:

script:
  - id: keep_winter_mode_on
    mode: restart
    then:
      - logger.log: "Keep Winter mode start"
      - if:
          condition:
            and:
              - switch.is_off: zone1_pump
              - switch.is_off: zone2_pump
          then:
            - logger.log: "Keep Winter mode will stop"
            - delay: 15min
            - switch.turn_off: winter_mode
            - logger.log: "Keep Winter mode stopped"

  - id: zone1_pump_security
    mode: restart
    then:
      - logger.log: "Zone 1 security start"
      - delay: 60min
      - switch.turn_off: zone1_pump
      - logger.log: "Zone 1 security stop"

  - id: zone2_pump_security
    mode: restart
    then:
      - logger.log: "Zone 2 security start"
      - delay: 60min
      - switch.turn_off: zone2_pump
      - logger.log: "Zone 2 security stop"

switch:
  - platform: gpio
    pin: GPIO16
    name: "Winter mode"
    id: winter_mode
    inverted: true
    restore_mode: ALWAYS_OFF

  - platform: gpio
    pin: GPIO17
    name: "Zone 1 pump"
    id: zone1_pump
    inverted: true
    restore_mode: ALWAYS_OFF
    on_turn_on:
      then:
        - script.stop: keep_winter_mode_on
        - switch.turn_on: winter_mode
        - script.execute: zone1_pump_security
    on_turn_off:
      then:
        - script.stop: zone1_pump_security
        - script.execute: keep_winter_mode_on

  - platform: gpio
    pin: GPIO18
    name: "Zone 2 pump"
    id: zone2_pump
    inverted: true
    restore_mode: ALWAYS_OFF
    on_turn_on:
      then:
        - script.stop: keep_winter_mode_on
        - switch.turn_on: winter_mode
        - script.execute: zone2_pump_security
    on_turn_off:
      then:
        - script.stop: zone2_pump_security
        - script.execute: keep_winter_mode_on

Thermometers

To measure the temperature in the rooms, I used two Xiaomi Mi Home Bluetooth Thermometer 2 (~6 EUR each). They transmit the temperature via BLE (Bluetooth Low Energy) beacons.

Their LCD display is very convenient and, as they are battery powered, you can place them in the better part of the room. I flashed them with this custom firmware:

https://github.com/pvvx/ATC_MiThermometer

I’m still surprised by these small beasts, there are now firmwares to transform them in Zigbee:

https://devbis.github.io/telink-zigbee/.

Home Assistant

The control, reading the thermometers and activating the pumps, is done via a Home Assistant (HA) running in an old X86 tablet with Ubuntu (this is usually run in a Raspberry Pi or similar…).

I installed HA in a Docker container, this is my script to update and start the container:

#!/bin/bash
cd $(dirname $(readlink -f $0))
docker stop homeassistant
docker rm homeassistant
docker pull ghcr.io/home-assistant/home-assistant:stable
docker run -d \
	--name homeassistant \
	--privileged \
	--restart=unless-stopped \
	-e TZ=Europe/Madrid \
	-v /usr/local/homeassistant/config:/config \
	-v /etc/letsencrypt:/etc/letsencrypt \
	--network=host \
	ghcr.io/home-assistant/home-assistant:stable
docker image prune --all

Home assistant reads the thermometers via the Passive BLE monitor integration: https://github.com/custom-components/ble_monitor that can be easily installed via HACS (the Home Assistant Community Store). I needed a Bluetooth 5 USB adapter.

Then, I needed to setup two thermostats in HA via the YAML configuration file:

climate:
  - platform: generic_thermostat
    name: "Living Room"
    unique_id: zone_1_thermostat
    heater: switch.zone_1_pump
    target_sensor: sensor.ble_temperature_living_room_thermometer
    min_temp: 15
    max_temp: 20
    ac_mode: false
    target_temp: 17
    cold_tolerance: 0.5
    hot_tolerance: 0
    min_cycle_duration:
      minutes: 30
    keep_alive:
      minutes: 5
    initial_hvac_mode: "off"
    away_temp: 15
    precision: 0.1

  - platform: generic_thermostat
    name: "Bedrooms"
    unique_id: zone_2_thermostat
    heater: switch.zone_2_pump
    target_sensor: sensor.ble_temperature_bedrooms_thermometer
    min_temp: 15
    max_temp: 20
    ac_mode: false
    target_temp: 17
    cold_tolerance: 0.5
    hot_tolerance: 0
    min_cycle_duration:
      minutes: 3
    keep_alive:
      minutes: 5
    initial_hvac_mode: "off"
    away_temp: 15
    precision: 0.1

Home Assistant doubles as temperature and humidity logger, and its easy to configure dashboards:

And, of course, now I’m using HA to control many other things at home.

Another complex parts were:

  • Making HA accessible via internet setting up a couple port redirections (one for HA and another for certbot) and a dynamic DNS service
  • Setup nginx and certbot for HTTPS
  • Connecting it to Google Home to allow receiving voice commands from my Nest Minis

But that is another long story…

The final installation if the ESP board, the relay module and the power adapter inside a box