How I built a lighthouse weather station?

Florent Blot
6 min readDec 30, 2020

My dad is a Brittany lover. If he could, he would live in a lighthouse. For his birthday, I thought to giving him a connected lighthouse station.

My idea was to built a device which could inform him about the weather with a color set. I also wanted my dad to be able to change the location or the device’s color from his smartphone. So I had two parts to create: a connected weather station and a mobile app.

When I have a new project in mind, I often search on Thingiverse and Cults3D to get inspiration. This helps me to visualize my idea. I found different lighthouses, here and there. And I found this one by Kijai. So, instead of recreating the wheel, I decided to use it. Plus, it had a hollowed out version.

The lighthouse model had been choose, but the first print I did was too small from what I expected and the lights inside were not visible enough. So I made some changes.
I duplicated the windows to all the walls on the tower. I also cutted the third of the top to let the light shines behind the model. Then, I increased its size to fully fit my printer’s dimensions.

I created the case box for the electronic components. The lower part (grey below) contains a bed to hold the battery, four snap-fits to tie the controller and two others for a power booster. The upper part (orange below) lets the wires going out through a hole.

The base in grey owns a LiPo Battery 3.7v 1200mAh, a PowerBoost Charger 5V and a Raspberry Pi Zero W. This part has three holes on its border — the first gets an access to the PowerBoost Charger by USB, the second fits a switch to power the Raspberry Pi, and the last holds a push-button to shutdown the system manually.

The light is provided by three Flora Smart RGB Neopixel, which climb inside the tower. I welded the first Neopixel upside down to diffuse the light in a more efficient way to the bottom of the lighthouse.

The Raspberry Pi runs a Python program, activated on boot as a service. The script connects to OpenWeatherMap, a free service of weather forecast. It gets current temperature and weather state — shinny, rainy, cloudy, etc — by calling the following function each 10 seconds:

def getCurrentWeather():
try:
data = get(url % (city_id), timeout = 5).json()
temperature = int(data['main']['temp'])
weather_id = data['weather'][0]['id']
return [temperature, weather_id]
except:
pass

Where url is built as:

base = 'https://api.openweathermap.org/data/2.5/weather'
city = 'id=%s'
units = 'units=metric'
appid = 'appid=my_custom_appid'
url = '%s?%s&%s&%s' % (base, city, units, appid)

Then, the three Neopixels (defined on pin D18) update their colors according to the temperature:

pin = board.D18
count = 3
pixels = neopixel.NeoPixel(pin, count, brightness = 1.0, auto_write = False, pixel_order = neopixel.GRB)
def updateNeopixelColor(temperature):
color = selectColorByDegrees(temperature)
pixels.fill(color)
pixels.show()

selectColorByDegrees uses a simple switch and returns a rgb array. It maps the temperature with a color set:

In the meantime, the program continously listens for Classical Bluetooth devices:

server_sock = BluetoothSocket(RFCOMM)
server_sock.bind(("", PORT_ANY))
server_sock.listen(1)
advertise_service(server_sock, 'Lighthouse Weather Station', ...)while True:
client_sock, client_info = server_sock.accept()
while True:
...

Following the request in getCurrentWeather(), if a Bluetooth client (client_sock) is connected, it sends the data out (temperature, weather_id and city_id):

def sendCurrentData(temperature, weather_id, city_id):
try:
data = 'D=%s,W=%s,C=%d' % (temperature, weather_id, city_id)
client_sock.send(data.encode('utf-8'))
except:
pass

I have an Android device, whereas my dad uses an iPhone. I did not think twice to use a multi-platform toolkit I like: Flutter.
On the app side, the weather_id maps a weather icon and the city_id maps a label. Then, the homepage displays them as follows:

I mentioned it, I did not want to stop here — I wanted my dad to be able to “play” with his weather station. As I already wrote a Flutter app with Bluetooth to change Neopixel colors, this was a simple feature adaptation.

However I also wanted him to change the forecast’s location. So I made a grid picker with a set of predefined cities:

When my dad selects the city he wants, the app sends a specific command like CMD=0,VAL=6454880. The RPi, still listening for data, checks the input received:

while True:
client_sock, client_info = server_sock.accept()
while True:
try:
data = client_sock.recv(1024)
except:
break
if len(data) == 0: break
data_decoded = data.decode('utf-8').rstrip()
...
try:
client_sock.close()
except:
pass

After decoded it, the script parses it and executes the right function:

cmd = data_decoded.split('CMD=')[1].split(',VAL=')key = int(cmd[0]) // is 0
val = int(cmd[1]) // is 6454880
if key == 0:
updateCurrentCity(int(val))
elif key == 1:
...

Then, the program updates the global city_id:

def updateCurrentCity(id):
global city_id
city_id = id

The next request with getCurrentWeather will use the new city_id and will show data for the new location:

I added few more commands with different keys and values to do something or get something back, as shutdown the station remotely or getting IP address.

On a last touch, my wife gently agreed to participate and to paint the printed lighthouse. My dad has now a fancy tower on his shelf, telling him how is the current weather.

You can find the Python service, the app code and the printed files in my repository:

⚠️ I updated this project to use Bluetooth Low Energy in Python and Flutter. This migration becomes the master branch on the repository. You can see the migration steps here: Using Bluetooth Low Energy between Raspberry Pi and Flutter 🚀

--

--

Florent Blot

Maker, Mobile developer, Kotlin & Dart enthusiast, IoT & DIY addict. @Geev: https://www.geev.com