<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Flask on Guillaume Delré</title><link>https://guillaumedelre.github.io/tags/flask/</link><description>Recent content in Flask on Guillaume Delré</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sun, 17 Nov 2019 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/tags/flask/index.xml" rel="self" type="application/rss+xml"/><item><title>From a €10 sensor to a Home Assistant dashboard with a Raspberry Pi and MQTT</title><link>https://guillaumedelre.github.io/2019/11/17/from-a-10-sensor-to-a-home-assistant-dashboard-with-a-raspberry-pi-and-mqtt/</link><pubDate>Sun, 17 Nov 2019 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2019/11/17/from-a-10-sensor-to-a-home-assistant-dashboard-with-a-raspberry-pi-and-mqtt/</guid><description>A €10 BME280 sensor, a Raspberry Pi, and an MQTT broker: building a room climate monitor that feeds Home Assistant.</description><content:encoded><![CDATA[<p>The question was simple: what&rsquo;s the temperature and humidity in my home office right now? Not the weather outside, not a city average — the actual conditions in the room where I spend most of my day. Opening a weather app for that felt wrong.</p>
<p>A Raspberry Pi was already running on the shelf. A BME280 sensor costs around €10. This should have been a weekend project.</p>
<p>It mostly was, except for the part where I assumed reading a temperature sensor meant reading a register.</p>
<h2 id="four-wires-and-a-chip">Four wires and a chip</h2>
<p>The Bosch BME280 measures temperature, humidity, and atmospheric pressure over I²C. Four wires to the Raspberry Pi GPIO pins, enable I²C in <code>raspi-config</code>, and the sensor shows up at address <code>0x77</code> on the bus:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>i2cdetect -y <span style="color:#ae81ff">1</span>
</span></span></code></pre></div><p>That&rsquo;s the easy part. The catch is what happens next.</p>
<h2 id="you-dont-just-read-the-temperature">You don&rsquo;t just read the temperature</h2>
<p>The BME280 doesn&rsquo;t hand you <code>21.5°C</code>. It gives you raw ADC values: 20-bit integers that mean absolutely nothing by themselves. To get an actual temperature, you have to:</p>
<ol>
<li>Read the calibration coefficients Bosch burned into the chip&rsquo;s EEPROM at the factory (registers <code>0x88</code>, <code>0xA1</code>, <code>0xE1</code>)</li>
<li>Apply Bosch&rsquo;s compensation formulas: double-precision floating point arithmetic that uses those coefficients to turn raw values into real measurements</li>
<li>Wait for the measurement to finish by polling the status register</li>
</ol>
<p>The temperature compensation alone takes the raw value, applies a quadratic correction with three calibration constants, and spits out a value in hundredths of degrees Celsius. Pressure depends on the corrected temperature. Humidity depends on both.</p>
<p>It&rsquo;s all straight from the <a href="https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf" target="_blank" rel="noopener noreferrer">Bosch datasheet</a>, nothing clever. But it does mean the driver isn&rsquo;t a five-liner. It&rsquo;s implementing a spec, not importing a library.</p>
<h2 id="making-it-network-accessible">Making it network-accessible</h2>
<p>Once the driver worked, the next question was how to get those values into Home Assistant. The simplest path: a Flask API with two endpoints.</p>
<p><code>GET /bme280</code> returns the current reading as JSON. <code>GET /bme280/publish</code> reads the sensor and pushes the three values to an MQTT broker. A cron job on the Pi calls the publish endpoint every few minutes, and Home Assistant picks up the values in real time.</p>
<p>The MQTT discovery mechanism made the Home Assistant side almost frictionless. One <code>mosquitto_pub</code> command per sensor type — publishing a JSON config payload to the right topic — and the entities appear automatically in the UI. No <code>configuration.yaml</code> editing, no restart required.</p>
<pre tabindex="0"><code>BME280  ──I²C──►  bme280.py  ──►  Flask API  ──MQTT──►  Home Assistant
</code></pre><p>The full setup guide is <a href="https://github.com/guillaumedelre/bme280" target="_blank" rel="noopener noreferrer">in the repo</a>.</p>
<h2 id="what-i-didnt-expect">What I didn&rsquo;t expect</h2>
<p><strong>The Bosch calibration is non-negotiable.</strong> I started by reading the raw temperature register directly and scaling it naively. The result was numbers that looked almost plausible and were completely wrong. The compensation algorithm isn&rsquo;t optional decoration, it&rsquo;s what makes the output mean anything.</p>
<p><strong>Polling beats events here.</strong> The sensor doesn&rsquo;t push data, you ask it for a reading. A cron job every minute is all you need for room monitoring. Real-time streaming would be overkill and would probably wear out the sensor faster.</p>
<p><strong>MQTT discovery is underrated.</strong> Manually declaring sensors in <code>configuration.yaml</code> works, but auto-discovery just feels right. Publish a config payload once, and Home Assistant takes it from there. Adding a new sensor type later takes about thirty seconds.</p>
<p>The room is now 21.4°C and 47% humidity. I know this without opening anything.</p>
<h2 id="a-note-on-the-official-bosch-sensorapi">A note on the official Bosch SensorAPI</h2>
<p>While writing the driver I peeked at the <a href="https://github.com/boschsensortec/BME280_SensorAPI" target="_blank" rel="noopener noreferrer">official Bosch SensorAPI</a> for reference. Two things caught my attention.</p>
<p>The Linux userspace example doesn&rsquo;t actually work on a Raspberry Pi out of the box. Several contributors tripped over the same bug independently: <code>ioctl</code> is called before <code>dev_addr</code> is assigned, so the I²C device address never gets set properly. The fix is obvious once you see it, and multiple PRs documented it, but they sat open for years. Some still are.</p>
<p>Then there&rsquo;s <a href="https://github.com/boschsensortec/BME280_SensorAPI/pull/94" target="_blank" rel="noopener noreferrer">PR #94</a> (still open as of early 2025), reporting undefined behavior in <code>bme280_get_sensor_mode()</code>: the left operand of a bitwise <code>&amp;</code> is an uninitialized variable, caught by static analysis.</p>
<p>The chip itself is great. But manufacturer reference code is a starting point, not gospel. Implementing the compensation algorithm straight from the datasheet meant I understood every line of it. When a reading looks weird, there&rsquo;s no mystery C library to blame.</p>
<div style="border: 1px solid #e8e8e8; padding: 16px; margin-top: 2em; border-radius: 3px;">
  <img src="https://cdn.simpleicons.org/github" width="20" style="vertical-align: middle; margin-right: 8px;" />
  <strong><a href="https://github.com/guillaumedelre/bme280" target="_blank" rel="noopener noreferrer">guillaumedelre/bme280</a></strong>
  <p style="margin: 8px 0 0; color: #828282; font-size: 14px;">Python driver for the BME280 sensor — temperature, humidity, and pressure over I²C, with MQTT publishing and Home Assistant integration.</p>
</div>
]]></content:encoded></item></channel></rss>