<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Python | Tomokatsu Yukishita | yre.jp</title><link>https://yre.jp/en/tag/python/</link><atom:link href="https://yre.jp/en/tag/python/index.xml" rel="self" type="application/rss+xml"/><description>Python</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-US</language><lastBuildDate>Tue, 19 Jul 2022 00:00:00 +0000</lastBuildDate><image><url>https://yre.jp/media/icon_hufbc159bd6ce6a866189b19a79c0d0f51_12846_512x512_fill_lanczos_center_3.png</url><title>Python</title><link>https://yre.jp/en/tag/python/</link></image><item><title>Build a Cloud-Connected Photo Frame with Raspberry Pi, Google Drive, rclone, fbi, and Python</title><link>https://yre.jp/en/post/raspi-photoframe/</link><pubDate>Tue, 19 Jul 2022 00:00:00 +0000</pubDate><guid>https://yre.jp/en/post/raspi-photoframe/</guid><description>&lt;p>I have been shooting with a digital SLR since 2003, and my total shot count is now at 388,524. I wanted a way to display those photos as a slideshow at home — without the hassle of manually copying files to an SD card every week.&lt;/p>
&lt;h2 id="the-problem-with-commercial-photo-frames">The Problem with Commercial Photo Frames&lt;/h2>
&lt;p>Most digital photo frames read images from an SD card or USB drive, in the 7–10 inch range. They work fine, but constantly re-copying photos is tedious enough to make you stop using it.&lt;/p>
&lt;p>What I wanted was a photo frame that pulls photos automatically from cloud storage like Google Drive. No such product seemed to exist, so I built one.&lt;/p>
&lt;p>
&lt;figure id="figure-the-concept-automatic-photo-sync-from-cloud-storage-to-the-photo-frame">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="System diagram of a cloud-connected photo frame using Google Drive and Raspberry Pi. Shows cloud storage automatically syncing photos for slideshow display" srcset="
/media/raspi-photoframe/5621deba8a18b839c7a4321764bb05e8_hub05f2d0d129c7ee7542169c2c963abde_86374_3f5a7eb7c7a059ffa19f361f5c9be38d.webp 400w,
/media/raspi-photoframe/5621deba8a18b839c7a4321764bb05e8_hub05f2d0d129c7ee7542169c2c963abde_86374_26bbc4e9cdb29660629bcf887c20be66.webp 760w,
/media/raspi-photoframe/5621deba8a18b839c7a4321764bb05e8_hub05f2d0d129c7ee7542169c2c963abde_86374_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://yre.jp/media/raspi-photoframe/5621deba8a18b839c7a4321764bb05e8_hub05f2d0d129c7ee7542169c2c963abde_86374_3f5a7eb7c7a059ffa19f361f5c9be38d.webp"
width="720"
height="405"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
The concept: automatic photo sync from cloud storage to the photo frame
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h2 id="hardware">Hardware&lt;/h2>
&lt;h3 id="raspberry-pi-4">Raspberry Pi 4&lt;/h3>
&lt;p>A photo frame runs 24/7, so low power consumption matters. Raspberry Pi 4 fits well.&lt;/p>
&lt;a href="https://www.amazon.co.jp/dp/B081YD3VL5?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/51ETv8Le3nL._AC_.jpg" alt="Raspberry Pi 4 Model B 4GB (Japan authorized distributor)">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">Raspberry Pi 4 Model B 4GB (Japan authorized distributor)&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h3 id="mobile-monitor">Mobile Monitor&lt;/h3>
&lt;p>A 15.6-inch FullHD IPS mobile monitor. Choosing an IPS panel avoids the narrow viewing angles common in off-the-shelf photo frames. Mobile monitors are compact and flexible to position.&lt;/p>
&lt;a href="https://amzn.to/4sgZdM5" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/71lbYPykVrL._AC_SL1500_.jpg" alt="15.6-inch Mobile Monitor with Touch Panel">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">15.6-inch Mobile Monitor with Touch Panel&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h3 id="microsd-card-and-power-supply">microSD Card and Power Supply&lt;/h3>
&lt;a href="https://amzn.to/4smVEnt" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/615exWhvZ0L._AC_SL1200_.jpg" alt="SanDisk High Endurance microSDHC 32GB for Dashcam and Action Cameras">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">SanDisk High Endurance microSDHC 32GB for Dashcam and Action Cameras&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;a href="https://www.amazon.co.jp/dp/B087CF6127?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/61W8QsDXSYL._AC_SL1500_.jpg" alt="Miuzei Raspberry Pi 4 USB-C Power Supply 5.1V 3A with Switch (PSE certified)">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">Miuzei Raspberry Pi 4 USB-C Power Supply 5.1V 3A with Switch (PSE certified)&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h2 id="system-architecture">System Architecture&lt;/h2>
&lt;h3 id="os-raspberry-pi-os">OS: Raspberry Pi OS&lt;/h3>
&lt;p>Write &lt;a href="https://www.raspberrypi.com/software/" target="_blank" rel="noopener">Raspberry Pi OS&lt;/a> to the SD card using Raspberry Pi Imager. Since no desktop is needed for a photo frame, the Lite (CLI-only) version is sufficient.&lt;/p>
&lt;h3 id="image-display-fbi">Image Display: fbi&lt;/h3>
&lt;p>&lt;code>fbi&lt;/code> renders images directly to the framebuffer from the command line. An earlier version used a GUI on Ubuntu but proved unstable, so I switched to this CLI-based approach.&lt;/p>
&lt;h3 id="cloud-storage-sync-rclone">Cloud Storage Sync: rclone&lt;/h3>
&lt;p>&lt;a href="https://rclone.org" target="_blank" rel="noopener">rclone&lt;/a> downloads photos from Google Drive on a schedule. It supports over 70 cloud storage providers including Dropbox and S3.&lt;/p>
&lt;p>The flow is: rclone downloads photos → fbi displays them as a slideshow.&lt;/p>
&lt;h2 id="setup">Setup&lt;/h2>
&lt;h3 id="installing-raspberry-pi-os">Installing Raspberry Pi OS&lt;/h3>
&lt;p>Use Raspberry Pi Imager to write the image to the SD card.&lt;/p>
&lt;p>
&lt;figure id="figure-writing-the-os-image-with-raspberry-pi-imager">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Raspberry Pi Imager screen showing OS selection and SD card write options. The WRITE button is visible" srcset="
/media/raspi-photoframe/8ffcb7bfb32b5079533396b7542ed9a1_hu8982ae701f5d02b9ab9d1e54062b4b24_153362_fa36f9d2badcc15fd6500bcdc35fea83.webp 400w,
/media/raspi-photoframe/8ffcb7bfb32b5079533396b7542ed9a1_hu8982ae701f5d02b9ab9d1e54062b4b24_153362_5221b7fcaf02876b6223f09d17408d34.webp 760w,
/media/raspi-photoframe/8ffcb7bfb32b5079533396b7542ed9a1_hu8982ae701f5d02b9ab9d1e54062b4b24_153362_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://yre.jp/media/raspi-photoframe/8ffcb7bfb32b5079533396b7542ed9a1_hu8982ae701f5d02b9ab9d1e54062b4b24_153362_fa36f9d2badcc15fd6500bcdc35fea83.webp"
width="760"
height="504"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
Writing the OS image with Raspberry Pi Imager
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;p>Either 32-bit or 64-bit works for this use case.&lt;/p>
&lt;h3 id="installing-fbi">Installing fbi&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo apt-get -y install fbi
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="granting-permissions">Granting Permissions&lt;/h4>
&lt;p>By default only root can use fbi to display images. Add the &lt;code>pi&lt;/code> user to the &lt;code>video&lt;/code> group:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pi@raspi3-photo:~ $ ls -Fla /dev/fb0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crw-rw---- &lt;span class="m">1&lt;/span> root video 29, &lt;span class="m">0&lt;/span> Apr &lt;span class="m">7&lt;/span> 10:33 /dev/fb0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo usermod -aG video pi
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="installing-rclone">Installing rclone&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">curl https://rclone.org/install.sh &lt;span class="p">|&lt;/span> sudo bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After installing, run &lt;code>rclone config&lt;/code> to authenticate with Google Drive.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
In a headless SSH-only environment, &lt;code>rclone config&lt;/code> will display a browser authentication URL during setup. Open that URL on another device to authorize access to your Google account.
&lt;/div>
&lt;/div>
&lt;h2 id="slideshow-program">Slideshow Program&lt;/h2>
&lt;h3 id="algorithm">Algorithm&lt;/h3>
&lt;ol>
&lt;li>Use &lt;code>rclone ls&lt;/code> to list photos in the designated cloud storage folder&lt;/li>
&lt;li>Start downloading images at random once an initial batch is available&lt;/li>
&lt;li>Display downloaded images with &lt;code>fbi&lt;/code>&lt;/li>
&lt;li>Continue fetching more images in the background&lt;/li>
&lt;li>Advance to a random new image at the configured interval&lt;/li>
&lt;li>Use weighted random selection — recently shown images are deprioritized&lt;/li>
&lt;li>Run &lt;code>rclone ls&lt;/code> weekly to merge newly added photos into the database&lt;/li>
&lt;li>Keep the database in memory (not a JSON file) for faster merging&lt;/li>
&lt;/ol>
&lt;h3 id="language-python">Language: Python&lt;/h3>
&lt;p>The slideshow controller is written in Python. The refactored object-oriented version reduced startup time by &lt;strong>1700%&lt;/strong> compared to the original.&lt;/p>
&lt;p>For Python learning, &lt;a href="https://www.udemy.com/course/python-beginner/" target="_blank" rel="noopener">Python 3 Bootcamp by Silicon Valley Engineer (Udemy)&lt;/a> was helpful.&lt;/p>
&lt;h2 id="source-code">Source Code&lt;/h2>
&lt;p>Published on GitHub:&lt;/p>
&lt;p>&lt;a href="https://github.com/yukishita/photoView4" target="_blank" rel="noopener">GitHub - yukishita/photoView4: Cloud-connected slideshow program&lt;/a>&lt;/p>
&lt;h2 id="usage">Usage&lt;/h2>
&lt;p>Add this to crontab to start the slideshow at boot:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">@reboot /home/pi/photoView4/photoView4.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="demonstration">Demonstration&lt;/h2>
&lt;p>The improved v4 demo. Startup to first image display is dramatically faster than the original:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/VP7y_E34EJA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;h3 id="original-version-for-reference">Original Version (for reference)&lt;/h3>
&lt;p>The original downloads the full file list before starting, which makes startup slow:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/-dxP_aHSjIU?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;h2 id="tips-scheduled-display-power-off">Tips: Scheduled Display Power Off&lt;/h2>
&lt;p>Running the display 24/7 wastes electricity. Use &lt;code>vcgencmd&lt;/code> in crontab to schedule power:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> * * * /usr/bin/vcgencmd display_power &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="m">30&lt;/span> &lt;span class="m">6&lt;/span> * * * /usr/bin/vcgencmd display_power &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This turns the display off at midnight and back on at 6:30 AM. A motion or light sensor can make this even more automatic.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Role&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Raspberry Pi 4&lt;/td>
&lt;td>Main computer (low power, always-on)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Mobile monitor&lt;/td>
&lt;td>FullHD IPS display for high-quality photo viewing&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>fbi&lt;/td>
&lt;td>Direct framebuffer image rendering from CLI&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>rclone&lt;/td>
&lt;td>Automatically downloads photos from Google Drive&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Python&lt;/td>
&lt;td>Slideshow control with weighted random selection&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Raspberry Pi 4 was used here, but Raspberry Pi Zero 2 W should also work. For an always-on device, choosing a lower-power board reduces running costs.&lt;/p></description></item><item><title>Continuous CO2 Monitoring with Raspberry Pi, CO2-mini, and Zabbix: A Complete Setup Guide</title><link>https://yre.jp/en/post/co2-mini/</link><pubDate>Tue, 19 Jul 2022 00:00:00 +0000</pubDate><guid>https://yre.jp/en/post/co2-mini/</guid><description>&lt;p>During the COVID-19 pandemic, I bought a CO2 monitor — the CUSTOM CO2-mini. It runs on USB power, and as it turns out, it can also communicate over USB. I connected it to a Raspberry Pi and set up Zabbix to continuously monitor indoor CO2 levels. Here is the full setup guide.&lt;/p>
&lt;h2 id="hardware">Hardware&lt;/h2>
&lt;h3 id="raspberry-pi">Raspberry Pi&lt;/h3>
&lt;a href="https://www.amazon.co.jp/dp/B081YD3VL5?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/51ETv8Le3nL._AC_.jpg" alt="Raspberry Pi 4 Model B 4GB (Japan authorized distributor)">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">Raspberry Pi 4 Model B 4GB (Japan authorized distributor)&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h3 id="co2-mini-custom-co2-monitor">CO2-mini (CUSTOM CO2 Monitor)&lt;/h3>
&lt;a href="https://www.amazon.co.jp/dp/B00I3XJ9LM?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/31zFBrDz0-L._SL1000_.jpg" alt="CUSTOM CO2 Monitor CO2-mini">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">CUSTOM CO2 Monitor CO2-mini&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h3 id="power-supply-and-microsd-card">Power Supply and microSD Card&lt;/h3>
&lt;a href="https://www.amazon.co.jp/dp/B07DN5V3VN?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/61Iaz4sqdaS._AC_SL1500_.jpg" alt="Smraza Raspberry Pi 4 USB-C Power Supply 5V 3A with On/Off Switch">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">Smraza Raspberry Pi 4 USB-C Power Supply 5V 3A with On/Off Switch&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;a href="https://www.amazon.co.jp/dp/B08K41Q79R?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/51D2iw1fbnS._AC_SL1000_.jpg" alt="SanDisk Ultra microSD 128GB UHS-I Class10">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">SanDisk Ultra microSD 128GB UHS-I Class10&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h2 id="reading-data-from-the-co2-mini-python">Reading Data from the CO2-mini (Python)&lt;/h2>
&lt;p>When you connect the CO2-mini to a Raspberry Pi via USB, it is recognized without any special drivers. Data retrieval uses the &lt;a href="https://github.com/heinemml/CO2Meter" target="_blank" rel="noopener">heinemml/CO2Meter&lt;/a> Python library.&lt;/p>
&lt;h3 id="installing-the-library">Installing the Library&lt;/h3>
&lt;p>On Raspberry Pi OS Bookworm (2023 and later), installing packages directly into the system Python with &lt;code>pip&lt;/code> is restricted. Use a virtual environment instead:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python3 -m venv /root/co2env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/root/co2env/bin/pip install git+https://github.com/heinemml/CO2Meter
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="alert alert-note">
&lt;div>
On Raspberry Pi OS Bookworm and later, &lt;code>sudo pip3 install ...&lt;/code> may fail with an &amp;ldquo;externally managed environment&amp;rdquo; error. Using a virtual environment as shown above is the safe approach.
&lt;/div>
&lt;/div>
&lt;h3 id="script-writing-co2-and-temperature-to-shared-memory">Script: Writing CO2 and Temperature to Shared Memory&lt;/h3>
&lt;p>To make values accessible to the Zabbix agent, the script continuously writes them to files under &lt;code>/dev/shm/&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">CO2Meter&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sensor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CO2Meter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/dev/hidraw0&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># may be hidraw1 or similar on your system&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sensor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_data&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;temperature&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="s1">&amp;#39;co2&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/dev/shm/co2&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;w&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;co2&amp;#39;&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/dev/shm/temperature&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;w&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;temperature&amp;#39;&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="alert alert-note">
&lt;div>
The device path &lt;code>/dev/hidraw0&lt;/code> may differ on your system. Run &lt;code>ls /dev/hidraw*&lt;/code> to find the correct path after plugging in the CO2-mini.
&lt;/div>
&lt;/div>
&lt;h3 id="auto-start-at-boot-crontab">Auto-start at Boot (crontab)&lt;/h3>
&lt;p>The device requires root access, so create a shell script that runs as root:&lt;/p>
&lt;p>&lt;code>/root/co2.sh&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>sleep &lt;span class="m">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/root/co2env/bin/python3 /root/co2.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Add to root&amp;rsquo;s crontab to run at boot:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">@reboot /root/co2.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="zabbix-configuration">Zabbix Configuration&lt;/h2>
&lt;p>The approach is:&lt;/p>
&lt;ul>
&lt;li>The Python script continuously writes CO2 and temperature values to &lt;code>/dev/shm/&lt;/code>&lt;/li>
&lt;li>The Zabbix agent reads those files via &lt;code>cat&lt;/code> and returns the values&lt;/li>
&lt;/ul>
&lt;h3 id="zabbix-server-creating-the-host-and-items">Zabbix Server: Creating the Host and Items&lt;/h3>
&lt;p>Install the zabbix-agent on the Raspberry Pi and add it as a host in Zabbix server.&lt;/p>
&lt;p>
&lt;figure id="figure-adding-the-raspberry-pi-as-a-zabbix-host">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Zabbix host creation screen showing the Raspberry Pi&amp;amp;rsquo;s IP address being added as a new host" srcset="
/media/co2-mini/da06e12e47b74a91077c01cd16525293_hu313760b14cba40592b4c4bfe8a53f596_72911_1b1d6d1894adc4b8bbffecf8e53f3544.webp 400w,
/media/co2-mini/da06e12e47b74a91077c01cd16525293_hu313760b14cba40592b4c4bfe8a53f596_72911_fea422ab79f99abf87bd10e92c424003.webp 760w,
/media/co2-mini/da06e12e47b74a91077c01cd16525293_hu313760b14cba40592b4c4bfe8a53f596_72911_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://yre.jp/media/co2-mini/da06e12e47b74a91077c01cd16525293_hu313760b14cba40592b4c4bfe8a53f596_72911_1b1d6d1894adc4b8bbffecf8e53f3544.webp"
width="760"
height="196"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
Adding the Raspberry Pi as a Zabbix host
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;p>Create two items — one for CO2 concentration and one for temperature.&lt;/p>
&lt;p>
&lt;figure id="figure-creating-co2-and-temperature-items-in-zabbix">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Zabbix item list showing CO2 concentration and temperature items for the Raspberry Pi host" srcset="
/media/co2-mini/5ce5e9a101dc3a9f247f663aa8f0a7b3_hud28ccdbdbed7334ddc3ee0b604058f9a_103897_9c11300127e1612b7ef1f8456817bccb.webp 400w,
/media/co2-mini/5ce5e9a101dc3a9f247f663aa8f0a7b3_hud28ccdbdbed7334ddc3ee0b604058f9a_103897_90cd0f3739ec77d7e841d665277f56f1.webp 760w,
/media/co2-mini/5ce5e9a101dc3a9f247f663aa8f0a7b3_hud28ccdbdbed7334ddc3ee0b604058f9a_103897_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://yre.jp/media/co2-mini/5ce5e9a101dc3a9f247f663aa8f0a7b3_hud28ccdbdbed7334ddc3ee0b604058f9a_103897_9c11300127e1612b7ef1f8456817bccb.webp"
width="760"
height="608"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
Creating CO2 and temperature items in Zabbix
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;p>
&lt;figure id="figure-configuring-item-keys-to-match-userparameter-names">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Zabbix item key configuration screen showing UserParameter keys co2.co2 and co2.temp being set" srcset="
/media/co2-mini/9017e828e3c4efcae08dca0091351baa-1024x819_hu8f2faefe9ab31af6c31fc87aa0f0d22a_115242_3294bf3e25f8357036b10c044d045648.webp 400w,
/media/co2-mini/9017e828e3c4efcae08dca0091351baa-1024x819_hu8f2faefe9ab31af6c31fc87aa0f0d22a_115242_e4cb036ba6e841d0d2fb0d635f2939e1.webp 760w,
/media/co2-mini/9017e828e3c4efcae08dca0091351baa-1024x819_hu8f2faefe9ab31af6c31fc87aa0f0d22a_115242_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://yre.jp/media/co2-mini/9017e828e3c4efcae08dca0091351baa-1024x819_hu8f2faefe9ab31af6c31fc87aa0f0d22a_115242_3294bf3e25f8357036b10c044d045648.webp"
width="760"
height="608"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
Configuring item keys to match UserParameter names
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;p>Use the following item keys:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">co2.co2 (CO2 concentration)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">co2.temp (Temperature)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="zabbix-agent-configuration-file">Zabbix Agent: Configuration File&lt;/h3>
&lt;p>Edit &lt;code>/etc/zabbix/zabbix_agentd.conf&lt;/code> to set the server address:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Server=&amp;lt;zabbix-server address&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ServerActive=&amp;lt;zabbix-server address&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Include=/etc/zabbix/zabbix_agentd.conf.d/*.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="userparameter-configuration">UserParameter Configuration&lt;/h3>
&lt;p>Create &lt;code>/etc/zabbix/zabbix_agentd.conf.d/userparameter_co2.conf&lt;/code>. It simply &lt;code>cat&lt;/code>s the shared memory files:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">UserParameter=co2.co2,cat /dev/shm/co2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">UserParameter=co2.temp,cat /dev/shm/temperature
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Restart the agent:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo systemctl restart zabbix-agent
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="results">Results&lt;/h2>
&lt;p>CO2 concentration and temperature are now graphed in Zabbix.&lt;/p>
&lt;p>
&lt;figure id="figure-co2-concentration-graph-in-zabbix-values-drop-at-ventilation-events">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Zabbix graph showing real-time CO2 concentration data from Raspberry Pi &amp;#43; CO2-mini. Values drop when the room is ventilated" srcset="
/media/co2-mini/2f571cc59d7fa276e5cedf024f4227cf_hu277cd2d825a81d3f8ff9ed731bf6d1ea_117855_48a1d62b253931673888293d34fd422b.webp 400w,
/media/co2-mini/2f571cc59d7fa276e5cedf024f4227cf_hu277cd2d825a81d3f8ff9ed731bf6d1ea_117855_f0c054ecb0a4aa5844ee97e07d272a80.webp 760w,
/media/co2-mini/2f571cc59d7fa276e5cedf024f4227cf_hu277cd2d825a81d3f8ff9ed731bf6d1ea_117855_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://yre.jp/media/co2-mini/2f571cc59d7fa276e5cedf024f4227cf_hu277cd2d825a81d3f8ff9ed731bf6d1ea_117855_48a1d62b253931673888293d34fd422b.webp"
width="760"
height="165"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
CO2 concentration graph in Zabbix. Values drop at ventilation events
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;p>
&lt;figure id="figure-long-term-trend-in-winter-windows-tend-to-stay-closed-allowing-co2-to-build-up">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Zabbix graph showing weeks of CO2 concentration history. In winter, CO2 spikes are visible when ventilation is reduced" srcset="
/media/co2-mini/78ac805712a44e11300fa5ba4588254c_hu495835c02be0289c551a3002b397186b_290858_2cad7f32843dfd27d09684ba3e89f769.webp 400w,
/media/co2-mini/78ac805712a44e11300fa5ba4588254c_hu495835c02be0289c551a3002b397186b_290858_0a7ce34aa4d5b3be8bfd7327941efc86.webp 760w,
/media/co2-mini/78ac805712a44e11300fa5ba4588254c_hu495835c02be0289c551a3002b397186b_290858_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://yre.jp/media/co2-mini/78ac805712a44e11300fa5ba4588254c_hu495835c02be0289c551a3002b397186b_290858_2cad7f32843dfd27d09684ba3e89f769.webp"
width="760"
height="269"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
Long-term trend. In winter, windows tend to stay closed, allowing CO2 to build up
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;p>Since setting up the monitor, I ventilate much more frequently. Seeing the numbers makes it obvious exactly when ventilation is needed.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Role&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>CO2-mini&lt;/td>
&lt;td>Measures CO2 concentration and temperature via USB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Raspberry Pi&lt;/td>
&lt;td>Reads sensor data and writes it to shared memory&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Python (CO2Meter library)&lt;/td>
&lt;td>Communicates with CO2-mini and parses data&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Zabbix (agent + server)&lt;/td>
&lt;td>Collects data, graphs it, and stores long-term history&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The complete scripts are available on GitHub:&lt;/p>
&lt;p>&lt;a href="https://github.com/yukishita/co2-mini" target="_blank" rel="noopener">GitHub - yukishita/co2-mini: Program to log CUSTOM CO2-mini data with Zabbix&lt;/a>&lt;/p></description></item><item><title>Raspberry Pi 3 + Official 7-inch Touchscreen Photo Frame with Ambient Light Sensor Auto-Brightness (BH1750FVI + I2C + Python)</title><link>https://yre.jp/en/post/raspi-photoframe-with-sensor/</link><pubDate>Tue, 19 Jul 2022 00:00:00 +0000</pubDate><guid>https://yre.jp/en/post/raspi-photoframe-with-sensor/</guid><description>&lt;p>After building a cloud-connected photo frame with Raspberry Pi 4, I had a spare Raspberry Pi 3 and official 7-inch touchscreen sitting around. I turned them into another photo frame — and added a GY-30 BH1750FVI ambient light sensor to automatically adjust display brightness based on the room&amp;rsquo;s lighting.&lt;/p>
&lt;h2 id="hardware">Hardware&lt;/h2>
&lt;h3 id="raspberry-pi-3">Raspberry Pi 3&lt;/h3>
&lt;a href="https://www.amazon.co.jp/dp/B082QN6L1N?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/71wjyr2fsJL._AC_SL1200_.jpg" alt="Raspberry Pi 3 Model B Single-board Computer">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">Raspberry Pi 3 Model B Single-board Computer&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h3 id="official-7-inch-touchscreen">Official 7-inch Touchscreen&lt;/h3>
&lt;p>800×480px, 24-bit RGB color, 60 fps, up to 10-point multitouch.&lt;/p>
&lt;a href="https://www.amazon.co.jp/dp/B01LC7U4XW?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/6188oB1j-OL._AC_SL1024_.jpg" alt="Raspberry Pi Official 7-inch Touchscreen LCD">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">Raspberry Pi Official 7-inch Touchscreen LCD&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h3 id="case-for-the-official-touchscreen">Case for the Official Touchscreen&lt;/h3>
&lt;p>Designed to house both the Raspberry Pi 3 Model B and the 7-inch display.&lt;/p>
&lt;a href="https://www.amazon.co.jp/dp/B01N6786IE?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/41PUW&amp;#43;GgD0L._AC_.jpg" alt="Raspberry Pi &amp;amp; 7-inch LCD Touchscreen Case ABS (RS Components, Black)">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">Raspberry Pi &amp;amp; 7-inch LCD Touchscreen Case ABS (RS Components, Black)&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h2 id="connecting-the-display">Connecting the Display&lt;/h2>
&lt;p>Follow the &lt;a href="https://www.raspberrypi.com/documentation/accessories/display.html" target="_blank" rel="noopener">Raspberry Pi Touch Display official documentation&lt;/a> to assemble the display.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
When connecting, pay attention to the ribbon cable orientation. For the power wires, connect red to the 5V pin and black to the GND pin.
&lt;/div>
&lt;/div>
&lt;h2 id="display-rotation-setting">Display Rotation Setting&lt;/h2>
&lt;p>By default the display is rotated 180°. The fix depends on your OS version.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
&lt;p>The &lt;code>lcd_rotate=2&lt;/code> option below is for &lt;strong>Raspberry Pi OS Bullseye and earlier (legacy/FKMS mode)&lt;/strong>. On Bookworm (2023 and later), &lt;code>lcd_rotate&lt;/code> is not officially recommended — use the &lt;strong>Screen Configuration&lt;/strong> GUI tool instead.&lt;/p>
&lt;p>The config file path also differs by OS version:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Bullseye and earlier&lt;/strong>: &lt;code>/boot/config.txt&lt;/code>&lt;/li>
&lt;li>&lt;strong>Bookworm and later&lt;/strong>: &lt;code>/boot/firmware/config.txt&lt;/code>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;p>&lt;strong>For Bullseye and earlier (legacy/FKMS mode):&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo vi /boot/config.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Add the following line:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">lcd_rotate=2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="slideshow-program">Slideshow Program&lt;/h2>
&lt;p>The slideshow uses the Python script from the &lt;a href="https://github.com/yukishita/photoView4" target="_blank" rel="noopener">cloud-connected photo frame project (photoView4)&lt;/a>.&lt;/p>
&lt;h2 id="display-brightness-control">Display Brightness Control&lt;/h2>
&lt;p>Running the display at full brightness all the time is uncomfortable in a dark room. The official 7-inch display allows brightness control by writing a value (0–255) to a sysfs file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;100&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> sudo tee /sys/class/backlight/rpi_backlight/brightness
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="adding-an-ambient-light-sensor-bh1750fvi">Adding an Ambient Light Sensor (BH1750FVI)&lt;/h2>
&lt;p>Rather than setting brightness manually, I wanted it to adjust automatically based on ambient light.&lt;/p>
&lt;h3 id="parts">Parts&lt;/h3>
&lt;a href="https://www.amazon.co.jp/gp/product/B06XHKTL58?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/61bi5cGPpFL._SL1100_.jpg" alt="WINGONEER GY-30 BH1750FVI Digital Light Intensity Sensor Module I2C for Raspberry Pi">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">WINGONEER GY-30 BH1750FVI Digital Light Intensity Sensor Module I2C for Raspberry Pi&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;a href="https://www.amazon.co.jp/gp/product/B06Y48V9DL?tag=snowunderco07-22" target="_blank" rel="noopener nofollow sponsored" class="amazon-card">
&lt;div class="amazon-card-image">
&lt;img src="https://m.media-amazon.com/images/I/81yjq1pkiGL._SL1500_.jpg" alt="ELEGOO 120pcs Dupont Jumper Wires Male-Female / Male-Male / Female-Female Set">
&lt;/div>
&lt;div class="amazon-card-info">
&lt;div class="amazon-card-title">ELEGOO 120pcs Dupont Jumper Wires Male-Female / Male-Male / Female-Female Set&lt;/div>
&lt;div class="amazon-card-meta">
&lt;span class="amazon-btn">Amazonで見る&lt;/span>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;h2 id="connecting-to-raspberry-pi-via-i2c">Connecting to Raspberry Pi via I2C&lt;/h2>
&lt;p>The BH1750FVI communicates over I2C. Refer to the &lt;a href="https://www.raspberrypi.com/documentation/computers/os.html#gpio-and-the-40-pin-header" target="_blank" rel="noopener">Raspberry Pi GPIO pin layout&lt;/a> for wiring.&lt;/p>
&lt;p>
&lt;figure id="figure-raspberry-pi-gpio-pinout-source-raspberry-pi-official-documentation">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Raspberry Pi 40-pin GPIO header pinout diagram showing the I2C pins: SDA on GPIO2 (Pin 3) and SCL on GPIO3 (Pin 5)" srcset="
/media/raspi-photoframe-with-sensor/GPIO-Pinout-Diagram-2_hua49859c3abb8d59effc7f48992911ff2_206936_43d9d80c817fbd45d29cc36e3b5ca73e.webp 400w,
/media/raspi-photoframe-with-sensor/GPIO-Pinout-Diagram-2_hua49859c3abb8d59effc7f48992911ff2_206936_d6d457c064379b94134319d1e0002c6f.webp 760w,
/media/raspi-photoframe-with-sensor/GPIO-Pinout-Diagram-2_hua49859c3abb8d59effc7f48992911ff2_206936_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://yre.jp/media/raspi-photoframe-with-sensor/GPIO-Pinout-Diagram-2_hua49859c3abb8d59effc7f48992911ff2_206936_43d9d80c817fbd45d29cc36e3b5ca73e.webp"
width="760"
height="436"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
Raspberry Pi GPIO pinout (source: Raspberry Pi official documentation)
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h3 id="wiring-table">Wiring Table&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Raspberry Pi&lt;/th>
&lt;th>GPIO Pin&lt;/th>
&lt;th>BH1750FVI&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>GPIO2 (SDA)&lt;/td>
&lt;td>3&lt;/td>
&lt;td>SDA&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPIO3 (SCL)&lt;/td>
&lt;td>5&lt;/td>
&lt;td>SCL&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5V Power&lt;/td>
&lt;td>2&lt;/td>
&lt;td>VCC&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Ground&lt;/td>
&lt;td>14&lt;/td>
&lt;td>ADO&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Ground&lt;/td>
&lt;td>20&lt;/td>
&lt;td>GND&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>
&lt;figure id="figure-bh1750fvi-wired-to-raspberry-pi-3-via-i2c">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="GY-30 BH1750FVI light sensor connected to Raspberry Pi 3 via jumper wires using I2C pins" srcset="
/media/raspi-photoframe-with-sensor/ILCE-7M4-_DSC1410_hu98206837892c374cd2028613e5b10c88_691631_c96c52f9123cb52726bbc85d7347db81.webp 400w,
/media/raspi-photoframe-with-sensor/ILCE-7M4-_DSC1410_hu98206837892c374cd2028613e5b10c88_691631_7b5ac30237dcef389547e52e6e3b9297.webp 760w,
/media/raspi-photoframe-with-sensor/ILCE-7M4-_DSC1410_hu98206837892c374cd2028613e5b10c88_691631_1200x1200_fit_q75_h2_lanczos.webp 1200w"
src="https://yre.jp/media/raspi-photoframe-with-sensor/ILCE-7M4-_DSC1410_hu98206837892c374cd2028613e5b10c88_691631_c96c52f9123cb52726bbc85d7347db81.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
BH1750FVI wired to Raspberry Pi 3 via I2C
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h2 id="verifying-the-sensor-python">Verifying the Sensor (Python)&lt;/h2>
&lt;p>After wiring, confirm data is coming through. Based on a &lt;a href="https://github.com/kujiraitakahiro/RaspberryPi/blob/master/bh1750fvi.py" target="_blank" rel="noopener">reference implementation on GitHub&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="ch">#!/usr/bin/python3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">smbus&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Bus&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">smbus&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SMBus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Addr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x23&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LxRead&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Bus&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read_i2c_block_data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Addr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mh">0x11&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Illuminance: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LxRead&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34; lx&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LxRead2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Bus&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read_i2c_block_data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Addr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mh">0x10&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Brightness: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">LxRead2&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">256&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">LxRead2&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mf">1.2&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Running it confirms the sensor is working:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">root@raspi3-photo:~# python br.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Illuminance: 1650 lx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Brightness: 990.8333333333334
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="auto-brightness-control-program">Auto-brightness Control Program&lt;/h2>
&lt;p>The final program adjusts backlight brightness smoothly based on ambient light readings:&lt;/p>
&lt;ul>
&lt;li>Target brightness levels are defined per illuminance range&lt;/li>
&lt;li>The display brightness is incremented or decremented one step at a time toward the target&lt;/li>
&lt;li>The I2C sensor is polled every 300ms — polling faster than this results in incorrect readings with SMBus&lt;/li>
&lt;/ul>
&lt;h2 id="sensor-placement">Sensor Placement&lt;/h2>
&lt;p>The sensor is mounted on the protruding back section of the case, where it receives ambient light from the room.&lt;/p>
&lt;p>
&lt;figure id="figure-sensor-mounted-on-the-case-back-where-it-can-see-ambient-light">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="GY-30 BH1750FVI ambient light sensor mounted on the back protrusion of the Raspberry Pi 7-inch touchscreen case" srcset="
/media/raspi-photoframe-with-sensor/ILCE-7M4-_DSC1407_hu98206837892c374cd2028613e5b10c88_594389_c8e1166602273b79f64bef721cf2db83.webp 400w,
/media/raspi-photoframe-with-sensor/ILCE-7M4-_DSC1407_hu98206837892c374cd2028613e5b10c88_594389_62c0ff78d1802062e2b677dae092cc5d.webp 760w,
/media/raspi-photoframe-with-sensor/ILCE-7M4-_DSC1407_hu98206837892c374cd2028613e5b10c88_594389_1200x1200_fit_q75_h2_lanczos.webp 1200w"
src="https://yre.jp/media/raspi-photoframe-with-sensor/ILCE-7M4-_DSC1407_hu98206837892c374cd2028613e5b10c88_594389_c8e1166602273b79f64bef721cf2db83.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
Sensor mounted on the case back where it can see ambient light
&lt;/figcaption>&lt;/figure>
&lt;/p>
&lt;h2 id="source-code">Source Code&lt;/h2>
&lt;p>The complete program (object-oriented version) is on GitHub:&lt;/p>
&lt;p>&lt;a href="https://github.com/yukishita/lcdBrightness2" target="_blank" rel="noopener">GitHub - yukishita/lcdBrightness2: Auto-brightness control for Raspberry Pi official display using BH1750FVI light sensor&lt;/a>&lt;/p>
&lt;h2 id="demonstration">Demonstration&lt;/h2>
&lt;p>The brightness adjusts smoothly as ambient light changes:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/R6XGl3KGRYA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
>&lt;/iframe>
&lt;/div>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Role&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Raspberry Pi 3&lt;/td>
&lt;td>Main computer&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Official 7-inch Touchscreen&lt;/td>
&lt;td>Photo frame display (800×480px)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GY-30 BH1750FVI&lt;/td>
&lt;td>I2C ambient light sensor&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Python (smbus)&lt;/td>
&lt;td>I2C communication and brightness control logic&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Adding the light sensor makes the photo frame genuinely pleasant to use — bright during the day, dimmed at night, automatically.&lt;/p></description></item><item><title>How to Install Django on Mac with Homebrew and Run a Local Development Server</title><link>https://yre.jp/en/post/jango_mac/</link><pubDate>Fri, 15 Apr 2022 00:00:00 +0000</pubDate><guid>https://yre.jp/en/post/jango_mac/</guid><description>&lt;p>This guide walks through installing Django on macOS step by step, from installing Python via Homebrew through to launching a local development server. Written for beginners.&lt;/p>
&lt;h2 id="test-environment">Test Environment&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Item&lt;/th>
&lt;th>Version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Machine&lt;/td>
&lt;td>MacBook Pro (Apple Silicon)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OS&lt;/td>
&lt;td>macOS Monterey 12.3.1&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="what-is-django">What Is Django?&lt;/h2>
&lt;p>&lt;a href="https://www.djangoproject.com/" target="_blank" rel="noopener">Django&lt;/a> is an open-source web framework written in Python. It comes with built-in admin interfaces, authentication, and an ORM (database abstraction layer), making it possible to build web applications efficiently without reinventing the wheel. Notable large-scale users include Instagram and Disqus.&lt;/p>
&lt;p>The goal of this article is to get Django installed and running locally.&lt;/p>
&lt;h2 id="step-1-install-homebrew">Step 1: Install Homebrew&lt;/h2>
&lt;p>Django runs on Python, so the first step is getting Python onto the Mac. There are several ways to do this; using &lt;a href="https://brew.sh/" target="_blank" rel="noopener">Homebrew&lt;/a> is the most practical because it also manages updates and other packages.&lt;/p>
&lt;p>Paste the following into Terminal and press Enter:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Enter your administrator password when prompted. When you see &lt;code>Installation successful!&lt;/code>, Homebrew is ready.&lt;/p>
&lt;h2 id="step-2-install-python">Step 2: Install Python&lt;/h2>
&lt;p>With Homebrew installed, install Python:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">brew install python3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Confirm the installation:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python3 --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You should see output like &lt;code>Python 3.x.x&lt;/code>.&lt;/p>
&lt;h2 id="step-3-set-the-path-for-homebrew-python">Step 3: Set the PATH for Homebrew Python&lt;/h2>
&lt;p>Homebrew installs Python under &lt;code>/opt/homebrew/bin/&lt;/code>. To make sure this version takes priority over the system Python at &lt;code>/usr/bin/python3&lt;/code>, you need to set your PATH.&lt;/p>
&lt;p>Check which Python is currently active:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">which python3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the output is &lt;code>/usr/bin/python3&lt;/code>, the PATH needs to be configured. Add the Homebrew path to &lt;code>~/.zprofile&lt;/code> (this file is sourced automatically at login on macOS Monterey and later with zsh):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=/opt/homebrew/bin/:$PATH&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zprofile
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Restart Terminal, then verify:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">which python3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Expected: /opt/homebrew/bin/python3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-4-install-django">Step 4: Install Django&lt;/h2>
&lt;h3 id="create-a-working-directory">Create a Working Directory&lt;/h3>
&lt;p>Create a project folder and move into it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mkdir myproject &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> myproject
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="set-up-a-python-virtual-environment">Set Up a Python Virtual Environment&lt;/h3>
&lt;p>A &lt;strong>virtual environment&lt;/strong> (venv) creates an isolated Python environment per project, allowing different projects to use different package versions without conflicts. This is standard practice for Python development.&lt;/p>
&lt;p>Create and activate a virtual environment named &lt;code>django&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python3 -m venv django
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> ./django/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When the virtual environment is active, &lt;code>(django)&lt;/code> appears at the start of your prompt.&lt;/p>
&lt;h3 id="install-django-with-pip">Install Django with pip&lt;/h3>
&lt;p>With the virtual environment active, install Django:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pip3 install django
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When you see &lt;code>Successfully installed django-x.x.x&lt;/code>, the installation is complete.&lt;/p>
&lt;h2 id="step-5-create-a-project-and-start-the-server">Step 5: Create a Project and Start the Server&lt;/h2>
&lt;h3 id="create-the-django-project">Create the Django Project&lt;/h3>
&lt;p>Use &lt;code>django-admin startproject&lt;/code> to generate a project scaffold:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">django-admin startproject firstproject
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> firstproject
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="start-the-local-development-server">Start the Local Development Server&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python3 manage.py runserver
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A successful start looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Watching for file changes with StatReloader
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Performing system checks...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">System check identified no issues (0 silenced).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Run &amp;#39;python manage.py migrate&amp;#39; to apply them.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Django version 4.0.4, using settings &amp;#39;firstproject.settings&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Starting development server at http://127.0.0.1:8000/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Quit the server with CONTROL-C.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>The &lt;code>unapplied migration(s)&lt;/code> warning means Django&amp;rsquo;s internal database (used for the admin panel and authentication) has not been initialized yet. You can safely ignore it for a quick test. Run &lt;code>python3 manage.py migrate&lt;/code> later to clear it.&lt;/p>
&lt;/blockquote>
&lt;p>Open &lt;code>http://127.0.0.1:8000/&lt;/code> in a browser. If you see the Django welcome page, everything is working.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Step&lt;/th>
&lt;th>Command&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Install Homebrew&lt;/td>
&lt;td>&lt;code>/bin/bash -c &amp;quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Install Python&lt;/td>
&lt;td>&lt;code>brew install python3&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Configure PATH&lt;/td>
&lt;td>&lt;code>echo 'export PATH=/opt/homebrew/bin/:$PATH' &amp;gt;&amp;gt; ~/.zprofile&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Create virtual environment&lt;/td>
&lt;td>&lt;code>python3 -m venv django &amp;amp;&amp;amp; source ./django/bin/activate&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Install Django&lt;/td>
&lt;td>&lt;code>pip3 install django&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Create project&lt;/td>
&lt;td>&lt;code>django-admin startproject firstproject&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Start server&lt;/td>
&lt;td>&lt;code>python3 manage.py runserver&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>From here, the &lt;a href="https://docs.djangoproject.com/en/stable/intro/tutorial01/" target="_blank" rel="noopener">official Django tutorial&lt;/a> is the best next step — it covers views, models, and templates in a structured way.&lt;/p></description></item></channel></rss>