Solar Hosting

This project is something I started tinkering on just over two years ago. It's not perfect, but I feel I have gotten it to a point where it is resilient enough to share.

It is easy to underestimate how much power is needed to sustain modern life and this experiment has given me a whole new appreciation for how power hungry even our smallest devices can be. I'll be updating this project over time as I find ways to further improve on the system and I would love to hear from you if you have suggestions or ideas on what can be improved. I'm not an electrical engineer and have lots to learn.

The philosophy

When we moved to Sweden two years ago, I quickly realised that full time solar hosting wouldn't be possible. During our first winter here, we didn't see the sun for over five weeks (it even made the news). In contrast, during summer we can get a good twenty hours of daylight. Although it's not always sunny and certainly not always strong enough to charge something.

This means I needed to design my system for resilience and have a plan for my solar host going offline. I needed some kind of monitoring and potentially a backup host. Worst case, I wanted to show a simple page explaining why my site is offline from time to time.

Another challenge is that we currently live in an apartment without a balcony. When you look at other well known solar hosting projects they tend to be built around 12V systems, using large lead-acid batteries and huge solar panels. We don't have that sort of room and I would rather avoid lead-acid — it's not very sustainable and can be fairly dangerous.

Most single board computers generally require 5V, so a 12V system is perhaps overkill. You also lose energy in the conversion from 12V to 5V. With that in mind I decided to build my system around smaller components at 3.2V.

The hardware

You will need:

I also used a few washers and battery connectors when connecting everything together. For the most part everything is plug and play, but you will need to snap off the battery holder on the solar charger HAT and solder some additional wires; two that run to the battery and two that run to the solar panel connector. The solar charger also requires a resistor matched to your panel. This is covered in the documentation and is something I have tweaked a few times (more on that later).

If I was to start from scratch today, I would consider a bigger battery and a bigger 20 Watt panel. Without the solar panel attached, the 20Ah battery I chose lasts for about 6.5 days, so 100Ah would give about one month of run time. The panel is OK, but once the battery is out, it can take a good week of sunlight for the system to boot again, so a bigger panel is probably going to be my next purchase.

The Raspberry Pi Zero W/H

I tested a few small computers to see which were most power efficient. Using a cheap USB power tester I got the following results:

I also checked online to see what other tinkerers had seen in terms of power consumption. The original Pi Zero seemed to do well in most tests. It's also well supported in terms of software and you can optimise its power consumption by turning off features like Bluetooth, the HDMI circuitry and LEDs.

Ideally I would have plugged a USB Ethernet adapter into the Pi Zero for a faster and more stable network connection, but the general consensus is that WiFi requires less power as it only draws power when in use. The other benefit of WiFi is that I can move the Pi around as the sun moves throughout the year.

The LiFePO4wered/Pi+ solar charger / HAT

This little HAT is what makes this project possible. They are made by a small team but are well designed and well supported. The LiFePO4wered/Pi+ has many smart features that make using the Pi off-grid possible. There are wake up timers, a real time clock that manages system time and an application watchdog that can monitor and reboot the system when your software crashes. While the Pi is off, the LiFePO4wered/Pi+ keeps track of time and monitors system voltages while consuming around 4 µA — so the Pi can sit in standby for many years.

The HAT comes with a CLI tool that allows you to get the current health of the panel and battery. This allowed me to create a small python utility that writes a few stats to a JSON file which then gets consumed by this site. If you can see the battery to the left of this article, your visit was served by the solar Pi.

As mentioned earlier, you will need to solder a resistor to the HAT to optimise how your battery charges. From the documentation:

The MPP resistor value can be calculated with the following formula:
RMPP = 51815 / (VMPP – 4.66)

I contacted the solar panel manufacturer and they said the Vmmp for my panel is 7.38V. Therefore I needed a 19 kohm resistor. Unfortunately this didn't really work out for me. It meant that I needed 7.38V from the panel and it just wasn't happening that often. It also breaks USB charging (you only get 5V from the Pi charger) which is less than ideal. At the moment I am testing a 31 kohm resistor which sets the charge voltage to 6.33v… so far this is working quite well and the solar panel is doing a much better job at charging the battery and running the Pi at the same time. I'm sure this is an area of improvement though.

The battery

As the name of the HAT suggests, it supports lithium iron phosphate batteries (LiFePO4). These batteries score well in terms of toxicity, safety and life cycle. This video does a pretty good job at selling the safety side of LFP:

I am particularly impressed by how much better for the environment LiFePO4 batteries are, with lifespans ten times longer than lead-acid batteries and a non-toxic chemistry.

Raspikey eMMC module

SD cards are not overly reliable (although they are getting better). I decided it would be better to use an eMMC module for longevity. The 16GB should be enough room for everything, but it really depends on how big the site you want to host is. I am secretly hoping that this hardware combination will last for a long time, if it does I have a similar project in mind for an off-grid time capsule.

The software

I've put a copy of the custom code you need for this project on GitHub, if you would rather start there.

Once you have the hardware in place, it's time to move on to the software setup. Start with a copy of Raspberry Pi OS Lite - you want as little software as possible. You can use the Raspberry Pi imager to setup WiFi, SSH, a fixed local IP address (for convenience) and a few other bits before putting the eMMC module in the Pi and booting. I also have a guide on my site for setting up the perfect Raspberry Pi. I followed most of the steps detailed there including the power saving tips like turning off HDMI and the LEDs, as well as setting up Docker.

You'll also want to install the software for the solar charger HAT which gives you CLI access to control settings and query the state of the HAT. I tweaked the following settings:

  • AUTO_BOOT to 2. lifepo4wered-cli set AUTO_BOOT 2. This ensures the Pi will boot back up when there is enough battery power to do so.
  • VBAT_BOOT to 3325. lifepo4wered-cli set VBAT_BOOT 3325. This is about 75% of the battery, so the Pi will boot once the battery hits this level.
  • LED_STATE to 0x00. lifepo4wered-cli set LED_STATE 0x00. To turn off the main power LED and save more energy.

These settings can be saved to flash using the command lifepo4wered-cli set CFG_WRITE 0x46. I highly recommend scanning the documentation to see how customisable this board is. At some point I may setup the watchdog feature to reboot the Pi in case of a software crash.

After that I created a custom python script that runs every 10 minutes. It gets started automatically at boot time (see method 4: systemd and this service file) and checks the current battery voltage. It then attempts to turn that into a battery percentage. It's not perfect as batteries tend to discharge in a non-linear fashion, but it does the job for now. There is also some code that translates that percentage into an LED output for a PIMORONI LED Shim. This is commented out for now in order to maximise run time. The python script populates a file called solar.json which sits in the web directory and contains the battery percentage, battery voltage, load, uptime and CPU temperature. This creates a crude API.

Installing Docker takes a bit of time on the Pi Zero but it's worth it. You can explore containerised software without messing up your OS. I use three containers to host my site, a lightweight static web server, cloudflared and watchtower. Here is my docker-compose.yaml.

version: "3.3"

services:
  web:
    image: joseluisq/static-web-server:latest
    container_name: web
    env_file: ./.settings.env
    ports:
      - 8080:80
    volumes:
      - ./web:/public:ro
    restart: unless-stopped
    command: -g info

  watchtower:
    image: containrrr/watchtower:latest
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    network_mode: host
    command: tunnel --no-autoupdate run --token ${CF_WEB_TOKEN}
    depends_on:
      - web

An example .settings.env file can be found on GitHub along with everything else you should need.

Cloudflared and self hosting

Self hosting has always been something I have shied away from. I just don't like the idea of opening up ports on my home router and letting outside traffic stream in. Cloudflare have a solution to this called Cloudflare Tunnels. It's a free service that creates a secure tunnel between your home network and Cloudflare's sustainable infrastructure. You can then expose certain services on your network to your Cloudflare domains. I'm really impressed with this service and the ease at which it can be setup. You can setup most of it via an online control panel, then just install the Cloudflared software on the host or in a container. In my docker-compose.yaml file you will need to replace ${CF_WEB_TOKEN} with your Cloudflare token from their web interface.

Using Cloudflare also brings other advantages like website caching, DDoS protection, a firewall and load balancing (which sounds useful). Cloudflare deserves a thank you or two for making all these fantastic things.

Static web server

Static Web Server (or SWS) is a very small and fast production-ready web server suitable to serve static web files. It is focused on lightness, ease of use, high performance and safety. It's perfectly suited to the memory and processor constrained Pi Zero.

This website is static (a bunch of HTML, CSS and JS files). It's built with Nuxt.js and Nuxt content, replacing the previous site that used a decoupled WordPress backend. I haven't published this site to a public GitHub repo yet, but I plan to in future and i'll update this project when I do.

I created a new Vue component that checks the values in solar.json and then shows the battery value with a background colour and icon. There is a good chance you are seeing it on the left hand side of this page right now.

The background colours change based on the battery percentage and I have output the other data in solar.json into the background too — I hope to make that a little more visually interesting in future.

This site has always responded to various environmental variables. The image quality is reduced if you visit from a weak 2G/3G connection. The images are compressed further if you turn on Save-Data. With battery data now easily accessible, I decided to make this another variable to respond to. It's most noticeable when the battery is < 35% — the images will become grayscale and heavily compressed.

Watchtower

Updating a containerised app is simple with Watchtower. Watchtower will pull down an updated image, gracefully shut down your existing container and restart it with the same options that were used when it was deployed initially. I have this running to ensure that my Docker containers stay up to date… mostly for security.

Security

Whilst Cloudflare does a good job at securing access to the Pi, I still took a few additional steps to improve security. This included:

This last thing I installed is log2ram to reduce writes to the disk. It's not really a security benefit but may help reduce power consumption and improve the longevity of the eMMC.

Designing for resilience

Like I mentioned at the start of this project, I needed to design this system with resilience in mind. That means finding a way to keep my site online when we may not see sun for several weeks.

We run Home Assistant on our home network (I need to write more about this, but for now you can browse this repo on GitHub). Home Assistant is fantastic piece of software and I use it to control and automate all sorts of things.

The first step is to monitor what the solar host is up to. I setup a Home Assistant sensor that checks the solar.json file and logs the battery state. Home Assistant gives us a nice UI for tracking the battery over time:

I also setup some simple push notifications from Home Assistant to my phone to tell me when the the solar goes offline (the sensor switches to Unavailable) and online again (the sensor switches from Unavailable to a value).

The next step was to work out what to do when the solar host goes offline and there are quite a few potential options to explore:

  1. I could change my DNS as the Pi powers off, pointing my URL elsewhere (perhaps to a generic HTML page other solar hosts could use, explaining the site is offline, and when it might be back again).
  2. I could set up a backup VPS or static host which serves a mirror of my site, perhaps on a different URL (but this seems wasteful).
  3. I could explore Cloudflare's load balancing tools, splitting the traffic across the solar host and another self hosted computer (most likely another Pi).
  4. I could just let my site go offline, after all it's a low traffic portfolio site.
  5. I could power the site up and down on a schedule to make it more predictable and extend battery life.

A little magic

At the moment I am exploring solution 3 with a potential twist.

Cloudflare tunnels support load balancing, so setting up a secondary host running over the same tunnel and with a copy of my website allows Cloudflare to randomly send traffic to my solar Pi or to a secondary host. This is how it is setup today. Your visit will be randomly handled by one of two Pis: the solar Pi or a Pi Zero v2.

Our home electricity is on a renewables tariff and Sweden has one of the lowest carbon intensity grids in the world, so I don't feel too bad about having a secondary Pi sipping around 130mA and acting as a backup host, however I am considering using our Home Assistant instance to improve things further.

With some small tweaks to the Home Assistant push notification automations I mentioned above, I can startup and shutdown the secondary host on demand. When the solar host is online and the battery is at a certain percentage, the secondary host can take a rest. Once the battery gets low though, Home Assistant will see that and get the secondary Pi back online, ready to serve some traffic. To me, that feels a little like magic.