<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Thiago Pinto</title>
    <description>The latest articles on Forem by Thiago Pinto (@thspinto).</description>
    <link>https://forem.com/thspinto</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F29733%2Fbffb886a-8256-4b30-86c3-a3f88f3f17b9.jpg</url>
      <title>Forem: Thiago Pinto</title>
      <link>https://forem.com/thspinto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thspinto"/>
    <language>en</language>
    <item>
      <title>Raspberry Pi K3s Cluster</title>
      <dc:creator>Thiago Pinto</dc:creator>
      <pubDate>Sat, 01 Jan 2022 22:46:36 +0000</pubDate>
      <link>https://forem.com/thspinto/raspberry-pi-k3s-cluster-5db7</link>
      <guid>https://forem.com/thspinto/raspberry-pi-k3s-cluster-5db7</guid>
      <description>&lt;p&gt;The last time I played with the Raspberry Pi, I used Ansible to configure docker and some services. However, I still wasn't happy with the solution: my RPi 2 was slow to run the Ansible configuration, I couldn't run much on it, and the setup couldn't recover from any errors without manual intervention.&lt;/p&gt;

&lt;p&gt;Now I've upgraded my hardware and certainly over complicated the software for a small home server, but now I got metrics =).&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware
&lt;/h2&gt;

&lt;p&gt;Since I use Kubernetes daily, I decided to upgrade my home setup to a k3s cluster. I've started with three nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RPi 2 model B&lt;/li&gt;
&lt;li&gt;RPi 3&lt;/li&gt;
&lt;li&gt;RPi 4 4GB ram&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I added "so much" hardware because my objective is to monitor the cluster using Prometheus and Grafana.&lt;/p&gt;

&lt;p&gt;The oldest node, &lt;code&gt;RPi 2 model B&lt;/code&gt;, got decommissioned in the process. It wasn't consistent and had issues joining the cluster. So I replaced it with an RPi4 to run the cluster with HA.&lt;/p&gt;

&lt;h2&gt;
  
  
  OS bootstrapping
&lt;/h2&gt;

&lt;p&gt;The first problem I didn't want to deal with manually was configuring the ssh on the RPis. In the previous process, I had one playbook to configure the ssh and user on Raspberian and another to configure everything else. This time, though, the node will boot with password login disabled and with my public keys automatically added to the host. To achieve this, I used &lt;a href="https://github.com/nmcclain/raspberian-firstboot"&gt;Raspberian Firstboot&lt;/a&gt; with a &lt;a href="https://github.com/thspinto/rpi-cluster/tree/main/os_bootstrap"&gt;custom script&lt;/a&gt; to set up the ssh configuration and hostname.&lt;/p&gt;

&lt;p&gt;The second step is putting the Rapberian Firstboot image on the SD card with the &lt;code&gt;firstboot.sh&lt;/code&gt; and &lt;code&gt;bootstrap.env&lt;/code&gt; in the &lt;code&gt;boot&lt;/code&gt; dir. After the node boots up, we can proceed directly to the Ansible configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ansible
&lt;/h2&gt;

&lt;p&gt;Ansible is responsible for the initial setup of the nodes. After that, the playbook optimizes the RPi to run headless, configures k3s, and sets my local kubectl context with the new cluster. Since ArgoCD manages all k3s resources, Ansible also installs it in the cluster. Argo manages itself from here on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YzDkh1rY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.pinimg.com/originals/b0/df/41/b0df417604a64d191b9b6c407d591503.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YzDkh1rY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.pinimg.com/originals/b0/df/41/b0df417604a64d191b9b6c407d591503.jpg" alt="Meme of spiderman pointing to another spiderman" width="880" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ArgoCD
&lt;/h2&gt;

&lt;p&gt;Why ArgoCD for such a small setup?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uEZfni0G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encrypted-tbn0.gstatic.com/images%3Fq%3Dtbn:ANd9GcR9es8OrAdRShum4eHs9WViIt72A05MZvQ30g%26usqp%3DCAU" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uEZfni0G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://encrypted-tbn0.gstatic.com/images%3Fq%3Dtbn:ANd9GcR9es8OrAdRShum4eHs9WViIt72A05MZvQ30g%26usqp%3DCAU" alt='Meme - Strong dog: "computers then: I sent people to the moon with just 4kb ram. Crying normal dog: "computers now: help me please 4Gb ram is not enough for me.' width="256" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the previous setup, I had to expose the ssh service on the internet to play with the cluster outside the LAN. I also had to run Ansible every time I modified the services. Because my RPi was slow, it was boring to wait several minutes for simple modifications. With GitOps on ArgoCD, I don't need direct access to the nodes to configure the services.&lt;/p&gt;

&lt;p&gt;There was a risk that I would exhaust the cluster resources with only the maintenance services, but there are still around 60% free resources after adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ArgoCD&lt;/li&gt;
&lt;li&gt;Prometheus&lt;/li&gt;
&lt;li&gt;Grafana&lt;/li&gt;
&lt;li&gt;External DNS&lt;/li&gt;
&lt;li&gt;Cert-Manager&lt;/li&gt;
&lt;li&gt;Kong&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ArgoCD project and application bootstrapping
&lt;/h3&gt;

&lt;p&gt;To bootstrap the ArgoCD configuration, I used the &lt;a href="https://argoproj.github.io/argo-cd/operator-manual/cluster-bootstrapping/"&gt;app of apps&lt;/a&gt; pattern. This configuration is Ansible's responsibility: it applies to k3s &lt;a href="https://github.com/thspinto/rpi-cluster/tree/main/argocd/bootstrap"&gt;the initial set up&lt;/a&gt;. Service and infrastructure applications are separated to give fewer permissions to the non-infrastructure service configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets
&lt;/h2&gt;

&lt;p&gt;My first approach to keeping secrets versioned was using &lt;a href="https://github.com/bitnami-labs/sealed-secrets"&gt;Sealed Secrets&lt;/a&gt;. However, I gave up on the sealed secrets because I rebuilt the cluster frequently and always lost the encryption keys since I didn't care to make backups. I found it a lot easier to keep an encrypted &lt;code&gt;secrets.yaml&lt;/code&gt; file in the repository. Every time I spin up a new cluster in this workflow, I have to decrypt the file and apply it to the new cluster.&lt;br&gt;
Although it is encrypted, I don't recommend keeping the file in a public Github repository. I'm doing this to avoid losing the secrets while I work out a better flow for personal projects.&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>raspberrypi</category>
      <category>k3s</category>
      <category>gitops</category>
    </item>
    <item>
      <title>My Raspberry Pi Server Setup</title>
      <dc:creator>Thiago Pinto</dc:creator>
      <pubDate>Sat, 01 Jan 2022 15:05:26 +0000</pubDate>
      <link>https://forem.com/thspinto/my-raspberry-pi-cluster-setup-523o</link>
      <guid>https://forem.com/thspinto/my-raspberry-pi-cluster-setup-523o</guid>
      <description>&lt;p&gt;I've had a raspberry pi mainly sitting in a drawer for a while now. Haphazardly I dust off the SD card to set up the box with dynamic DNS and play with it as a personal server. It always went back to the drawer when I broke the SO with some crazy experiment.&lt;/p&gt;

&lt;p&gt;As an SRE, I spend most of my time thinking about what flows are more burdensome to the team and how to automate the process to gain more time to do other important stuff. But with my RBP it was different. I'd never versioned the scripts I executed and never thought about creating proper automation for the box.&lt;/p&gt;

&lt;p&gt;To avoid giving up on the Pi again, I started my journey differently this time. Every configuration running on the Pi must be replayable to fix it just by burning a new SO in the SD card and running the automation to set it up. &lt;a href="https://github.com/thspinto/raspberry-config"&gt;This&lt;/a&gt; is what I have up to now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provisioning Raspbian
&lt;/h2&gt;

&lt;p&gt;I started with the &lt;a href="https://www.raspberrypi.org/downloads/raspbian"&gt;Rasbian Buster Lite image&lt;/a&gt; and burned it on the SD card. Assure you're pointing to the correct disk before running the commands below!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unzip -p 2020-02-13-raspbian-buster-lite.zip | sudo dd of=/dev/disk2 bs=4m
touch /Volumes/boot/ssh # to enable ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ansible automation
&lt;/h2&gt;

&lt;p&gt;Ansible &lt;a href="https://docs.ansible.com/ansible/latest/index.html"&gt;Ansible&lt;/a&gt; is an excellent tool for automating systems configurations. This setup starts with the default user and password.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;initialize&lt;/code&gt; playbook adds my user and public keys to the RBP and disables password ssh logins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook initialize.yaml -i inventories/thspinto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;code&gt;inventories/thspinto/all.yaml&lt;/code&gt; is unreadable. That's because its encrypted with &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/vault.html#file-level-encryption"&gt;Ansible Vault&lt;/a&gt;. I don't recommend versioning secrets in public repositories for most use cases, even if they are encrypted. In my case, I versioned it because I'll certainly lose the file otherwise. Also the only sensitive information I have is a token to alter my DNS registry. Since there is virtually no traffic in my domain, it's no big deal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic setup
&lt;/h3&gt;

&lt;p&gt;In the second phase, I did a little hardening and added docker. This is done by the setup playbook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-galaxy &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.yml
ansible-playbook &lt;span class="nt"&gt;--vault-password-file&lt;/span&gt; /path/to/my/vault-password-file setup.yaml &lt;span class="nt"&gt;-i&lt;/span&gt; inventories/thspinto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Services
&lt;/h3&gt;

&lt;p&gt;The last playbook spins up the services I want to run using docker-compose. I separated it from the rest because it is frequently changed and deployed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-playbook &lt;span class="nt"&gt;--vault-password-file&lt;/span&gt; /path/to/my/vault-password-file services.yaml &lt;span class="nt"&gt;-i&lt;/span&gt; inventories/thspinto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently I run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/oznu/docker-cloudflare-ddns"&gt;Cloudflare DDNS&lt;/a&gt; to register the Pi's id the my domain.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.traefik.io"&gt;Traefik 2.1&lt;/a&gt; as proxy with docker service discovery&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/thomseddon/traefik-forward-auth"&gt;ForwardAuth&lt;/a&gt; to have OpenConnectID login using &lt;a href="https://auth0.com/"&gt;Auth0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Pi
&lt;/h2&gt;

&lt;p&gt;Everything runs fine on model B with one 700MHz core and 512MB of ram. Checkout the &lt;a href="https://github.com/thspinto/raspberry-config"&gt;raspberry-config repo&lt;/a&gt; for more details and updates. However I can't really do much with this hardware. That's why as next steps I'm considering buying more RBPs and setting up a clustered environment with &lt;a href="https://k3s.io/"&gt;k3s&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>raspberrypi</category>
      <category>auth0</category>
      <category>traefik</category>
    </item>
    <item>
      <title>ESP8266 Adventures</title>
      <dc:creator>Thiago Pinto</dc:creator>
      <pubDate>Fri, 31 Dec 2021 23:02:42 +0000</pubDate>
      <link>https://forem.com/thspinto/esp8266-adventures-12g4</link>
      <guid>https://forem.com/thspinto/esp8266-adventures-12g4</guid>
      <description>&lt;p&gt;How can my knowledge of technology help in global warming impacted future? My main area of study is in Computer Science and I've mostly had contact with intangible stuff. If, as a consequence of climate change, we enter a new era of scarcity, how can this intangible knowledge help?&lt;/p&gt;

&lt;p&gt;With this in mind and with curiosity about IoT. Our most basic need is food. Vegetal food needs light, healthy soil and the right amount of water to grow. The most basic gadget I could build that can help food to grow and to learn with my experimentation is a weather station.&lt;/p&gt;

&lt;p&gt;All that introduction is just to say that my goal is collecting the temperature, air humidity, soil humidity and sending it to my cluster for processing. Since we are in the future, no cables are allowed therefore of wireless connection is required. So I went online and bought the most simple-looking and cheap module I could find to couple it to an Arduino I've already owned. I got the ESP-12e and that is where a whole new world opened beneath my feet and thing got a little more complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming the ESP8266
&lt;/h2&gt;

&lt;p&gt;What I didn't know is that the ESP8266 is not just a Wifi module, it can be programmed and even run a web server inside. Of course there was a community leveraging this for IOT, and they led me to &lt;a href="https://docs.openmqttgateway.com/" rel="noopener noreferrer"&gt;OpenMQTTGateway&lt;/a&gt;. This project collects various signals and sensor from IOT devices and sends it to a queue to be used by HomeAutomation services such as &lt;a href="https://www.home-assistant.io/" rel="noopener noreferrer"&gt;Home Assistant&lt;/a&gt;. Given my astonishment of the capacity of this module of course I didn't have any tools that would make my life easier in programming it.&lt;/p&gt;

&lt;p&gt;Since I had an Arduino I thought it would be easy to use it to program the ESP. My first blocker is that I saw on a tutorial that I needed an external 3,3V power source because Arduino's 3,3V output didn't have enough current for the module. I found 3 used 1,5V batteries that connected in series gave me 3,6V. The datasheet says that 3,6V is within the operational voltage, so achievement unlocked for recycling used batteries. (I do have to notice that I found out later that used batteries are really bad at keeping the voltage I measured without any usage.)&lt;/p&gt;

&lt;p&gt;Now I just had to flash the OpenMQTTGateway project into the module. Well, to put it into flash mode you have to reboot the module with the GPIO0. I found this nice schematics in instructables:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcontent.instructables.com%2FORIG%2FF3W%2FON46%2FIYKG2WG9%2FF3WON46IYKG2WG9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcontent.instructables.com%2FORIG%2FF3W%2FON46%2FIYKG2WG9%2FF3WON46IYKG2WG9.png" alt="Image showing how to connect Arduino to flash an ESP8266 module"&gt;&lt;/a&gt;&lt;a href="https://www.instructables.com/ESP-12E-ESP8266-With-Arduino-Uno-Getting-Connected/" rel="noopener noreferrer"&gt;https://www.instructables.com/ESP-12E-ESP8266-With-Arduino-Uno-Getting-Connected/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My execution of it was a disaster because I bought the wrong pin bar (the ESP-12E has a smaller distance between the roles in comparison to the default protoboard) so I just attached the wires directly to the module. Of course that doing so made it very unstable and I couldn't breath near it without triggering a reset. But it I got it be be stable enough to flash it. The next issue I had was not flowing correctly the instructions: I was trying to use Arduino IDE to program the module and it wasn't able to connect. First of all it is very important to connect the ground to reset in the Arduino board. Apparently when you open the usb-to-serial converter Arduino automatically resets and keeping reset pin to ground avoids that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pin info
&lt;/h2&gt;

&lt;p&gt;⚠️ To flash software to the module no other sensors can be attached.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcircuits4you.com%2Fwp-content%2Fuploads%2F2016%2F12%2FESP-12-PinOut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcircuits4you.com%2Fwp-content%2Fuploads%2F2016%2F12%2FESP-12-PinOut.png" alt="Image showing where the pins are located in the ESP 8266."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ended buying a fiberglass protoboard to weld everything together and make the setup more stable (I admit I do have practice welding to get more motor coordination). Finally, I also added a USB cable and a tension regulator to 3,3V for a constant power supply.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnsi4tyna3ymfbp4n0mp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnsi4tyna3ymfbp4n0mp.jpg" alt="Image of the welded final setup of my ESP with a tension regulator and the DHT sensor."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might ask me why didn't you just buy the &lt;a href="https://www.nodemcu.com/" rel="noopener noreferrer"&gt;nodemcu&lt;/a&gt;? Well, I basically didn't know of its existence and I never thought that "wifi module" could do so much. It was fun to learn, but it is certainly better to buy the nodemcu because it is pretty cheap (even in Brazil), and flashing it and connecting stuff to it is a lot simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenMQTTGateway Configuration
&lt;/h2&gt;

&lt;p&gt;To manually configure the network and MQTT I added the following to &lt;a href="https://github.com/1technophile/OpenMQTTGateway/blob/development/main/User_config.h" rel="noopener noreferrer"&gt;User_config.h&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define ESPWifiManualSetup true
#define MQTT_SERVER "your.server"
#define MQTT_USER "user"
#define MQTT_PASS "pass"
#define wifi_ssid "ssid"
#define wifi_password "pass"
#define SECURE_CONNECTION
#define MQTT_PORT "9443"
&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;certificate&lt;/span&gt; &lt;span class="n"&gt;CERT_ATTRIBUTE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="s"&gt;"EOF("&lt;/span&gt;
&lt;span class="o"&gt;-----&lt;/span&gt;&lt;span class="n"&gt;BEGIN&lt;/span&gt; &lt;span class="n"&gt;CERTIFICATE&lt;/span&gt;&lt;span class="o"&gt;-----&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;// your root certificate: see here for let`s encypt https://letsencrypt.org/certificates/&lt;/span&gt;
&lt;span class="o"&gt;-----&lt;/span&gt;&lt;span class="n"&gt;END&lt;/span&gt; &lt;span class="n"&gt;CERTIFICATE&lt;/span&gt;&lt;span class="o"&gt;-----&lt;/span&gt;
&lt;span class="s"&gt;")EOF"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Collecting the metrics
&lt;/h2&gt;

&lt;p&gt;To collect and view the sensor data I'm using Mosquitto, Prometheus, and Grafana. For Prometheus to be able to collect the data I'm running &lt;a href="https://github.com/kpetremann/mqtt-exporter" rel="noopener noreferrer"&gt;an exporter&lt;/a&gt; that reads the data from the queue and provides an HTTP endpoint in the format Prometheus expects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75om1gdfgmjcv1nc3pw3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75om1gdfgmjcv1nc3pw3.png" alt="Diagram showing the flow where the data is collected, sent to MQTT. Moquitto exporter reads the queue and exposes the data to Prometheus and Grafana queries Prometheus and renders the dashboard."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq06f6vg0z34by2syj75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq06f6vg0z34by2syj75.png" alt="Dashboard showing the metrics temperature and humidity, collected by the sensor connected to the ESP8266."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.openmqttgateway.com/" rel="noopener noreferrer"&gt;🔗 OpenMQTTGateway&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf" rel="noopener noreferrer"&gt;🔗 AT instruction set&lt;/a&gt;&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>esp8266</category>
      <category>grafana</category>
      <category>mqtt</category>
    </item>
  </channel>
</rss>
