aboutsummaryrefslogtreecommitdiff
path: root/content/articles/temperature-sensors.md
blob: 23fbcd988e666b042386585a318c1daa193f9315 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
---
title: "Temperature Sensors"
slug: "temperature-sensors"
date: "2024-08-26"
toc: true

pics:
  pico-ws:
    css: "image"
    tip: "Pico Ws with BME280 sensors attached."
    sources:
      - "/files/articles/temperature-sensors/pico-ws-1024.webp"
      - src: "/files/articles/temperature-sensors/pico-ws-1024.jpg"
        width: 1024
        height: 771
  old-sensors:
    css: "image"
    tip: "Original BME280 sensors."
    sources:
      - "/files/articles/temperature-sensors/old-sensors-20180724-1024.webp"
      - src: "/files/articles/temperature-sensors/old-sensors-20180724-1024.jpg"
        width: 1024
        height: 768
  old-pi3:
    css: "image"
    tip: "Bedroom Pi 3 HTPC with BME280 sensor attached."
    sources:
      - "/files/articles/temperature-sensors/old-pi3-20180724-1024.webp"
      - src: "/files/articles/temperature-sensors/old-pi3-20180724-1024.jpg"
        width: 1024
        height: 768
  old-web-ui-1:
    css: "image"
    tip: "Original web interface (2018)."
    sources:
      - "/files/articles/temperature-sensors/old-web-ui-1-1024.webp"
      - src: "/files/articles/temperature-sensors/old-web-ui-1-1024.png"
        width: 1024
        height: 576
  old-web-ui-2:
    css: "image"
    tip: "Improved web interface (2019)."
    sources:
      - "/files/articles/temperature-sensors/old-web-ui-2-20190905-1024.webp"
      - src: "/files/articles/temperature-sensors/old-web-ui-2-20190905-1024.png"
        width: 1024
        height: 765
  web-desktop-0:
    css: "image"
    tip: "Current Data, Charts, and Forecast panels in the desktop web interface (2024)."
    sources:
      - "/files/articles/temperature-sensors/web-desktop-0-1024.webp"
      - src: "/files/articles/temperature-sensors/web-desktop-0-1024.png"
        width: 1024
        height: 640
  web-desktop-1:
    css: "image"
    tip: "Edit Sensor dialog in the desktop web interface (2024)."
    sources:
      - "/files/articles/temperature-sensors/web-desktop-1-1024.webp"
      - src: "/files/articles/temperature-sensors/web-desktop-1-1024.png"
        width: 1024
        height: 640
  web-desktop-2:
    css: "image"
    tip: "Charts panel in the desktop web interface (2024)."
    sources:
      - "/files/articles/temperature-sensors/web-desktop-2-1024.webp"
      - src: "/files/articles/temperature-sensors/web-desktop-2-1024.png"
        width: 1024
        height: 640
  web-desktop-3:
    css: "image"
    tip: "Forecast details dialog in the desktop web interface (2024)."
    sources:
      - "/files/articles/temperature-sensors/web-desktop-3-1024.webp"
      - src: "/files/articles/temperature-sensors/web-desktop-3-1024.png"
        width: 1024
        height: 640

carousel:
  bottom:
    - link: "/files/articles/temperature-sensors/web-mobile-0.png"
      tip: "Current Data panel."
      css: "image"
      sources:
        - "/files/articles/temperature-sensors/web-mobile-0-1024.webp"
        - src: "/files/articles/temperature-sensors/web-mobile-0-1024.png"
          width: 460
          height: 1024
    - link: "/files/articles/temperature-sensors/web-mobile-1.png"
      tip: "Edit Sensor dialog."
      css: "image"
      sources:
        - "/files/articles/temperature-sensors/web-mobile-1-1024.webp"
        - src: "/files/articles/temperature-sensors/web-mobile-1-1024.png"
          width: 460
          height: 1024
    - link: "/files/articles/temperature-sensors/web-mobile-2.png"
      tip: "Charts panel."
      css: "image"
      sources:
        - "/files/articles/temperature-sensors/web-mobile-2-1024.webp"
        - src: "/files/articles/temperature-sensors/web-mobile-2-1024.png"
          width: 460
          height: 1024
    - link: "/files/articles/temperature-sensors/web-mobile-3.png"
      tip: "Forecast Details dialog."
      css: "image"
      sources:
        - "/files/articles/temperature-sensors/web-mobile-3-1024.webp"
        - src: "/files/articles/temperature-sensors/web-mobile-3-1024.png"
          width: 460
          height: 1024

# table data
tables:
  # available table properties
  pins:
    name: "Pins"

    cols:
      - id: "color"
        name: "Wire Color"
        tip: "Wire color."
      - id: "pico"
        name: "Pico W"
        tip: "Pico W pin."
      - id: "bme280"
        name: "BME280"
        tip: "BME280 pin."

    rows:
      - color: "Red"
        pico: "#36, 3V3(OUT)"
        bme280: "VIN"
      - color: "Black"
        pico: "#3, GND"
        bme280: "GND"
      - color: "Yellow"
        pico: "#6, GP4, I2C0 SDA"
        bme280: "SDA"
      - color: "Green"
        pico: "#7, GP5, I2C0 SCL"
        bme280: "SCL"
---
## Introduction

We have several [Raspberry Pi Pico Ws][pico w] throughout the house
which collect temperature and humidity measurements and a corresponding
web interface which shows the current measurements, charts of recent
measurements, and the weather forecast for the next few days.

Here is a picture of the web interface:

[{{< pe-figure "web-desktop-0" >}}][web-desktop-0]

The following sections describe the history of this project, the
hardware, the software on the [Pico Ws][pico w], and the software behind
the web interface.

**Note:** [Home Assistant][] or an [Ecobee][] are both more practical
solutions; this started as a tinkering project that accidentally became
useful for us.

That said, there is some value in controlling all of the hardware and
software; none of our our data leaves our home network, we don't have to
create an account on a ridiculous online hub with a dubious privacy
policy, and we don't have to worry about our [devices being bricked in a
couple of years][bricked].

## History

In 2018 I attached [BME280][] sensors to our [Raspberry Pi 3][pi3]
[HTPCs][htpc] and created a minimal web interface to view the sensor
measurements.

Here is a picture of the original [BME280][] sensors:

[{{< pe-figure "old-sensors" >}}][old-sensors]

Here is the original bedroom [Pi 3][pi3] [HTPC][] with a [BME280][]
sensor attached:

[{{< pe-figure "old-pi3" >}}][old-pi3]

And here is the original web interface (yikes!):

[{{< pe-figure "old-web-ui-1" >}}][old-web-ui-1]

In 2019 I added a third sensor attached to a [Raspberry Pi Zero W][pi0w]
in the basement and outdoor measurements pulled from the [National
Weather Service (NWS) API][nws-api].

I also made several improvements to the web interface:

- Redesigned in [Bootstrap 4][].
- Added [chart.js][]-based charts.
- Added weather forecast from [NWS API][nws-api].

Here is the improved web interface from 2019:

[{{< pe-figure "old-web-ui-2" >}}][old-web-ui-2]

The setup above worked for several years, but the software was a
mishmash of undocumented [Ruby][], [shell scripts][], and [cron][] jobs.

This caused several problems:

- It was difficult to install and difficult to maintain.
- It required a full Raspberry Pi running Linux.  This was fine
  for the dual-use [Pi 3][pi3] [HTPCs][htpc], but it felt like a waste
  of a [Pi Zero W][pi0w].
- The web interface queried the status of all sensors on page load
  (pull model).  This made the page unnecessarily slow.
- The web interface would fail to load when sensors were inactive or
  unreachable.
- The charts in the web interface were not mobile-friendly.
- It collected several measurements that we never used, like [load
  average][loadavg] and network quality.
- The collected data was concatenated to a [CSV][] file rather than
  inserted atomically into a database.  This made it slow and difficult
  to query.
- When we moved we needed more sensors, but  I didn't want to "waste"
  any more [Pi Zero Ws][pi0w] or deal with the hassle of setting up the
  software.

In 2022 I [bought a spool of Pico Ws][bought-pico-ws] to replace the
existing hardware and decided to rewrite the web interface to address
the problems listed above.

The [Pico Ws][pico w] sat unused for a couple of years because I didn't
have a good work area.  In 2023 we lost two of the sensors:

- one failed during a [botched Pi Zero W Bookworm
  upgrade][failed-upgrade].
- one failed for no apparent reason.

In 2024 we set up my [home office][] and I started working on the new
hardware and software.

## Hardware

[Raspberry Pi Pico Ws][pico w] microcontrollers with [BME280s][bme280]
connected via [I2C][].  Here are three of them:

[{{< pe-figure "pico-ws" >}}][pico-ws]

[Fritzing][] is on the fritz for me at the moment so I don't have a
pinout diagram.  Here is a pinout table instead:

{{< table "pins" >}}

## Software

The software is available in the [Sensortron Git repository][repo].
The repository contains two directories:

- `pico-w/`: [Pico W][] [Micropython][] code which periodically collects
  and sends [BME280][] sensor measurements to an [API][] endpoint.
- `web/`: Web interface which shows sensor measurements and the weather
  forecast.


### Pico W

[Micropython][] script in the `pico-w/` directory of the [Sensortron Git
repository][repo] which runs on the [Pico Ws][pico w] and does the
following:

1. Read the configuration from `config.py`.
2. Initialize the [BME280][] sensor attached to [I2C][] bus 0.
3. Connect to the [Wi-Fi][] network.
4. Read sensor values every 10 seconds and send them to an [API][]
   endpoint.

The code for the logic above is in [pico-w/main.py][].

Installation is documented and straightforward:

1. Flash latest [Pico W Micropython image][].
2. Copy `main.py` and `bme280.py` to the [Pico W][].
3. Edit `config.py` and copy it to the [Pico W][].

Here is an example `config.py`:

```python
# diningroom.py: example sensortron configuration file

# wifi ssid and password (required)
WIFI_SSID = 'YOUR-WIFI-SSID'
WIFI_PASSWORD = 'YOUR-WIFI-PASSWORD'

# api url (required)
URL = 'http://sensors.home.pmdn.org:1979/api/read'

# pseudo-mac secret (optional, defaults to '' if unspecified)
SECRET = 'SOME-SECRET-STRING'

# host name (optional, defaults to pico-$UNIQUE_ID if unspecified)
HOSTNAME = 'pico-diningroom'
```

### Web interface

Web interface in the `web/` directory of the [Git repository][repo]
which shows current sensor measurements, a filterable chart of recent
sensor measurements, and the weather forecast for the next several days.

The web interface also exposes an [HTTP][] [API][] endpoint which the
[Pico Ws][pico w] use to submit their current sensor readings.

The appearance of the current web interface is similar to the older 2019
web interface, but with many improvements.

Backend improvements:

- Written in [Go][].
- Two dependencies: [go-chi][] and [modernc.org/sqlite][].
- Web assets are [embedded][embed] in the binary.
- Periodic tasks are handled in [goroutines][] spawned from the binary
  rather than a set of [cron][] jobs.
- Data is stored in a [SQLite][] database.
- Sensors post measurements to an [API][] endpoint (push model).
- Built and deployed as single static binary in a a small (<20M)
  [container][].  The build is [multi-stage][] and the output image is
  based on [distroless][].  Persistent data is stored in one place
  (`/data`) so the container is [volume][]-friendly.

Frontend improvements:

- [Bootstrap 5.3][] and [chart.js 4.4][chart.js].
- Sensor display parameters (name, color, and sort order) are now
  editable via the web interface.
- Charts are now [responsive][] and render correctly on desktop and
  mobile.
- Charts can be downloaded as [PNG][] images.
- Removed unused features. Example: air pressure.
- Added new useful features. Example: probability of precipitation.
- Interface refreshes automatically without a page reload.

The home page loads instantly with these improvements.  It's also much
easier to update, build, and deploy.  Here's the one-liner I use to
deploy, split into multiple lines for legibility:

```sh
# - pull latest code from git repo
# - build image
# - stop old container (if any)
# - start new ephemeral container named "sensortron" which exposes port
#   1979, and uses the "sensortron" volume
git pull && podman build -t sensortron . && (
  podman stop sensortron;
  podman run -d --rm -p 1979:1979 -v sensortron:/data --name sensortron sensortron
)
```
&nbsp;

Here are the current data, charts, and forecast panels on the home page
of the 2024 web interface:

[{{< pe-figure "web-desktop-0" >}}][web-desktop-0]

Edit Sensor dialog:

[{{< pe-figure "web-desktop-1" >}}][web-desktop-1]

Charts panel:

[{{< pe-figure "web-desktop-2" >}}][web-desktop-2]

Forecast Details dialog:

[{{< pe-figure "web-desktop-3" >}}][web-desktop-3]

The web interface is [responsive][] so it works just as well on mobile.
Here are screenshots of the same interface components on my phone:

[pico w]: https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html#raspberry-pi-pico-w
  "Raspberry Pi Pico W"
[I2C]: https://en.wikipedia.org/wiki/I%C2%B2C
  "Inter-Integrated Circuit (I2C)"
[bme280]: https://www.bosch-sensortec.com/products/environmental-sensors/humidity-sensors-bme280/
  "BME280 temperature sensor"
[pico-ws]: /files/articles/temperature-sensors/pico-ws.jpg
  "Pico Ws with BME280 sensors attached."
[pi3]: https://www.raspberrypi.com/products/raspberry-pi-3-model-b/
  "Raspberry Pi 3 Model B"
[htpc]: https://en.wikipedia.org/wiki/Home_theater_PC
  "Home Theater PC (HTPC)"
[old-sensors]: /files/articles/temperature-sensors/old-sensors-20180724.jpg
  "Original BME280 sensors."
[old-pi3]: /files/articles/temperature-sensors/old-pi3-20180724.jpg
  "Bedroom Pi 3 HTPC with BME280 sensor attached."
[old-web-ui-1]: /files/articles/temperature-sensors/old-web-ui-1.png
  "Original web interface (2018)."
[nws-api]: https://www.weather.gov/documentation/services-web-api
  "National Weather Service (NWS) Application Programming Interface (API)"
[pi0w]: https://www.raspberrypi.com/products/raspberry-pi-zero-w/
  "Raspberry Pi Zero W"
[old-web-ui-2]: /files/articles/temperature-sensors/old-web-ui-2-20190905.png
  "Improved web interface (2019)."
[bootstrap 4]: https://getbootstrap.com/docs/4.6/getting-started/introduction/
  "Bootstrap 4 CSS framework."
[chart.js]: https://www.chartjs.org/
  "JavaScript charting library."
[ruby]: https://ruby-lang.org/
  "Ruby programming language"
[shell scripts]: https://en.wikipedia.org/wiki/Shell_script
  "Unix shell script."
[cron]: https://en.wikipedia.org/wiki/Cron
  "Command-line Unix job scheduler."
[loadavg]: https://en.wikipedia.org/wiki/Load_(computing)
  "CPU load average."
[csv]: https://en.wikipedia.org/wiki/Comma-separated_values
  "Comma-separated Value (CSV)."
[failed-upgrade]: {{< relref "posts//2023-06-02-end-of-may-miscellany.md" >}}
  "Failed Pi Zero W Bookworm upgrade."
[bought-pico-ws]: {{< relref "posts/2022-11-11-pico-ws.md" >}}
  "Pico Ws"
[home office]: {{< relref "articles/home-office.md" >}}
  "Home Office"
[repo]: https://github.com/pablotron/sensortron
  "Sensortron Git repository."
[micropython]: https://micropython.org/
  "Micropython programming language"
[go]: https://go.dev/
  "Go programming language"
[container]: https://en.wikipedia.org/wiki/Containerization_(computing)
  "Operating-system-level virtualization."
[sqlite]: https://sqlite.org/
  "Small, fast, self-contained SQL database."
[restful]: https://en.wikipedia.org/wiki/REST
  "HTTP-based API which uses HTTP verbs and adheres loosely to REST"
[api]: https://en.wikipedia.org/wiki/API
  "Application Programming Interface (API)"
[wi-fi]: https://en.wikipedia.org/wiki/Wi-Fi
  "Wireless network."
[Pico W Micropython image]: https://micropython.org/download/RPI_PICO_W/
  "Pico W Micropython image."
[go-chi]: https://go-chi.io/
  "Small web router for building HTTP services."
[modernc.org/sqlite]: https://modernc.org/sqlite
  "cgo-free port of SQLite."
[html]: https://en.wikipedia.org/wiki/HTML
  "HyperText Markup Language (HTML)."
[js]: https://en.wikipedia.org/wiki/JavaScript
  "Javascript."
[css]: https://en.wikipedia.org/wiki/CSS
  "Cascading Style Sheets (CSS)."
[embed]: https://pkg.go.dev/embed
  "embed package in Go standard library."
[goroutines]: https://en.wikipedia.org/wiki/Go_(programming_language)#Concurrency
  "Concurrency in Go."
[bootstrap 5.3]: https://getbootstrap.com/docs/5.3/
  "Bootstrap 5.3 CSS framework."
[multi-stage]: https://docs.docker.com/build/building/multi-stage/
  "Multi-stage build."
[responsive]: https://en.wikipedia.org/wiki/Responsive_web_design
  "Responsive web design."
[distroless]: https://en.wikipedia.org/wiki/Responsive_web_design
  "Minimal container base."
[png]: https://en.wikipedia.org/wiki/PNG
  "Portable Network Graphics (PNG)"
[volume]: https://docs.docker.com/engine/storage/volumes/
  "Persistent data storage for containers."
[web-desktop-0]: /files/articles/temperature-sensors/web-desktop-0.png
  "Current Data, Charts, and Forecast panels in the desktop web interface (2024)."
[web-desktop-1]: /files/articles/temperature-sensors/web-desktop-1.png
  "Edit Sensor dialog in the desktop web interface (2024)."
[web-desktop-2]: /files/articles/temperature-sensors/web-desktop-2.png
  "Charts panel in the desktop web interface (2024)."
[web-desktop-3]: /files/articles/temperature-sensors/web-desktop-3.png
  "Forecast Details dialog in the desktop web interface (2024)."
[http]: https://en.wikipedia.org/wiki/HTTP
  "HyperText Transfer Protocol (HTTP)"
[home assistant]: https://en.wikipedia.org/wiki/Home_Assistant
  "Open source home automation software."
[ecobee]: https://en.wikipedia.org/wiki/Ecobee
  "Company which makes smart thermostats and temperature sensors"
[fritzing]: https://en.wikipedia.org/wiki/Fritzing
  "Open source electronics CAD hardware."
[pico-w/main.py]: https://github.com/pablotron/sensortron/blob/main/pico-w/main.py
  "pico-w/main.py in Sensortron Git repository."
[bricked]: https://arstechnica.com/gadgets/2024/09/ftc-urged-to-make-smart-devices-say-how-long-theyll-be-supported/
  "FTC urged to make smart devices say how long they will be supported (arstechnica.com)"