<?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>DIY Electronics on Making Clean Tech</title><link>https://nunrg.eu/tags/diy-electronics/</link><description>Recent content in DIY Electronics on Making Clean Tech</description><generator>Hugo -- 0.136.5</generator><language>en-us</language><copyright>2024 Franci Kopač</copyright><lastBuildDate>Fri, 16 May 2025 09:36:50 +0200</lastBuildDate><atom:link href="https://nunrg.eu/tags/diy-electronics/index.xml" rel="self" type="application/rss+xml"/><item><title>Measuring Air Velocity and Flow as a Maker — Introduction to Pitot Tubes</title><link>https://nunrg.eu/posts/pitot-tubes/</link><pubDate>Fri, 16 May 2025 09:36:50 +0200</pubDate><guid>https://nunrg.eu/posts/pitot-tubes/</guid><description>An introduction to Pitot tubes for makers: how to measure air velocity and flow, the physics involved and how to build a DIY sensor.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/measuring-air-velocity-and-flow-as-a-maker-introduction-to-pitot-tubes-644efad44709">reading this article on Medium</a> or you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
<h2 id="introduction">Introduction</h2>
<p>Being a maker means having to measure things all the time. Usually, the things we measure are relatively straightforward — temperatures, voltages, currents, humidities. Usually, all you need is an off-the shelf sensor and an analog pin or an I2C bus on your microcontroller, a couple of lines of code, maybe a tutorial, and you are done.</p>
<p>But air velocity is different; there are few off-the shelf sensors that a maker would readily use:</p>
<ul>
<li>
<p>Suppose you wanted to measure the airspeed of your model airplane? Or maybe you wanted to build a simple protection controller to automatically close your motorized awning when it gets windy? You could maybe use a 3D-printed propeller-type sensor, but these are fragile (trust me, I tried a lot), and moving parts are something to be avoided in any project.</p>
</li>
<li>
<p>Or maybe you are building something to do with ventilation and you need to measure air flow. While you might find an off-the-shelf sensor for air flow used for air-conditioning, I found these to be quite expensive and not particularly flexible.</p>
</li>
</ul>
<p>Luckily, there is a maker-friendly answer to all these—<a href="https://en.wikipedia.org/wiki/Pitot_tube">Pitot tubes</a>. If you never heard of them, I&rsquo;m pretty sure you at least saw them before: it&rsquo;s the pointy thing on the nose of most airplanes.</p>
<figure>
    <img src="/images/pitot/f16-pitot.jpeg" alt="A Pitot tube on the nose of a fighter jet">
    <figcaption>A Pitot tube on the nose of an F16, found on r/WarplanePorn/</figcaption>
  </figure>
  
<h2 id="how-does-it-work">How Does It Work?</h2>
<p>To put it simply, a Pitot tube (named after the inventor) is a tube that we insert into the flow of air with the opening pointing into the flow. The air pushes into the opening, producing a measurable pressure change. If we measure this change and know the density and physical properties of the gas, we can calculate the velocity of the flow of air.
You can find a nice graphical explanation <a href="https://instrumentationtools.com/pitot-tube-working-principle/">here</a>.</p>
<h2 id="how-do-we-measure-the-pressure-difference">How Do We Measure the Pressure Difference?</h2>
<p>In conditions you are likely to encounter as a maker (roughly room temperature, roughly sea-level pressure, velocities not exceeding 100m/s), Pitot tubes produce rather small pressure differences (tens of millibars/hectopascals at most). Sensors for such low differential pressures with any kind of resolution/accuracy were rather expensive until the advent of <a href="https://en.wikipedia.org/wiki/MEMS">MEMS</a>-sensors, but the situation has changed dramatically since then.</p>
<p>You can now buy a <a href="https://sensirion.com/products/catalog?categories=differential-pressure">Sensirion SDP differential pressure sensor</a> with a pressure range of 125 or 500 Pa, I2C bus connection, some limited intelligence like averaging, and a couple of milliamps of power consumption for ~25€. This sensor, a couple of thin connection hoses to connect to the Pitot tube, and a very basic microcontroller are all you need.</p>
<h2 id="how-to-build-a-practical-pitot-tube">How to Build a Practical Pitot Tube</h2>
<p>If you clicked the explainer and the Wikipedia article above, you already know a Pitot tube is basically a stick with two holes or ports, one facing the airflow and one facing to the side. These ports are then connected to the differential pressure sensor by channels of some sort. You could simply point the two hoses coming from your sensor in the right directions, and it would already sort of work.</p>
<p>Of course, if you want any kind of accuracy, there are some things to consider:</p>
<ul>
<li>Positioning matters — you need to make sure the rest of your setup is not influencing the air velocity at the point of measurement. Imagine a car with a Pitot tube on the roof — the airflow along the hood, windshield, and roof will be quite different from the free airflow one meter higher or maybe one meter in front of the car. This is why jets have these tubes sticking forward on the nose — it&rsquo;s the only place where the airflow hasn&rsquo;t changed much yet.</li>
<li>Direction matters — if the tube is not pointed straight into the airflow, you will have cosine losses, but you will also be adding pressure on the reference (side) port. Both will play havoc with your accuracy.</li>
<li>Temperature matters — the calculation of air velocity depends on gas density and temperature. Gas density is again dependent on temperature and absolute pressure. So, depending on your application, you need to measure the temperature of the air near the tube. If you will be flying an airplane model, you should measure the absolute pressure as well.</li>
</ul>
<h3 id="an-example-design-for-3d-printing">An Example Design for 3D-Printing</h3>
<p>Let me explain the above points using my first Pitot tube design:</p>
<figure>
    <img src="/images/pitot/free-pitot.png" alt="A render of a Pitot tube design">
    <figcaption>A basic Pitot tube design render</figcaption>
  </figure>
  
<p>As you can see, it is a simple pointed stick with one port on the pointed end and one on the side. At the back, there are two nipples to connect the sensor hoses.</p>
<p>It&rsquo;s best explained by showing you the section view:</p>
<figure>
    <img src="/images/pitot/free-pitot-section.png" alt="A section view of the previously shown Pitot tube design">
    <figcaption>The section view of the design above</figcaption>
  </figure>
  
<p>Here, you can see the two channels connecting the ports to the nipples. The chamfers are there to keep the bridging to a minimum. The nipples are a long way away from the ports to avoid any influence on the air velocity, e.g. when holding it.</p>
<p>3D-printing is a fantastic way to make Pitot tubes; you can make the necessary channels and everything in one easily printed piece.</p>
<h2 id="the-actual-measurement">The Actual Measurement</h2>
<p>Once you have the Pitot tube connected to the differential pressure sensor, the next step is calculating the actual air velocity.</p>
<h3 id="the-assumptions">The Assumptions</h3>
<p>I&rsquo;m assuming ~2–5% accuracy with good repeatability, and a refresh rate in the order of a second or so is enough for your needs here. If you need more, it&rsquo;s time for a deep dive, sorry. 😅</p>
<p>BTW, did you know: 5% is the difference between a measurement and an indication. Once you go above 5% accuracy, you are measuring things.</p>
<p>Of course, we are talking  about volumetric air flow here. But in most practical cases, multiplying the mass air flow by the fixed air density value in your working conditions should give you a useful value.</p>
<h3 id="the-algorithm">The Algorithm</h3>
<p>It involves several steps:</p>
<ol>
<li>
<p>The pressure signal can be a bit noisy. E.g. with the Sensirion SDP810 sensor, the sampling rate can be up to ~2 kHz, which covers even a part of the audio spectrum and is sure to be noisy. A good antidote to noise is averaging, but the good news is, the sensor can do averaging already. It does full averaging for 25 ms and then exponential smoothing after that. As long as you aren&rsquo;t worried about noise with frequencies under about 40 Hz, this is good enough. If you need to go even lower, read the average every 25 ms and do exponential smoothing yourself. <a href="https://www.influxdata.com/blog/exponential-smoothing-beginners-guide/">Here</a> is a good primer. It&rsquo;s really simple to code and covers the majority of »we need to filter noisy input data« use cases, so you need it in your toolbox in any case.</p>
</li>
<li>
<p>You need temperature info. Temperature influences both the Mach velocity and gas density, both factors in the calculation. The Sensirion sensors include a temperature readout, so as long as the sensor is somewhat close to the Pitot tube, this should suffice. If not, add an DS18B20 sensor close to the sensing port of the Pitot tube.</p>
</li>
<li>
<p>You need absolute static (environment) pressure info. If you will never leave ground, assuming the pressure to be 1013hPa (mbar) is good enough, unless you live above about 500m of elevation. If you do, take the value from <a href="https://www.engineeringtoolbox.com/air-altitude-pressure-d_462.html">here</a>. If you are flying a (model) airplane, you will need to have an absolute air pressure sensor on board too.</p>
</li>
<li>
<p>Once you have all this data, the formula to use is (C++, Arduino): <code>v=(a0+((t)*css))*sqrt(5*(pow((fabs(p)/p0*k)+1,0.2857)-1));</code>, where:</p>
<ul>
<li><code>a0</code> is the speed of sound in m/s,</li>
<li><code>t</code> is the temperature of the air at the input port,</li>
<li><code>css</code> is the temperature coefficient of the speed of sound (at given absolute pressure, 0.606 at sea level; this is a non-linear relationship, but the value given should be good enough for atmospheric pressures given our assumptions),</li>
<li><code>p</code> is the measured pressure difference (Pa),</li>
<li><code>p0</code> is the absolute pressure (Pa).</li>
<li><code>k</code> is the Pitot tube coefficient, basically the measure of how much your measuring setup is influencing the airflow around the measuring ports. The good news is: If you positioned the Pitot tube well ahead of the rest of the hardware and did a reasonable job at directing and designing the Pitot tube, you can just say there should not be more than 2% of random influences, and you can set <code>k = 0.98</code>. Or you could take your setup for a ride in the car and calibrate it or at least check your assumptions. But keep in mind — getting the accuracy above 2% will involve some serious work.</li>
</ul>
</li>
</ol>
<p>The result is given in m/s.</p>
<p>Don&rsquo;t forget the conversion factors! E.g., with the SDP810, divide the raw pressure value by 240 and the raw temperature value by 200 to get Pascals and °C.</p>
<h3 id="the-hidden-bonus--no-calibration-required">The Hidden Bonus — No Calibration Required</h3>
<p>If you look at the above formula, there is only one calibration coefficient, and it is one that we can at least partially omit. So if you built the system well, and you need only a reasonable accuracy (a few percent), no calibration is needed. This is a major advantage compared to other types of sensors, like Venturi or fan-type, etc.</p>
<h2 id="what-about-the-flow-rate">What About the Flow Rate?</h2>
<p>Model airplanes are cool and all, but I think the average maker will be more interested in measuring air flows, e.g. for ventilation, air conditioning, burner control, carburetors, etc.</p>
<p>Some examples:</p>
<ul>
<li>
<p>If you are building a ventilation system with heat recuperation, you need the ingoing and outgoing airflows to be about equal, or you are wasting heat that could be recuperated.</p>
</li>
<li>
<p>If you are doing air conditioning, it&rsquo;s good to have some idea about how clogged your filters are. If a clean filter gives you 100 m<!-- raw HTML omitted -->3<!-- raw HTML omitted -->/h of flow at a certain fan power level, a measurement of only 80 m<!-- raw HTML omitted -->3<!-- raw HTML omitted -->/h over a certain time means it&rsquo;s time to clean or replace the filter.</p>
</li>
<li>
<p>Burners and carburetors have one job: keeping the ratio of mass flow of air to mass flow of fuel constant. Modern fuel injection systems use a MAF sensor to do this, and this should be the first thing you try, but if that is not an option, measure the air flow at least.</p>
</li>
</ul>
<h2 id="so-how-can-i-measure-airflow-using-a-pitot-tube">So How Can I Measure Airflow Using a Pitot Tube?</h2>
<h3 id="how-to-calculate-the-flow-rate">How to Calculate the Flow Rate?</h3>
<p>Once you have the air velocity, calculating the flow is easy: the air flow is simply the product of the <em>average</em> air velocity and the cross-section area of the flow channel. A duct with 1 m<!-- raw HTML omitted -->2<!-- raw HTML omitted --> of area and 1 m/s of <em>average</em> air velocity will thus have exactly 1 m<!-- raw HTML omitted -->3<!-- raw HTML omitted -->/s of air flow.</p>
<h3 id="surely-there-is-a-catch">Surely There Is a Catch?</h3>
<p>Note the italicized <em>average</em>s above. Airflow velocity inside a duct is not constant across the whole area. At the edges (near the walls of the duct), it will be slower due to air friction with the duct walls. The average velocity is thus the highest in the center of the duct and slowest at the walls. The place where we measure it clearly matters — putting the Pitot tube in the center will give values that are too high, and putting it near the walls will result in values that are too low. So how do we account for this?</p>
<p>One way is to use an engineering approximation — at a certain airspeed and a certain temperature, the average airspeed will be the measured airspeed in the center times a certain coefficient that you would get from engineering tables. But this is not very accurate, both because your airspeeds and temperatures vary and because there are other factors at play — maybe your duct is not straight or there is more roughness (dust) on the bottom than on the top of the duct. Maybe your duct is weird in some other way? Wouldn&rsquo;t it be nice if we could measure the average air velocity directly?</p>
<h3 id="the-averaging-pitot-tube">The Averaging Pitot Tube</h3>
<p>It turns out that we can do that, at least mostly. The answer is the <em>averaging Pitot tube</em>. It sounds complicated, but it is simply a normal Pitot tube with several input and reference ports. These ports are connected to a larger cavity (one for the inputs and one for the reference), so the air pressure in the cavity is roughly the average of all the air pressures at individual ports.</p>
<p>Again, it&rsquo;s easiest to explain with a couple of pictures. The averaging Pitot tube is installed to span the whole width of a duct:</p>
<figure>
    <img src="/images/pitot/averaging-pitot.png" alt="A render of the averaging Pitot tube design inside an air duct">
    <figcaption>The averaging Pitot tube in an air duct</figcaption>
  </figure>
  
<p>It has several ports over the whole width, which are connected by a large averaging chamber, which gives us an average over the individual port pressures and thus an average pressure difference:</p>
<figure>
    <img src="/images/pitot/averaging-pitot-section.png" alt="A section view of the averaging Pitot tube">
    <figcaption>The section view of the averaging Pitot tube above</figcaption>
  </figure>
  
<p>Some designs use a cross shape to average over the height AND width of the channel.</p>
<p>The pressures at the output nipples should roughly correspond to the average air velocity. We can calculate it using the formula in the previous section and then simply multiply with the (known) duct area, giving us the air flow.</p>
<p>Still, this is not a cure-all. You should have reasonably laminar flow in the duct to achieve any accuracy. In practice, this means you should try to put the tube at the end of a long straight duct, and avoid things that cause turbulence, like elbows.</p>
<h2 id="a-practical-example-of-a-measurement-system">A Practical Example of a Measurement System</h2>
<p>To help you see how to put all of this together, I set up a <a href="https://github.com/FrenkK/pitot">Github repository</a> with the STEP files for the two Pitot tubes and the code for an Arduino based measuring device I built.</p>
<figure>
    <img src="/images/pitot/pitot-controller.png" alt="The Pitot measuring device containing the differential pressure sensor, an Arduino controller and a battery">
    <figcaption>The Pitot measuring device I built</figcaption>
  </figure>
  
<p>From these humble beginnings, I went on to build a balanced-flow ventilation system for my home. It still works after several years. 🥳</p>
<p>Here is the testing setup I used for it. Note the measuring gear (averaging Pitot tube, hoses, and the measuring device) on the leftmost side of the duct. And on the right there are the 3D-printed impellers I was testing:</p>
<figure>
    <img src="/images/pitot/measuring-flow.jpg" alt="A measuring system comprised of a centrifugal fan, ducts, and the Pitot measuring device described above">
    <figcaption>Measuring flows of different impeller designs using a Pitot measuring system</figcaption>
  </figure>
  
<p>Let me know if you are interested in this in the comments.</p>
]]></content:encoded></item><item><title>Hardware for Talking Through 800 Years Old Walls Using LoRa</title><link>https://nunrg.eu/posts/lora_hardware/</link><pubDate>Sun, 09 Feb 2025 09:45:43 +0100</pubDate><guid>https://nunrg.eu/posts/lora_hardware/</guid><description>The hardware behind a LoRa link that works through 800-year-old walls: modules, enclosures, power and wiring for a heritage retrofit.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/hardware-for-talking-through-800-years-old-walls-using-lora-8bfcc990dcaf">reading this article on Medium</a></p>
<h2 id="part-2-the-hardware-you-need-to-control-a-distributed-heating-system-through-meter-thick-walls-you-are-not-allowed-to-drill">Part 2: The Hardware you Need to Control a Distributed Heating System Through Meter-thick Walls You are Not Allowed to Drill</h2>
<p>In <a href="https://nunrg.eu/posts/lora_basics/">part 1 of this series</a> we talked about how to retrofit a temperature control system into an 800-years-old heritage-protected monastery without drilling and running new cables. The solution turned out to be the <a href="https://en.wikipedia.org/wiki/LoRa">LoRa (Long Range)</a> radio transmission technology that trades transmission speed for extreme transmission range, enabling us to wirelessly talk to stations behind several thick walls over 100 meters away.</p>
<h2 id="the-constraints">The Constraints</h2>
<ul>
<li>No changes to the heritage protected building were allowed.</li>
<li>Retrofit budget was limited, ruling out changes to existing cabling.</li>
<li>It needed to be retrofitted to existing underfloor heating piping and valves.</li>
<li>It needed remote per-room temperature control.</li>
</ul>
<h2 id="the-hardware-structure">The Hardware Structure</h2>
<p>Let&rsquo;s refresh our memory and look at the proposed system structure:
<figure>
    <img src="/images/lora/structure.png" alt="Structure of a control system showing a LoRa gateway in the center, connected to an application server, the temperature sensor stations, and the control stations">
    <figcaption>System Structure</figcaption>
  </figure>
  </p>
<h3 id="the-hardware-parts">The Hardware Parts</h3>
<p>Note: I listed all the hardware parts I used with links to the manufacturer web pages. These are just for your information and I&rsquo;m not earning any income through these.</p>
<h4 id="temperature-control-stations">Temperature Control Stations</h4>
<p>Most of the rooms of the monastery have a large central table containing the electrical installation, and underfloor heating piping with valves, but some had only a small cubicle. I was going to have to install the control stations for these rooms there.</p>
<p>The designer chose to build these tables and cubicles from thick iron plate with laser/plasma cut decorations on the sides. It looks wonderful and they are extremely durable, but it meant I could not hide the entire control station inside — the iron plates were effectively a Faraday cage and prevented any radio communication inside. This meant I had to use control stations made from two parts — the radio communication part on the outside wall, and the power supply box with relays on the inside.</p>
<p>After fruitlessly searching for a suitable off-the-shelf product I decided to build these myself from individual modules:</p>
<h5 id="the-wireless-station">The Wireless Station</h5>
<p>The wireless station was the heart of the station, containing:</p>
<figure>
    <img src="/images/lora/wstationinside.png" alt="LoRa wireless station with cover removed and numbered parts">
    <figcaption>The Inside of the Wireless Station</figcaption>
  </figure>
  
<ol>
<li><a href="https://www.seeedstudio.com/Seeeduino-XIAO-Expansion-board-p-4746.html">A Seeeduino XIAO motherboard</a>, providing connectivity for everything else and a small OLED display for installation and diagnostic purposes, and holding:</li>
<li><a href="https://www.seeedstudio.com/Seeeduino-XIAO-Pre-Soldered-p-4747.html">A Seeeduino XIAO microcontroller board</a>, the heart of the control station. It controls the relays and the display, reads the data from the temperature/humidity sensor, and handles the communication with the LoRa board.</li>
<li>A <a href="https://www.seeedstudio.com/Grove-LoRa-E5-STM32WLE5JC-p-4867.html">Grove LoRa-E5 transceiver board</a>, handling all the wireless tasks.</li>
<li>A <a href="https://www.seeedstudio.com/Grove-AHT20-I2C-Industrial-grade-temperature-and-humidity-sensor-p-4497.html">Grove AHT20 I2C board</a> used to sense the humidity and temperature.</li>
<li>The <a href="https://www.seeedstudio.com/External-Antenna-868MHz-L195mm-black-folding-antena-With-Male-SMA-J-p-5045.html">LoRa antenna</a> for the transceiver board. The transceiver actually has it&rsquo;s own little antenna, but that was not enough, so I used a proper antenna, screwed to an <a href="https://www.seeedstudio.com/UF-L-SMA-K-1-13-120mm-p-5046.html">SMA connector with a pigtail cable and an I-PEX connector</a> for the transceiver board.</li>
<li>The wiring block.</li>
</ol>
<p>I had to design a housing for it as well, which I 3D-printed out of flame retardant PETG. It has some holes for the sensors and the diagnostic display. Given that it was installed in a public space, I designed it to be extra sturdy and it also features a holder making it harder to unscrew or break off the antenna.</p>
<figure>
    <img src="/images/lora/wstation-outside.jpg" alt="LoRa wireless station with cover">
    <figcaption>The Finished Wireless Station</figcaption>
  </figure>
  
<p>This was mounted on the outside of the iron tables and connected, using a 6-wire cable, to the power supply and relays board inside.</p>
<h5 id="the-power-supply-and-relays-board">The Power Supply and Relays board</h5>
<p>The wireless station, being publicly accessible, was strictly low-voltage (5V powered) for safety and it needed a power supply and relays to control the 230VAC underfloor heating control valves, so I needed to put all the high-voltage parts on a separate board, safely installed inside the table. It contains:</p>
<figure>
    <img src="/images/lora/relay-box.jpg" alt="The relay box for the LoRa wireless station with cover removed">
    <figcaption>The Inside of the Power Supply and Relay Board</figcaption>
  </figure>
  
<ul>
<li>A generic 4-relay board with built-in logic-level relay drivers</li>
<li>A generic 5-watt 230VAC to 5VDC safety switching power supply, powering all the electronics.</li>
<li>A fuse</li>
<li>The low-voltage wiring block (above, for the 6-wire cable coming from the wireless station)</li>
<li>The high-voltage wiring block (below, used to connect the station to 230VAC power and to wire the 230VAC valve actuators)</li>
</ul>
<p>This board is safely tucked inside the table/cubicle and contains some components that get warm, so I designed a lighter housing. The cover has ventilation holes for cooling, but provides touch protection.</p>
<figure>
    <img src="/images/lora/relay-box-cover.jpg" alt="The relay box for the LoRa wireless station with cover">
    <figcaption>The Finished Power Supply and Relay Board</figcaption>
  </figure>
  
<h4 id="wireless-temperature-sensors">Wireless temperature sensors</h4>
<p>After some research, I decided to use an off-the-shelf component, the <a href="https://store.rakwireless.com/products/rak7204-lpwan-environmental-node">RAK WisNode Sense Home (RAK7204)</a>, for the remaining wireless temperature, humidity and air quality sensors.</p>
<figure>
    <img src="/images/lora/RAK7204.jpg" alt="The RAK7204 LoRa wireless air temperature, humidity, and air quality sensor">
    <figcaption>The Wireless Sensor, image credit: RAK Wireless</figcaption>
  </figure>
  
<p>These wireless sensors are an elegant solution, running for anywhere from one to several years (depending on signal quality) on one included lithium primary battery. They also feature an automotive air quality sensor, giving us an additional data to monitor, e.g. for ventilation.</p>
<p>I glued some strong permanent magnets on their backs and simply stuck them to a less visible/reachable metal part in the room I wanted to monitor.</p>
<p>The monastery has quite a few rooms and corridors, so I installed 11 of these in all the places not covered with the control stations.</p>
<h4 id="the-lora-gateway">The LoRa Gateway</h4>
<p>As explained in the <a href="https://nunrg.eu/posts/lora_basics/">part 1 of this series</a>, using a public LoRa network was not an option for several reasons. This required installing a LoRa Gateway.</p>
<p>After some research, I again went for a solution from RAK, the <a href="https://store.rakwireless.com/products/rak7268-8-channel-indoor-lorawan-gateway?variant=42316475924678">WisGate Edge Lite (RAK7268)</a>. It has since been superseded, but the product on the link is mostly the same.</p>
<figure>
    <img src="/images/lora/rak7268.jpg" alt="The RAK7268 v2 LoRa gateway">
    <figcaption>The LoRa Gateway, image credit: RAK Wireless</figcaption>
  </figure>
  
<p>It only needs network and power. I found that the easiest way to interface with it was using the built-in MQTT server, but we will cover that in the software part of the series.</p>
<p>I installed it in a high place to get the best possible coverage and connected it to the same network switch the application server running Home Assistant was connected to.</p>
<h4 id="the-home-assistant-server">The Home Assistant Server</h4>
<p>The Home Assistant Server is running on a Raspberry Pi 4 SBC. For storage I used an SSD though an USB-SATA interface cable that allows me to store several years worth of data and backups. It is connected to the same switch as the LoRa Gateway.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In <a href="https://nunrg.eu/posts/lora_basics/">part 1 of this series</a> I described this solution in a general way, but this part is my attempt to clarify by showing you the  hardware I used.</p>
<p>As you can see, the off-the-shelf parts are all pretty low-cost components, easy to buy and install. The harder part was to select and integrate the custom wireless control stations, but I was forced to do this due to not finding the right component off the shelf. Still, using the rich plethora of modules available, it is not a big deal to assemble an usable system.</p>
<p>The wireless station design was heavily influenced by the fact it is a one-off project. I took proper care of electrical safety and reliability, but with less than 10 stations built, I did not have to think too much about the manufacturability of it.</p>
<p>The system still runs three years later with the original hardware and has turned out to be very reliable. The only maintenance needed is to replace the wireless sensor batteries as they wear out. In the three years so far, I only had to replace 5 batteries, making this a minimal, once-a-year effort.</p>
<p>The system needs some minimal software maintenance as well, but we will talk about that in part 3.</p>
<p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item><item><title>Wireless Control Through 800 Year Old Walls using LoRA</title><link>https://nunrg.eu/posts/lora_basics/</link><pubDate>Tue, 14 Jan 2025 08:18:04 +0100</pubDate><guid>https://nunrg.eu/posts/lora_basics/</guid><description>How to control a heating system through metre-thick heritage walls using LoRa — a wireless retrofit solution for old buildings.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/wireless-control-through-800-year-old-walls-using-lora-967e7168a1dc">reading this article on Medium</a></p>
<h2 id="part-1-how-to-control-a-distributed-heating-system-through-meter-thick-walls-you-are-not-allowed-to-drill">Part 1: How to control a distributed heating system through meter-thick walls you are not allowed to drill?</h2>
<figure>
    <img src="/images/lora/okno.jpg" alt="A very old restored monastery window">
    <figcaption>The monastery window, attribution: Matevz1400, CC BY-SA 4.0, via Wikimedia Commons</figcaption>
  </figure>
  
<p>A couple of years ago a friend had a problem: He is in charge of an 800-year-old monastery building now used as a conference center and an event venue. This majestic building was thoroughly renovated about 10 years ago, but the otherwise excellent new heat-pump-based underfloor heating system was missing the provisions for temperature control in individual rooms. Heating all the rooms all the time was wasteful and there was no way to set the temperature higher (e.g. in the conference hall) or lower (for custodial or temporarily unused spaces). Fortunately, the system already included the necessary underfloor heating loop valves, we just needed to find a way to control them.</p>
<p>The building spans about 100 meters with a square footprint and has very thick walls (meter or more). It is of course heritage protected as well, so drilling for new cabling was out of the question. We also really did not want to change the existing cabling; the long cable runs would be both expensive and time-consuming. We had to go wireless.</p>
<p>But there was a big problem: Wireless communication over more than 100 meters of distance and through meter-thick walls is too much for most of the known technologies, like WiFi, Zigbee, Bluetooth, etc. There is also the issue of licensing. What to do?</p>
<h2 id="lora">LoRa</h2>
<p>The solution was to use <a href="https://en.wikipedia.org/wiki/LoRa">LoRa (Long Range) technology</a>. It is a low-cost, long-range wireless communication technology that uses frequency bands from 400 to 900MHz. The LoRa protocol is much slower (kilobits instead of megabytes) than e.g. WiFi, but this is offset by the fact that it will work over distances of several kilometers in the open. Or, in our case, over 100 meters and through several thick walls. It also does not require a radio license (but you do have to check for legal limits in your area like permissible frequency bands, transmit powers, and duty cycles).</p>
<h3 id="timing">Timing</h3>
<p>LoRa transfers data at low speeds, between 0.3 to 50 kbits/s. This is not enough to visit  websites, control a robot, or listen to music, but plenty for a heating control system that needs at least several minutes to respond to changing inputs. This is why LoRa is used for  monitoring and control of remote systems like weather stations, reservoirs, and even satellites. There are many systems out there that only need to send or receive a few tens of bytes every few minutes or hours.</p>
<h3 id="complexity">Complexity</h3>
<p>The LoRa protocol is complex to implement from the ground up, but you can buy off-the-shelf gateways and sensor stations, e.g. from <a href="https://www.rakwireless.com/">RAK</a>. If you want to build custom systems, there are comprehensive software stacks and hardware modules available for all major micro-controller families, e.g. from <a href="https://www.seeedstudio.com/lora-solution.html">Seeed</a>. All this makes it possible to build a basic prototype quite quickly.</p>
<h3 id="an-iot-solution-or-a-closed-system">An IoT Solution or a Closed System?</h3>
<p>LoRa is a popular IoT solution and there are internet-connected public LoRa networks all over the world, many requiring a low-cost subscription. This makes it a great solution for mobile stations, e.g. location monitoring for machinery etc., or smaller systems where you don&rsquo;t want to set up a gateway yourself.</p>
<p>In our case, we had to install a LoRa gateway because of the difficult environment (a public network was available, but the signal would not reach the inside stations). We also have over 20 stations, so the gateway was not a big expense. And there is the issue of control system safety and reliability, both easier to achieve using own gateway.</p>
<h2 id="control-system">Control System</h2>
<h3 id="system-structure">System structure</h3>
<p>So what does the structure of the whole system look like?</p>
<figure>
    <img src="/images/lora/structure.png" alt="Structure of a control system showing a LoRa gateway in the center, connected to an application server, the temperature sensor stations, and the control stations">
    <figcaption>System Structure</figcaption>
  </figure>
  
<h3 id="application-server">Application Server</h3>
<p>The application server is the heart of the whole system. It runs on a <a href="https://www.raspberrypi.com/for-home/">Raspberry Pi</a>, runs Home Assistant, and is connected to the MQTT server in the LoRa gateway.</p>
<h3 id="home-assistant">Home Assistant</h3>
<p><img loading="lazy" src="/images/lora/homeassistant.png" alt="The Home Assistant logo"  />
</p>
<p><a href="https://www.home-assistant.io/">Home Assistant</a> is an open-source home automation platform that runs on Python. It provides many different components, including a web-based front end with authentication and integrations for climate control, MQTT, backups, data logging, and more. It provides a GUI for our whole system.</p>
<h3 id="connection-to-the-sensors-and-control-stations">Connection To the Sensors and Control Stations</h3>
<p>The LoRa gateway makes it possible to talk to all the LoRa sensors and control stations using <a href="https://mqtt.org/">MQTT</a>. Each underfloor heating valve is represented as an MQTT switch in Home Assistant, while the temperature/humidity/air quality sensors are represented as MQTT sensors. The actual temperature control is done using Home Assistant&rsquo;s <a href="https://www.home-assistant.io/integrations/climate/">Climate integration</a> as simple thermostats reading the MQTT temperature sensors and controlling the MQTT switches for underfloor heating valves.</p>
<h3 id="data-logging">Data Logging</h3>
<p>The system includes temperature, humidity, and air quality sensors. Their values are all <a href="https://www.home-assistant.io/integrations/history/">logged inside the Home Assistant system</a> and <a href="https://www.home-assistant.io/integrations/recorder/">stored in a database</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This article is an outline of the control system solution and I will be going into more detail in the following articles, where I will be talking about the control station design, the LoRa gateway config, and the Home Assistant config.</p>
<p>LoRa turns out to be an excellent solution for retrofitting monitoring and control into existing distributed systems, giving us the option to avoid expensive cabling and making it easier to respect the heritage protection rules.</p>
<p>Home Assistant was designed for home automation by enthusiasts but grew into a lovely  solution for diverse automation needs, including small and medium-sized HVAC and related systems. It needs some attention as the ecosystem grows and changes, but one update a year with maybe some config updates doesn&rsquo;t take a lot of time. It gives you a gorgeously looking GUI and all the features you need for control and data logging.</p>
<p>One of the larger challenges of decarbonization is the legacy systems. These will often use energy inefficiently (heating everything all the time is an excellent example) and lack things like temperature control, which is important for comfort. Sometimes, it makes sense to rebuild them completely, e.g. to replace fossil-fuel-based systems, but some legacy systems really only need some additional control. In these cases, using LoRa can be a comparatively simple and extremely low-cost solution with incredible ROI.</p>
<p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item><item><title>An Introduction to Smart Homes</title><link>https://nunrg.eu/posts/smarthome_intro/</link><pubDate>Wed, 16 Oct 2024 11:28:21 +0200</pubDate><guid>https://nunrg.eu/posts/smarthome_intro/</guid><description>An introduction to planning a smart home: how to think about controllers, wiring, reliability and openness before you build or renovate.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/an-introduction-to-smart-homes-4e88b88af677">reading this article on Medium</a>.</p>
<p>When you decide to build or extensively renovate your home, you will need to do a LOT of planning. This series of posts will give you some insights on how to begin to plan your smart home installation.</p>
<h2 id="the-old-way-of-doing-things">The Old Way of Doing Things</h2>
<p>Our homes weren&rsquo;t smart until about 10, maybe 15 years ago (for some rare enthusiasts like me).</p>
<p>The old way was to run two circuits of power wiring on separate fuses to each room, one for lamps, the other for outlets. Then you would have the lamps directly hard-wired to the switches and the switches to the power circuit (using wires in cable conduits inside the walls). The outlets would be directly wired to the other power circuit.</p>
<p>For large loads (washing machine, oven, water heater) you would have a separate fuse and power circuit, again directly hard-wired to the corresponding outlet.</p>
<p>All the fuses and a RCD were collected in a main distribution box somewhere near the entrance of the house.</p>
<h3 id="pros">Pros</h3>
<ul>
<li>Simple to install</li>
<li>Minimal length of wiring, minimal costs</li>
</ul>
<h3 id="cons">Cons</h3>
<ul>
<li>Very hard to make any changes after installation, everything is hard-wired</li>
<li>Very hard to automate</li>
<li>Very hard to log useful data</li>
</ul>
<h2 id="what-is-a-smart-home-then">What Is a Smart Home Then?</h2>
<p>For me, the defining feature of a smart home installation is the fact that hard-wiring of functions is, to the extent this is sensible, replaced with software and automation elements.</p>
<p>Instead of wiring a switch directly to the lamp the old way, the switch is wired to a controller digital input and the lamp is wired to the controller relay output. When we press the switch, the controller software senses this and switches the relay, turning the lamp on or off.</p>
<figure>
    <img src="/images/smarthome/hardwire.png" alt="Hardwired v. Controlled">
    <figcaption>The Paradigm Shift</figcaption>
  </figure>
  
<p>At first glance, this seems like a really roundabout way of doing things, but it opens all kinds of possibilities:</p>
<ul>
<li>You can turn the lamp off automatically once the presence sensor is inactive for a while to save energy.</li>
<li>You can turn the lamp on automatically when someone enters the room, but only if the illumination sensor shows it&rsquo;s dark in there.</li>
<li>If you are deaf, you can set the lights to blink three times if somebody rings at the door.</li>
<li>You can turn things (e.g. heat) on or off remotely, so you come home from a long trip to a warm home.</li>
<li>You can reuse the same sensors for different purposes, e.g. presence sensors can be used for alarms, energy saving functions and comfort.</li>
</ul>
<p>The possibilities are really endless and can help you save money and energy, have more comfort, be safer, etc.</p>
<h2 id="what-are-the-main-features-of-a-well-designed-smart-home-system">What Are the Main Features of a Well Designed Smart Home System?</h2>
<h3 id="reconfigurability">Reconfigurability</h3>
<p>We all know how we try to make plans and then life happens.</p>
<p>You want to use a different switch for a certain lamp? With a good smart home system, all you need to do is change a line of a YAML file. Adding many features often entails simply installing and configuring a plugin. <a href="https://nunrg.eu/posts/smarthome_topology/">A well designed system also allows easy hardware upgrades and system extensions (e.g. adding a room) due to it&rsquo;s modular and distributed nature.</a></p>
<h3 id="reliability">Reliability</h3>
<p>Things break. A smart home will have many more parts than a traditional installations and you are certain to have a failure every now and then. It is important the system is built as resiliently as possible.</p>
<p>I cannot overstate the importance of good backup, which is the backbone of reliability in any software-controlled system. It is the difference between an utter catastrophe and an inconvenient chore once something breaks or you are hacked. <strong>Make at least daily backups of any relevant files locally</strong> (you need a NAS anyway) and <strong>invest in a good well-encrypted online backup service</strong> to backup at least the important files from there. I found <a href="https://www.idrive.com/">IDrive</a> to work on any Linux SBC and the pricing is reasonable.</p>
<p>Modularity is an important part of reliability—a <a href="https://nunrg.eu/posts/smarthome_topology/">set of several smaller subsystems</a> will be more reliable, less complex, and easier to troubleshoot than an equivalent big system. There is also an added benefit that most of the overall system will continue to work despite a part being down.</p>
<p>Another important part is wiring. Wireless connectivity can be really helpful where wiring things is impractical, but you should wire everything else. This gives you several advantages:</p>
<ul>
<li>No need for batteries. Batteries wear out and this is bad for two reasons: First there are the natural resources being wasted and secondly, a lot of e-waste comes from devices that are simply thrown away instead of replacing the battery (if that is even possible).</li>
<li>Wireless works most of the time. Wired works all the time.</li>
<li>Some devices need to be responsive: When you press a button, the lamp should light up in less than a second or you will think there is a problem. Wireless loses packets all the time and you will get weird delays/issues when you least expect them.</li>
</ul>
<h3 id="sustainability">Sustainability</h3>
<p>Sustainability has several dimensions:</p>
<h4 id="sustainable-use-of-materials">Sustainable Use of Materials</h4>
<p>Choosing the <a href="https://nunrg.eu/posts/smarthome_topology/">right system topology</a> can substantially reduce the amount of work and materials needed. This will also have knock-on effects on reliability and reconfigurability.</p>
<h4 id="sustainable-choice-of-platforms">Sustainable Choice of Platforms</h4>
<p>To put it very simply: If you are building a system that needs to work for a couple of decades, you must <strong>actively avoid any closed source products!</strong></p>
<p>15 or 20 years later, that fancy PLC will not be available to buy if you need to replace it. Even more, the programming software for it will likely not work on the future OS (Windows 17?). And since it&rsquo;s all closed source, you can&rsquo;t port or hack it. All the expensive gear and precious natural resources will go to waste and you will have to start almost from scratch.</p>
<p>Bought a cloud-enabled app-controlled EV charger? Read about <a href="https://www.theverge.com/2024/10/2/24260316/juicebox-ev-chargers-enel-x-way-closing-discontinued-app">Juicebox</a> and imagine being locked out of your smart home because somebody ran their company into the ground and simply turned off the cloud computers.</p>
<p>What will work forever? Open source software (Linux, Home Assistant and a lot more), open hardware (any Linux SBC or PC, Modbus or MQTT relay/input boxes, etc.) and open industrial communication protocols/standards (Modbus, RS232, RS485, Ethernet, MQTT, and a lot more). If something breaks, these are cookie-cutter modules—even if the one you want to replace is not available anymore, just buy a similar one and reconfigure (RaspberryPi 12 is sure to happen someday).</p>
<h4 id="sustainable-use-of-energy">Sustainable Use of Energy</h4>
<p>As you are planning your home, remember that efficiency pays for itself quickly. Buy better performing machines, but don&rsquo;t pay extra for gimmicks:</p>
<ul>
<li>Better home insulation will be more expensive, but then you need a smaller and cheaper heat pump, air conditioning, etc., so it should pay for itself.</li>
<li>A heat pump does not need a fancy app to control it, just a Modbus interface for your system and the best COP you can afford.</li>
<li>Invest in ventilation with heat recuperation. Or live with mold. Forget about apps, choose the model with efficient motors and some kind of communication interface. An integrated CO2/VOC sensor is a plus, so you only run it when needed.</li>
<li>Buy an EV charger that will be locally controllable (i.e. it has a control interface) by your smart-home system so you can charge efficiently. Or <a href="https://nunrg.eu/posts/diy_ev_charging/">build one yourself</a>.</li>
<li>Some EVs will consume 12kWh/100km, some will consume 25. Buy what you need, but not more. Same size cars from different manufacturers can have very different consumption. Why am I including your car in this article about smart homes? It will be a major consumer of energy, but also has the potential for a LOT of optimization, from smart charging control to <a href="https://en.wikipedia.org/wiki/Vehicle-to-grid">V2G</a>. Most important: For the love of Earth, don&rsquo;t replace it until it stops working or you <em>really</em> need a different one. Buying a new car every few years is easily the most expensive habit of your life and a colossal waste of resources.</li>
<li>Heating/air-conditioning is easily your biggest energy consumer. Install temperature and humidity sensors in all the residential rooms and make the heating controllable so you can only use the energy you really need.</li>
</ul>
<h2 id="questions-you-need-to-ask-yourself">Questions You Need To Ask Yourself</h2>
<p>Having a smart home can save you money, increase comfort and safety, and make your life more sustainable, mainly by reducing your energy consumption. But there are some important questions:</p>
<ul>
<li>Could you build it yourself, even if you choose to use a contractor?</li>
<li>If not, do you understand it well enough to communicate your desires, needs, and concepts to the contractor?</li>
<li>Do you trust the contractor?</li>
<li>Is the design clear and well-documented enough for somebody else to continue maintaining the system in case you want to change the contractor?</li>
<li>Are you able to reconfigure the system yourself after it is built or will you need ongoing support here as well? What about repairs?</li>
<li>Have you thought enough about the sustainability of the system?</li>
<li>Did you avoid pitfalls related to closed systems?</li>
</ul>
<p>Be honest with yourself, the answers to these questions are important! Depending on them, your smart home can be an awesome thing or a source of SEVERE frustration. Good luck!</p>
<p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item><item><title>A Very Simple Low Cost Filament Dryer</title><link>https://nunrg.eu/posts/filamentdryer/</link><pubDate>Thu, 19 Sep 2024 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/filamentdryer/</guid><description>A cheap, simple DIY filament dryer for 3D printing, built from a peltier dehumidifier, an ESP8266 and ESPHome for humidity control.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/a-simple-and-efficient-low-cost-filament-dryer-70985a44214a">reading this article on Medium</a></p>
<h2 id="the-problem">The problem</h2>
<p>A 3D printer is the most versatile tool any maker can have, but it has possible pitfalls and one of the more insidious ones is damp filament.</p>
<p>What happens: You have the printer dialled-in perfectly, everything works perfectly, a couple of weeks pass and then you try to print something again. It comes out looking terrible: blobs, stringing, ugly surfaces.</p>
<p>This is due to humidity absorbed by the filament. Once the filament enters the hot extruder, humidity expands into steam and plays havoc with the flow of molten material in the nozzle. In really bad cases you can actually hear the pops from the tiny steam bubbles during printing.</p>
<p>Some materials are more sensitive than others—you will be able to leave ABS or PLA out longer than, say Nylon, the worst of them. But one month outside in summer heat will make any filament unusable.</p>
<p>You can delay these problems a lot by storing filaments in an enclosed container and adding some desiccant helps even more. But eventually, any filament will get humid enough to need drying.</p>
<h2 id="the-most-common-solution">The Most Common Solution</h2>
<figure>
    <img src="/images/dryer/sunlu.png" alt="Sunlu Filadryer">
    <figcaption>SUNLU S2 Filament Dryer, credit: Sunlu</figcaption>
  </figure>
  
<p>On the surface, this looks like a solved problem: e.g. Sunlu dryer boxes work well and are not that expensive. But this kind of dryer box is not without problems:</p>
<ul>
<li>They are not  cheap, a two-roll box will cost you about ~90€.  If you are serious about printing, you will need at least two of those.</li>
<li>They have 150-200W heaters. If you are paying German electricity prices, each day you use one will cost ~1-2€. And each year has 365 days&hellip;</li>
</ul>
<p>So is there a different way?</p>
<h2 id="how-does-drying-even-work">How Does Drying Even Work?</h2>
<h3 id="the-usual-dryer-boxes">The Usual Dryer Boxes</h3>
<p>The dryer boxes I mention above work by heating up the air. Hotter air can carry more moisture (relative humidity is reduced) and this &ldquo;sucks&rdquo; moisture from the filament due to osmosis (which happens faster due do higher temperature as well). The hot and humid air is then blown out of the enclosure automatically or you need to do it manually after the end of each cycle. Some will take advantage of moisture condensing on the cooler walls, which you must then wipe down.</p>
<p><a href="https://www.printdry.com/the-science-behind-it/">See this explanation for extra info.</a></p>
<h3 id="the-proper-way-to-dry-things">The Proper Way To Dry Things</h3>
<p>If you look at industrial dryers or modern clothes dryers, these work differently.</p>
<p>The first step of the process is the same: Blow hot air into the chamber to capture the humidity. The next step is very different, though: The hot humid air is cooled, which causes the excess moisture to condense away, leaving you with dry cool air that you can then heat up again to dry the filament, in an endless circle.</p>
<p>At first glance, this seems like a very wasteful process: instead of simply heating the air, you need to cool it too. But here comes the magic part: You can use a heat-pump to transfer the heat from cooling to the heating part of the cycle. A modern heat-pump is extremely efficient and will only consume about 25% of energy it is able to transfer. So if you need 200W of heating power, you will only need about 40W of electricity for the same effect. What is more, because you are cooling the return air, the return air is very dry and the drying effect is even stronger.</p>
<figure>
    <img src="/images/dryer/hpdryer.jpg" alt="Heat-pump dryer schematic">
    <figcaption>A schematic of a heat-pump clothes-dryer, credit Massachusets Clean Energy Center</figcaption>
  </figure>
  
<h2 id="my-solution">My Solution</h2>
<h3 id="the-parameters">The parameters</h3>
<p>I wanted a solution that does the following:</p>
<ul>
<li>There should be enough space for at least 10 rolls of filament.</li>
<li>It should be able to work continuously, no wiping down etc.</li>
<li>It should consume no more than 50W of electricity (a quarter of the consumption of a normal dryer)</li>
<li>Some monitoring would be nice</li>
</ul>
<h3 id="the-hardware">The hardware</h3>
<p>There are many possibilities:</p>
<p>I could possibly build a really big dryer out of a second-hand clothes-dryer. My neighbor and I actually converted one into a big fruit dryer so his apples and plums don&rsquo;t spoil after the harvest. But while this is a good way to dry out large amounts of moisture, filaments only contain a few grams of moisture each at most and the dryer will consume 700W of electricity. So this would be a really wasteful way of doing it and it would take up a lot of space.</p>
<p>Maybe a mini fridge? I could use an air duct to heat up the air at the rear heat-exchanger, blow it into a box, and then return it to the cold side of the mini fridge. But this would take extra space and I&rsquo;m not sure how to cool the air effectively.</p>
<p>In the end I decided to buy a very simple and very cheap room air-dryer on Aliexpress. These small units will consume about 15W and dry out a couple of tens of grams of moisture a day from the surrounding air.</p>
<figure>
    <img src="/images/dryer/airdryer.png" alt="The Little Air-Dryer">
    <figcaption>It Looks Like This</figcaption>
  </figure>
  
<p>They use a <a href="https://en.wikipedia.org/wiki/Thermoelectric_cooling">Peltier element</a> instead of a proper heat-pump, but since we need so little drying capacity, this is good enough. The Peltier element, the two heat-sinks and the fan are combined into one compact unit like this:</p>
<figure>
    <img src="/images/dryer/peltier.jpg" alt="The Peltier Block">
    <figcaption>Connect it to 12 V and it freezes in a minute or two</figcaption>
  </figure>
  
<p>There is also a power-supply, so you only need some more fans, a control board and a new housing.</p>
<h3 id="the-box">The Box</h3>
<p>I decided to use a simple plastic storage box I kept the filaments in already. IKEA is a good place to find one that is large enough for your needs. Mine is about 40 x 50 x 60 cm, enough for about 12 rolls.</p>
<h3 id="the-housing">The Housing</h3>
<p>My idea was to put the entire Peltier block along with two temperature sensors (DS1820, hot and cold side), a humidity sensor (AHT20, for monitoring) and two fans (hot and cold side) inside the box as a drying unit.</p>
<p>A small hole in the side of the box connects this drying unt with the control box outside. The control box contains the power supply, an ESP8266 controller and a couple of relays for control.</p>
<p>There is also a small outlet for the condensed water on the bottom of the cold side housing. This is connected to a small tube and routed outside the box.</p>
<figure>
    <img src="/images/dryer/housing.png" alt="The Housing">
    <figcaption>A render of the housing design. The side with the fans is inside the box, the side with the grid is outside.</figcaption>
  </figure>
  
<p><em>Here is the <a href="https://a360.co/4e8Qa8P">Fusion project</a>, if you want to play with it.</em></p>
<h3 id="the-first-prototype">The First Prototype</h3>
<figure>
    <img src="/images/dryer/dryer.jpg" alt="The whole thing">
    <figcaption>The whole thing. It&#39;s the first prototype and therefore ugly, but it works, so it may take a while before I work on it again. The small fan is for the power supply, but it&#39;s not really needed, I should have put that inside, near the hot side fan.</figcaption>
  </figure>
  
<h3 id="control">Control</h3>
<p>I decided to use ESPHome and integrate it into my Home Assistant system to control the whole thing.</p>
<p>The first version was over-engineered, with PWM control of both fans and measurements on hot and cold side. It turns out you only need to monitor humidity and control two relays, one for the Peltier element and the hot side fan, the other for the cold side fan. You could even do without the cold side fan at the expense of a slightly lower drying power.</p>
<figure>
    <img src="/images/dryer/esphome1.png" alt="The Home Assistant dashboard">
    <figcaption>The peaks happen when I turn the whole thing off to save power</figcaption>
  </figure>
  
<h4 id="the-algorithm">The Algorithm</h4>
<p>The algorithm is simple:</p>
<ol>
<li>Turn on the Peltier and the hot side fan.</li>
<li>Wait for a while for the water to condense on the cold side. (The hot side fan is strong enough to ensure the circulation on the cold side too.)</li>
<li>Turn off the Peltier and turn on cold side fan to blow the water off the heat sink ribs down into the water outlet. A minute is enough. This is optional, water will drip down on it&rsquo;s own until there is very little moisture left.</li>
<li>Repeat.</li>
</ol>
<p>It also needs a safety feature to turn off the whole thing if the hot side heats up above 45 °C (i.e. fan problems).</p>
<p>I also added an automation that only turns the dryer on when there is extra power available from the solar plant I have installed. This is optional, but it does keep the whole thing from taking energy from the grid.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>The whole thing cost me about 70€ at most and it works. There is one drawback: It takes a few days to dry a moist filament roll, but it keeps everything nice and dry afterwards. It seems that putting in a moist roll does not affect the rest of the stored rolls at all.</p>
<h2 id="the-esphome-code-and-planned-improvements">The ESPHome Code and Planned Improvements</h2>
<h3 id="planned-improvements">Planned improvements</h3>
<p>The code is for the current over-engineered system. The next version will have no PWM, no cold side fan, and no cold side temperature sensor. The hot side fan power will be fixed using a resistor. There will be only one relay that controls both the Peltier and hot side fan. The power supply will be next to the hot side fan to use the wasted energy to heat the box.</p>
<h3 id="the-code">The Code</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">esphome</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">susilnik</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">friendly_name</span>: <span style="color:#ae81ff">susilnik</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">esp8266</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">board</span>: <span style="color:#ae81ff">esp01_1m</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Enable logging</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">logger</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Enable Home Assistant API</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">api</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">encryption</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">key</span>: <span style="color:#e6db74">&#34;secret&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">ota</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">esphome</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>: <span style="color:#e6db74">&#34;secret&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">wifi</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ssid</span>: !<span style="color:#ae81ff">secret wifi_ssid</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">password</span>: !<span style="color:#ae81ff">secret wifi_password</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">use_address</span>: <span style="color:#ae81ff">secret</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">one_wire</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">gpio</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pin</span>: <span style="color:#ae81ff">13</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">sensor</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">aht10</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">temperature</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Temperatura&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">humidity</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">id</span>: <span style="color:#ae81ff">vlaznost</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Vlaznost&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">update_interval</span>: <span style="color:#ae81ff">60s</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">dallas_temp</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">0x090417b10b0cff28</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Temperatura topla stran&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura_topla_stran</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">dallas_temp</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">0x690517a0c505ff28</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Temperatura hladna stran&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura_hladna_stran</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">i2c</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">sda</span>: <span style="color:#ae81ff">4</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scl</span>: <span style="color:#ae81ff">5</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">frequency</span>: <span style="color:#ae81ff">10kHz</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">switch</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">gpio</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pin</span>: <span style="color:#ae81ff">16</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Vklop 12V&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">restore_mode</span>: <span style="color:#ae81ff">ALWAYS_ON</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">output</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">esp8266_pwm</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pin</span>: <span style="color:#ae81ff">12</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">frequency</span>: <span style="color:#ae81ff">10</span> <span style="color:#ae81ff">Hz</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">pwm_hladni_ventilator</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">esp8266_pwm</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pin</span>: <span style="color:#ae81ff">14</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">frequency</span>: <span style="color:#ae81ff">10</span> <span style="color:#ae81ff">Hz</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">pwm_hladilni_ventilator</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">fan</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">speed</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">output</span>: <span style="color:#ae81ff">pwm_hladni_ventilator</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Hladni ventilator&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">hladni_ventilator</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">speed</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">output</span>: <span style="color:#ae81ff">pwm_hladilni_ventilator</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Hladilni ventilator&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">hladilni_ventilator</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">time</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">homeassistant</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">homeassistant_time</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">on_time</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">#Safety functions</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—minutes</span>: <span style="color:#ae81ff">/1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">if</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">switch.is_on</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>             <span style="color:#f92672">—if</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">or</span>:
</span></span><span style="display:flex;"><span>                     <span style="color:#f92672">—sensor.in_range</span>:
</span></span><span style="display:flex;"><span>                          <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura_topla_stran</span>
</span></span><span style="display:flex;"><span>                          <span style="color:#f92672">above</span>: <span style="color:#ae81ff">45</span>
</span></span><span style="display:flex;"><span>                     <span style="color:#f92672">—sensor.in_range</span>:
</span></span><span style="display:flex;"><span>                          <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura</span>
</span></span><span style="display:flex;"><span>                          <span style="color:#f92672">above</span>: <span style="color:#ae81ff">40</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">switch.turn_off</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—minutes</span>: <span style="color:#ae81ff">55</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">if</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">switch.is_on</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">fan.turn_on</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">id</span>: <span style="color:#ae81ff">hladni_ventilator</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">speed</span>: <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—minutes</span>: <span style="color:#ae81ff">57</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">fan.turn_off</span>: <span style="color:#ae81ff">hladni_ventilator</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">#Start cooling fan for PSU</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—minutes</span>: <span style="color:#ae81ff">/1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">if</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">switch.is_on</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">fan.turn_on</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">id</span>: <span style="color:#ae81ff">hladilni_ventilator</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">speed</span>: <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">else</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">fan.turn_off</span>: <span style="color:#ae81ff">hladilni_ventilator</span>
</span></span></code></pre></div><p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item><item><title>How To Use Minimal Power To Control an E-paper Display With an ESP32</title><link>https://nunrg.eu/posts/lowpowerepaper/</link><pubDate>Sat, 14 Sep 2024 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/lowpowerepaper/</guid><description>How to drive an e-paper display on minimal power with an ESP32 — deep sleep, an energy harvester and a supercapacitor instead of a battery.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/p/0f0f993d9ee0">reading this article on Medium</a></p>
<h2 id="what-do-i-mean-by-low-power">What Do I Mean by Low Power?</h2>
<p>I was involved in an art project where we wanted to use a smallish (10 x 15cm) solar panel to power an e-paper display inside a gallery, controlled by an ESP32 micro controller.</p>
<p>This would be a pretty trivial job outdoors: the panel has 5W peak power and I could just put a small DC-DC converter on it to get stable 3.3V power from it during normal daylight.</p>
<p>Indoors, the game changes completely, due to two main issues: low illuminance and low solar panel efficiency at low illuminance.</p>
<p>Outdoors, the illuminance will be anywhere from 1000 to 100000lux during the day. On the other hand, normal indoor lighting would normally range from 100 to 500lux. This alone reduces the available power from the solar cell by a factor of 200 or more. 5W turns into 25mW.</p>
<p>But there is another problem: The solar panel efficiency drops at low light. At full illuminance, the total power conversion efficiency of a modern solar panel can exceed 20%, but the same panel will have a much lower efficiency at low light due to interior current leakage. The expected 25mW turns into 5 or 10mW at most with normal indoor lighting.</p>
<p>To put this in perspective: ESP32 will consume somewhere around 900 mW transferring data via WiFi and some 20 to 70 mW with radio off. See <a href="https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/">this helpful article</a>.</p>
<p>So is there no way to power the ESP32 using our solar panel?</p>
<h2 id="the-solution">The Solution</h2>
<p>Our project involves refreshing an e-paper display at certain intervals and e-paper consumes power only during the refresh (drawing). So the solution is to somehow store the miniscule amount of available energy until there is enough to draw the next picture, turn everything on, draw the picture, save the data for the next one, and power off.</p>
<h3 id="the-hardware">The Hardware</h3>
<p>On the hardware side, I achieved this using a solar harvester chip <a href="https://e-peas.com/product/aem10941/">AEM10941</a> evaluation board that very efficiently charges a single super-capacitor. This super-capacitor in turn powers a <a href="https://www.pololu.com/product/2561">low-power, low-voltage boost converter</a> and this powers the ESP32.</p>
<figure>
    <img src="/images/epaper/powerpath.png" alt="The Power Harvesting System">
    <figcaption>The Power Harvesting System</figcaption>
  </figure>
  
<h3 id="the-software">The Software</h3>
<p>But the hardware alone is not the solution yet: I needed a way to turn off everything so the harvester has the chance to charge the capacitor up, do the drawing only when the capacitor is full and then turn off everything again until there is enough power available.</p>
<h4 id="esp32-deep-sleep">ESP32 Deep Sleep</h4>
<p>The solution is to implement the deep sleep on the ESP32. While in deep sleep, the ESP32 chip only consumes 10μA, but it powers off almost everything, leaving only a small portion of slow RTC RAM and a counter that periodically wakes it up.</p>
<p>So how does it all work?</p>
<ol>
<li>
<p>The system spends most of the time in deep sleep, where the combined power usage of the power harvesting system, converter and ESP32 never exceeds about 150μW.</p>
</li>
<li>
<p>Every 5 or 10 seconds it wakes up for a fraction of a second to measure the capacitor voltage:</p>
<p>2a. If the voltage is not high enough, it goes to deep sleep again.</p>
<p>2b. If the voltage is high enough, it draws the picture, stores the sequence number into the RTC RAM and goes to deep sleep again.</p>
</li>
<li>
<p>Rinse and repeat.</p>
</li>
</ol>
<h4 id="pick-the-right-esp32-board">Pick the Right ESP32 Board!</h4>
<p>There is a possible pitfall here: Some ESP32 development boards, like the basic ESP32 Dev Module, power additional circuitry (level converters, USB-to-RS232 chips etc.) from the 3.3V rail. This means they will always use at least 10mW of power, regardless of the deep-sleep mode, which is useless for our use case. I had really good experience with the <a href="https://www.olimex.com/Products/IoT/ESP32/ESP32-DevKit-LiPo/open-source-hardware">Olimex ESP32-DevKit-LiPo</a> boards in this respect, but there are other well built development boards out there.</p>
<h4 id="there-are-always-unintended-consequences">There Are Always Unintended Consequences</h4>
<p>Remember how the chip powers off almost everything when in deep sleep? All the regular RAM contents are lost too, only the sequence number in RTC RAM remains.
So when I try to draw the next picture, the system doesn&rsquo;t know a picture was already drawn previously and simply draws over it. The results are really ugly:</p>
<p><img loading="lazy" src="/images/epaper/ugly.gif" alt="Video of the flawed drawing"  />
</p>
<p>I spent way too much time thinking how to fix this&hellip;</p>
<p>One option would be to use a full refresh, but that would entail the annoying blinking of the whole display. Then I thought about adapting the library and storing some of it&rsquo;s variables in the RTC RAM. This seemed like a sure way to lose all my remaining hair, though.</p>
<p>After a lot of thinking, I finally thought of a nice hack. The system doesn&rsquo;t know what was displayed previously? I should draw the exact same picture as the last time to &ldquo;refresh it&rsquo;s memory&rdquo; and right afterwards draw the new one.</p>
<p>This worked beautifully:</p>
<p><img loading="lazy" src="/images/epaper/nice.gif" alt="Video of the corrected drawing"  />
</p>
<h2 id="conclusion">Conclusion</h2>
<p>Super-capacitors are a wonderful way to power small devices that work at intervals when there is not much power available. They are preferable to batteries due to their much better cycling ability. I like things that you can put on a wall and expect them to work for years and years instead of becoming electronic waste soon.</p>
<p>And if you use deep sleep with ESP32, always keep in mind the lost RAM contents when the chip wakes up again.</p>
<h2 id="the-code">The Code</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;GxEPD2_BW.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;GxEPD2_display_selection_new_style.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;bitmaps/images.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;pgmspace.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;driver/adc.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;esp_sleep.h&gt;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>RTC_DATA_ATTR <span style="color:#66d9ef">int</span> pic_index <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">int</span> adcPin <span style="color:#f92672">=</span> <span style="color:#ae81ff">34</span>; <span style="color:#75715e">// GPIO34 (ADC1_CH6)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">int</span> sleepTimeSeconds <span style="color:#f92672">=</span> <span style="color:#ae81ff">3</span>; <span style="color:#75715e">// Sleep time in seconds
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">int</span> array_size <span style="color:#f92672">=</span> <span style="color:#66d9ef">sizeof</span>(images)<span style="color:#f92672">/</span><span style="color:#66d9ef">sizeof</span>(images[<span style="color:#ae81ff">0</span>]);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">int</span> refresh_size <span style="color:#f92672">=</span> <span style="color:#ae81ff">30</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setup</span>()
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Configure ADC
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#a6e22e">adc1_config_width</span>(ADC_WIDTH_BIT_12);
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">adc1_config_channel_atten</span>(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Read the voltage on pin 34
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">int</span> adcValue <span style="color:#f92672">=</span> <span style="color:#a6e22e">adc1_get_raw</span>(ADC1_CHANNEL_6);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">float</span> voltage <span style="color:#f92672">=</span> adcValue <span style="color:#f92672">*</span> (<span style="color:#ae81ff">3.3</span> <span style="color:#f92672">/</span> <span style="color:#ae81ff">4095.0</span>); <span style="color:#75715e">// Convert ADC reading to voltage
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (voltage<span style="color:#f92672">&gt;</span><span style="color:#ae81ff">2.45</span>) { <span style="color:#75715e">// Only if voltage is high enough
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">if</span> (pic_index<span style="color:#f92672">%</span>refresh_size <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> <span style="color:#f92672">||</span> pic_index<span style="color:#f92672">%</span>array_size <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) {
</span></span><span style="display:flex;"><span>      display.<span style="color:#a6e22e">init</span>(<span style="color:#ae81ff">0</span>, true, <span style="color:#ae81ff">2</span>, true); <span style="color:#75715e">// first update should be full refresh
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>      display.<span style="color:#a6e22e">init</span>(<span style="color:#ae81ff">0</span>, false, <span style="color:#ae81ff">2</span>, true); <span style="color:#75715e">// following updates should be normal refresh + draw previous picture to avoid artifacts
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      display.<span style="color:#a6e22e">drawImage</span>(images[(pic_index<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)<span style="color:#f92672">%</span>array_size], <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">400</span>, <span style="color:#ae81ff">300</span>, false, false, true);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    display.<span style="color:#a6e22e">drawImage</span>(images[pic_index<span style="color:#f92672">%</span>array_size], <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">400</span>, <span style="color:#ae81ff">300</span>, false, false, true);
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>    display.<span style="color:#a6e22e">hibernate</span>(); <span style="color:#75715e">//Turn off voltage converters inside the display to save power
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>    pic_index<span style="color:#f92672">++</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Configure the ESP32 to wake up after a fixed delay
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#a6e22e">esp_sleep_enable_timer_wakeup</span>(sleepTimeSeconds <span style="color:#f92672">*</span> <span style="color:#ae81ff">1000000</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">esp_deep_sleep_start</span>();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">loop</span>()
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item><item><title>Example DIY EV Charging Point</title><link>https://nunrg.eu/posts/example_diy_charging_point/</link><pubDate>Fri, 27 Oct 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/example_diy_charging_point/</guid><description>A complete DIY EV charging point: bill of materials, schematics, ESPHome firmware and code for a simple, safe home EVSE build.</description><content:encoded><![CDATA[<p>Let’s go into details!</p>
<figure>
    <img src="/images/EV_charging/finished.jpg" alt="Finished product">
    <figcaption>What It Might Look Like</figcaption>
  </figure>
  
<p>If you’ve been reading my <a href="https://nunrg.eu/posts/diy_ev_charging/" title="DIY EV Charging">DIY EV Charging Point Basics</a> post, you probably wonder how all the options mentioned might be distilled into a practical DIY project. This post describes a charging point I actually built. I will explain why and how I made the individual choices, list an actual BOM, describe how everything is connected and even give you the configuration files I used in the first version.</p>
<h2 id="design-objectives">Design Objectives</h2>
<h3 id="some-background">Some Background</h3>
<p>I built this charging point for a neighbor friend who recently, after some deliberation, decided to start driving an EV in the best possible way for “beginners”: He bought a used short-range EV he uses as the “second car” to drive to work and do errands (he has a big family, so there are many errands to take care of). This saves him a ton of gas money and the objectively better driving experience is simply the icing on the cake.</p>
<p>Until now, he was using the charging cable he got with the car, but this was slow (only half the available charging power) and he had no handy sockets to plug it in, forcing him to use an extension cord. So we talked and I offered to help him build a proper Type 2 charging point, enabling him to use the full charging power and avoid dealing with the “cable salad”, while having everything properly prepared for the next EV that is sure to come in time. We sat down, talked and decided what to build.</p>
<h3 id="parameters">Parameters</h3>
<ul>
<li>He does not have a smart home solution, so the solution had to be standalone.</li>
<li>We decided it would be best if we installed it on the outside wall of his house, next to his driveway.</li>
<li>To keep the charging point visually clean, we would use a socket, not a cable.</li>
<li>Although his present car only has a one-phase internal charger, we decided to build a full three-phase charging point, since the difference in costs is minimal and we wanted to keep everything future-proof.</li>
<li>With 3x 20A distribution fuses, dynamic current control was a must.</li>
<li>He needs minimal access control, a switch in the house was enough.</li>
<li>There should be some easy way to charge only at night, when energy is cheaper. But there should also be an option to charge at any time to top up the car when needed.</li>
</ul>
<h2 id="design-choices">Design Choices</h2>
<p>After the parameters were set, it was relatively easy to decide on the final solution:</p>
<ul>
<li>I decided to use the <a href="https://github.com/kortas87/simple-evse">SimpleEVSE</a> controller to take care of the SAE J1772 standard signalling and control since I had good experience using it in the past.</li>
<li>I would use a ESPHome controller, connected to the WiFi to control the EVSE controller (current and time control, monitoring).</li>
<li>The charging point would have it’s own weatherproof switchboard on the outside wall, containing the fuse, relay, EVSE controller and the ESPHome controller. The socket would be built right next to it.</li>
<li>He had some space left in an auxiliary switchboard inside the house, so we would put the RCD switch there, so it can also serve as an access control switch to turn off the whole charging point when needed.</li>
<li>We would install a cheap smart electricity meter in the main switchboard to measure the currents on distribution fuses. Since this switchboard is on the other side of the house and wiring an extra data transfer cable to the charging point would be a problem, I decided to read it using a separate ESPHome controller connected to the home WiFi network.</li>
</ul>
<h2 id="the-actual-build">The Actual Build</h2>
<figure>
    <img src="/images/EV_charging/plan.png" alt="Electric diagram">
    <figcaption>The Plans</figcaption>
  </figure>
  
<p>In our case, the system is housed in two small boxes, but if your installation allows, you could connect the energy counter directly to the charge control ESPHome controller, saving yourself one extra ESPHome controller and getting better reliability.</p>
<h3 id="bom">BOM:</h3>
<h4 id="current-supervisor">Current Supervisor:</h4>
<ul>
<li>Wiring box as your situation requires</li>
<li>An energy meter with Modbus interface, e.g. <a href="https://www.ebay.de/itm/384473893449">this one</a> or <a href="https://www.ebay.de/itm/404221985277">this one</a></li>
<li>RS485 to 3.3V converter, e.g. <a href="https://www.ebay.de/itm/295361148016">this</a></li>
<li>ESP8266 or ESP32 module, e.g. <a href="https://www.ebay.de/itm/144123804853">this</a></li>
<li>A 5V power supply</li>
</ul>
<h4 id="charge-control-unit">Charge Control Unit:</h4>
<ul>
<li>Wiring box</li>
<li>RS485 to 3.3V converter, e.g. <a href="https://www.ebay.de/itm/295361148016">this</a></li>
<li>ESP8266 or ESP32 module, e.g. <a href="https://www.ebay.de/itm/144123804853">this</a></li>
<li>12V to 5V DC-DC converter, e.g. <a href="https://www.ebay.de/itm/255283194266">this</a></li>
<li>EVSE DIN controller, buy it <a href="https://shop.gwl.eu/Electric-Car-Chargers-1/EVSE-Kit-V1-1-For-EV-Charging-Station-Cable-Wallbox-Kit-Only.html">here</a>, the have many other parts for this project too</li>
<li>4-pole contactor, like <a href="https://shop.gwl.eu/Electric-Car-Chargers-1/EVSE-AC-Relay-4X40A.html">this one</a></li>
<li>A RCD, type B, like <a href="https://www.ebay.de/itm/293370848371">this one</a></li>
<li>A MCB (fuse), 16A B-type in our case</li>
<li>A 4P T2 surge protecting device (SPD), like <a href="https://www.ebay.de/itm/404534273552">this one</a></li>
<li>A type 2 socket, like <a href="https://shop.gwl.eu/Electric-Car-Chargers-1/EVSE-EV-Connector-EVSE-Mennekes-Type-2-Socket.html">this one</a></li>
</ul>
<h3 id="build-instructions">Build Instructions</h3>
<p>Use the schematic as your guide. Be careful about proper grounding, correct wire and component sizing, weatherproofing, wiring screw torques etc. DIN-rails are your friend.</p>
<p>You should have enough experience with electric projects to be able to build and customize this from the data I provided. If you don’t, it’s likely you are not able to build it safely on your own, so talk to people who understand it better.</p>
<h3 id="example-code-for-the-two-esphome-controllers">Example Code for the Two ESPHome Controllers</h3>
<h4 id="current-supervisor-1">Current Supervisor:</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">esphome</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">stevec</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">esp8266</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">board</span>: <span style="color:#ae81ff">d1_mini</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Disable logging to be able to use Modbus</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">logger</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">baud_rate</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">ota</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">wifi</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">networks</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—ssid</span>: <span style="color:#e6db74">&#34;ssid&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>: <span style="color:#e6db74">&#34;pass&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">web_server</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">port</span>: <span style="color:#ae81ff">80</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">uart</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">id</span>: <span style="color:#ae81ff">mod_bus</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">tx_pin</span>: <span style="color:#ae81ff">GPIO1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">rx_pin</span>: <span style="color:#ae81ff">GPIO3</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">baud_rate</span>: <span style="color:#ae81ff">9600</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">stop_bits</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">parity</span>: <span style="color:#ae81ff">even</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">modbus</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">id</span>: <span style="color:#ae81ff">modbus_stevec</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">send_wait_time</span>: <span style="color:#ae81ff">200ms</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">flow_control_pin</span>: <span style="color:#ae81ff">GPIO5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">modbus_controller</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—id</span>: <span style="color:#ae81ff">stevec</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">24</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_id</span>: <span style="color:#ae81ff">modbus_stevec</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">update_interval</span>: <span style="color:#ae81ff">5s</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">setup_priority</span>: -<span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">sensor</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">#Template sensor</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">template</span> <span style="color:#75715e">#Max phase current, will be written by lambda</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;TokMax&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">tokmax</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">#Readouts of individual phase currents from the smart meter</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">stevec</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">tokr</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;TokR&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">0x0008</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">unit_of_measurement</span>: <span style="color:#e6db74">&#34;A&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">register_type</span>: <span style="color:#e6db74">&#34;read&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">value_type</span>: <span style="color:#ae81ff">FP32</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">accuracy_decimals</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">filters</span>:
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—sliding_window_moving_average</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">window_size</span>: <span style="color:#ae81ff">6</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">stevec</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">toks</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;TokS&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">0x000A</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">unit_of_measurement</span>: <span style="color:#e6db74">&#34;A&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">register_type</span>: <span style="color:#e6db74">&#34;read&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">value_type</span>: <span style="color:#ae81ff">FP32</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">accuracy_decimals</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">filters</span>:
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—sliding_window_moving_average</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">window_size</span>: <span style="color:#ae81ff">6</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">stevec</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">tokt</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;TokT&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">0x000C</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">unit_of_measurement</span>: <span style="color:#e6db74">&#34;A&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">register_type</span>: <span style="color:#e6db74">&#34;read&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">value_type</span>: <span style="color:#ae81ff">FP32</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">accuracy_decimals</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">filters</span>:
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—sliding_window_moving_average</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">window_size</span>: <span style="color:#ae81ff">6</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">time</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">sntp</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">sntp_time</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">on_time</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Every 10 seconds do readout of max phase current</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—seconds</span>: <span style="color:#ae81ff">/10</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>         <span style="color:#f92672">—lambda</span>: |-<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              id(tokmax).publish_state((id(tokr).state &gt; id(toks).state) ? ((id(tokr).state &gt; id(tokt).state) ? id(tokr).state : id(tokt).state) : ((id(toks).state &gt; id(tokt).state) ? id(toks).state : id(tokt).state));</span>              
</span></span></code></pre></div><p>This is simple: We talk to the Modbus electricity meter every 10 seconds to get the three currents. We do a moving average to keep track of fast current changes. The result (largest average current) is available over HTTP API.</p>
<h4 id="charge-controller">Charge Controller:</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">esphome</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">evse</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">esp8266</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">board</span>: <span style="color:#ae81ff">d1_mini</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Disable logging to be able to use Modbus</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">logger</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">baud_rate</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">ota</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">http_request</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">useragent</span>: <span style="color:#ae81ff">esphome/ESP8266</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">timeout</span>: <span style="color:#ae81ff">10s</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">esp8266_disable_ssl_support</span>: <span style="color:#66d9ef">yes</span> <span style="color:#75715e">#to prevent OTA filing</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">id</span>: <span style="color:#ae81ff">http_request_data</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">wifi</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">networks</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—ssid</span>: <span style="color:#e6db74">&#34;ssid&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>: <span style="color:#e6db74">&#34;pass&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">web_server</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">port</span>: <span style="color:#ae81ff">80</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">uart</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">id</span>: <span style="color:#ae81ff">mod_bus</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">tx_pin</span>: <span style="color:#ae81ff">GPIO1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">rx_pin</span>: <span style="color:#ae81ff">GPIO3</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">baud_rate</span>: <span style="color:#ae81ff">9600</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">modbus</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">id</span>: <span style="color:#ae81ff">modbus_evse</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">modbus_controller</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—id</span>: <span style="color:#ae81ff">evse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">0x01</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_id</span>: <span style="color:#ae81ff">modbus_evse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">update_interval</span>: <span style="color:#ae81ff">30s</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">setup_priority</span>: -<span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">sensor</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">template</span> <span style="color:#75715e">#Max phase current, will be written by lambda</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;TokMax&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">tokmax</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">#EVSE sensors</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">evse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">set_charge_current</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">register_type</span>: <span style="color:#ae81ff">holding</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">1000</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">value_type</span>: <span style="color:#ae81ff">U_WORD</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Set Charge Current</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">unit_of_measurement</span>: <span style="color:#ae81ff">A</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">device_class</span>: <span style="color:#ae81ff">current</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">state_class</span>: <span style="color:#ae81ff">measurement</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">accuracy_decimals</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">evse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">vehicle_state</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">register_type</span>: <span style="color:#ae81ff">holding</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">1002</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">value_type</span>: <span style="color:#ae81ff">U_WORD</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Vehicle State</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">accuracy_decimals</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">evse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">ctrl_bits</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">register_type</span>: <span style="color:#ae81ff">holding</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">1004</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">value_type</span>: <span style="color:#ae81ff">U_WORD</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Control Bits</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">entity_category</span>: <span style="color:#ae81ff">diagnostic</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">disabled_by_default</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">evse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">evse_state</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">register_type</span>: <span style="color:#ae81ff">holding</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">1006</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">value_type</span>: <span style="color:#ae81ff">U_WORD</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">EVSE State</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">accuracy_decimals</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#Manual current setting slider</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">number</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">evse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">set_charge_current_input</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">1000</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">value_type</span>: <span style="color:#ae81ff">U_WORD</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Set Charge Current (Input)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">unit_of_measurement</span>: <span style="color:#ae81ff">A</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">min_value</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">max_value</span>: <span style="color:#ae81ff">16</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">step</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">use_write_multiple</span>: <span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">write_lambda</span>: |-<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      if (x &gt; 0 &amp;&amp; x &lt; 6) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        return 6;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      return x;</span>      
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#Switch for turning EVSE on or off. Note the negated function: switch on = EVSE off!</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">switch</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">modbus_controller</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">modbus_controller_id</span>: <span style="color:#ae81ff">evse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">disable_charging</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Disable Charging</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">use_write_multiple</span>: <span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">register_type</span>: <span style="color:#ae81ff">holding</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">2005</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">bitmask</span>: <span style="color:#ae81ff">16384</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#Template binary sensor for enabling/disabling the whole EVSE system, used for time scheduling to reduce electricity costs</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">template</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Enable</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">enable</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">restore_state</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">optimistic</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">turn_on_action</span>:
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—switch.turn_off</span>: <span style="color:#ae81ff">disable_charging</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">turn_off_action</span>:
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—switch.turn_on</span>: <span style="color:#ae81ff">disable_charging</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#Readouts and automation</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">time</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">sntp</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">sntp_time</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">on_time</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Every 30 seconds do readout of max phase current</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—seconds</span>: <span style="color:#ae81ff">/30</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>         <span style="color:#f92672">—http_request.get</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">url</span>: <span style="color:#ae81ff">http://stevec.local/sensor/tokmax</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">headers</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">Content-Type</span>: <span style="color:#ae81ff">application/json</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">verify_ssl</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">on_response</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>                 <span style="color:#f92672">—lambda</span>: |-<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      json::parse_json(id(http_request_data).get_string(), [](JsonObject root) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                        id(tokmax).publish_state(root[&#34;value&#34;]);
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      });</span>                      
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Every 5 minutes do current control</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">minutes</span>: <span style="color:#ae81ff">/2</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>         <span style="color:#f92672">—lambda</span>: |-<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              //Current limits of system
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              const float max_phase_current=20, max_charge_current=16, min_charge_current=6;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              //Only do current control if EVSE is ON
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              if (!id(disable_charging).state) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                //Only do current control if max phase current is too high or charge current setting is reduced (plus offset to prevent corrections under 1A, which have no effect)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                if ((id(tokmax).state &gt; (max_phase_current + 0.5))||(id(set_charge_current).state &lt; max_charge_current)) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                  //If max phase current is too high (plus offset)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                  if (id(tokmax).state &gt; (max_phase_current + 0.5)) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                    //If current is low enough to limit properly
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                    if (id(tokmax).state &lt; (max_phase_current + id(set_charge_current).state—min_charge_current)) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      //Reduce current setting by exactly the excess current
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      auto call = id(set_charge_current_input).make_call();
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      call.set_value(id(set_charge_current).state—(id(tokmax).state-max_phase_current));
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      call.perform();
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                    } else {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      //Turn off EVSE to cool everything off
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      id(disable_charging).turn_on();
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                    }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                  } else if (id(set_charge_current).state &lt; max_charge_current) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                    //Increase current setting by exactly the unused current capacity
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                    if ((id(set_charge_current).state—(id(tokmax).state-max_phase_current)) &lt; max_charge_current) {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      auto call = id(set_charge_current_input).make_call();
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      call.set_value(int (id(set_charge_current).state—(id(tokmax).state-max_phase_current)));
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      call.perform();
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                    } else {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      //Or set maximum charge current
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      auto call = id(set_charge_current_input).make_call();
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      call.set_value(max_charge_current);
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                      call.perform();
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                    }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                  }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              }</span>              
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Every 15 minutes turn EVSE back on</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">minutes</span>: <span style="color:#ae81ff">/15</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>         <span style="color:#f92672">—if</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">lambda</span>: |-<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">                  return id(enable).state;</span>                  
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>               <span style="color:#f92672">—switch.turn_off</span>: <span style="color:#ae81ff">disable_charging</span>
</span></span></code></pre></div><p>This is the main functionality. We set up the Modbus communication with the EVSE controller and prepare the necessary variables to control it. My neighbor did not want to have the whole Home Assistant setup, so the control works via a slider in the inbuilt web server.</p>
<p>The control principle is simple – if the average max current exceeds the permitted maximum, the charging current is set to a lover value. E.g. with 20A limit, if the actual phase current is 22A and the set charging current is 16A, the charging current is reduced by 22-20 = 2A, e.g. to 14A. If there is headroom (a big load has disconnected), the charging current is set higher in the same way. This repeats every 5 minutes.</p>
<p>In extreme cases, where the charging current would have to be reduced under 6A (minimum for the EVSE standard), the charging point is turned off until the next 15 minute interval. This should cool off the fuses enough. Keep in mind that normal  fuses should hold a significant overload for tens of minutes (standard requirement is: blow in less than one hour for 45% overload, over one hour for 13% overload).</p>
<p>All that is left is a simple option to turn the whole thing on or off via a switch in the web server or an API call.</p>
<h2 id="final-notes">Final Notes</h2>
<p>This was intended as an illustration of how to build a safe EV charging point with dynamic current control. There are literally thousands of ways how you could go about achieving this, but the general outline will always be the same:</p>
<p><strong>Measure current coming into your home, compare to limit, set charging current, repeat. Turn off EVSE for 15 minutes, if load is too damn high 😉</strong></p>
<p>This could be thought of as trivial, but there is an important point here: if you buy a ready-made charging point, it needs to dynamically control the charging current! If you buy a “dumb” charging point, you are going to be paying for blown fuses at least some of the time and extra power capacity all the time.</p>
<p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item><item><title>DIY EV Charging Point Basics</title><link>https://nunrg.eu/posts/diy_ev_charging/</link><pubDate>Mon, 20 Feb 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/diy_ev_charging/</guid><description>How to build your own EV charging point (EVSE): the basics of EV charging, connectors and a simple DIY design for home use.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/diy-ev-charging-point-basics-f0f5b32d1695">reading this article on Medium</a></p>
<h2 id="why-diy-an-ev-charging-point">Why DIY an EV-charging Point?</h2>
<ul>
<li>It’s fun to build things yourself 🎉</li>
<li>You can save some money 💰</li>
<li>You have so much freedom: how it looks, how it works, it’s almost entirely up to you 🤓</li>
</ul>
<h3 id="disclaimerdisclaimer"><a href="#disclaimer">Disclaimer</a></h3>
<figure>
    <img src="/images/electrical-safety-warning-sign.png" alt="Electrical safety warning sign">
    <figcaption>Adults only beyond this point!</figcaption>
  </figure>
  
<p>Unless you know how to DIY some serious electrical installations on your own AND have done that before, DO NOT start building a charging point. A short list of things that could go wrong: fire, electrocution, blown mains fuses, blown built-in car charger…</p>
<p><strong>Adults with electrical engineering background only!</strong></p>
<h2 id="what-to-build">What To Build?</h2>
<h3 id="just-a-charging-cable">Just a Charging Cable</h3>
<figure>
    <img src="/images/EV_charging/type1.png" alt="A Type 1 charging cable">
    <figcaption>A Type 1 charging cable, image credit: Metron</figcaption>
  </figure>
  
<p>It’s hard to make a reliable current limiting solution this way, so it’s probably not worth the extra effort – just buy a standard one.</p>
<p>But it can be a worthwhile option if you need to repair a dead charging cable. Just buy a <a href="https://github.com/kortas87/simple-evse/wiki">SimpleEVSE controller</a>, a 2-pole relay, and a power supply and put it in the old charging cable casing. Please don’t forget to put a Type A RCD on the supply side cable to keep it safe – you never know what’s behind a socket you did not install yourself.</p>
<h3 id="a-full-blown-type-2-charging-point">A Full-blown Type 2 Charging Point</h3>
<p>This is where all the options are: current limiting, automation, access control, visual design, cable v. socket, integration, automation…</p>
<h2 id="options">Options</h2>
<h3 id="current-limiting">Current Limiting</h3>
<p>I think it’s not an option, actually. Either:</p>
<ul>
<li>You risk blowing the distribution fuses every time you forget you are charging a car and you turn on an electric oven or another large load for more than a few minutes.</li>
</ul>
<p>Or:</p>
<ul>
<li>
<p>You pay through the nose for bigger distribution fuses.</p>
<p>Where I live, the standard option is 230V, 3x 20A. Most EVs now have 3x 16A or larger built-in charger. This gives you 4 amps of headroom. Any significant appliance like an oven, washing machine, vacuum, cooking plate, microwave, etc. puts you in the danger zone. So, if you want to be sure, you need at least 35A fuses. This is costly for several reasons:</p>
<ul>
<li>
<p>First of all, bigger fuses directly increase your electric bill even before you use any extra power. In EU, the difference will be on the order of several hundred euros per year.</p>
</li>
<li>
<p>You will have to pay for the change and take care of the paperwork.</p>
</li>
<li>
<p>Your existing house connection likely does not have big enough cabling so you need to upgrade this as well.</p>
</li>
<li>
<p>Your local transformer station could already be loaded to the limit and you can’t even get the extra capacity even if you are willing to pay for it.</p>
</li>
</ul>
</li>
</ul>
<p>The downside to current limiting is a slightly more complicated installation and charging will sometimes take a little longer when you have big loads turned on in your house.</p>
<h3 id="cable-or-socket">Cable or Socket?</h3>
<p>My preference is clear: use a socket.</p>
<figure>
    <img src="/images/EV_charging/type2socket.png" alt="Type 2 socket">
    <figcaption>Type 2 socket, image credit: Digitehnika</figcaption>
  </figure>
  
<p><strong>Pros</strong>: It looks cleaner (no “elephant trunk” on your wall), it’s easier to weatherproof, and every EV owner owns a Type 2 charging cable.</p>
<p><strong>Cons</strong>: You have to take the cable out of your car before you plug it in. But when you come home, you usually have to take groceries etc. out of the car anyway, so it’s not a big deal.</p>
<figure>
    <img src="/images/EV_charging/type2cable.png" alt="Type 2 Cable">
    <figcaption>Type 2 charger with connecting cable AKA the Elephant’s Trunk, image credit: Amazon</figcaption>
  </figure>
  
<h3 id="access-control">Access Control</h3>
<figure>
    <img src="/images/EV_charging/access_control.png" alt="RFID Reader">
    <figcaption>RFID Reader</figcaption>
  </figure>
  
<p>I think most people reading this own a house and are thinking about building a charging point of their own in their own driveway or garage. If this is the case, you don’t need access control unless you live in a really rowdy neighborhood. Let’s face it, most people will not park a car in a driveway belonging to someone else, much less hook up a charging cable unless they REALLY need it. Even if they do, this will cost you a few euros at most. If you are in this situation, you can probably afford a few euros – we should look out for each other, when we can.</p>
<p>Nevertheless, if you need access control, it’s quite easy to arrange and you have several options:</p>
<ul>
<li>The standard solution seems to be RFID. This is an easy option to implement, you just need a RFID reader:
<ul>
<li>Most RFID-readers have the option to simply hard wire an output to the device you want to control. In this case, any EVSE controller should have an input pin to control. Just program your reader, connect the output to the controller and you are done.</li>
<li>You could use the <a href="https://github.com/CurtRod/SimpleEVSE-WiFi">SimpleEVSE-Wifi</a> solution – a simple ESP8266 board you can connect your RC522 RFID-Reader to. It takes care of access control and many other things as well.</li>
</ul>
</li>
<li>You could control the charging station via your <a href="https://www.home-assistant.io/">Home Assistant</a> system. A simple switch there can turn your charging point on or off, but you can also do so much more – time control, control according to your photovoltaic plant production, the possibilities are endless.</li>
<li>Just wire a simple key-controlled switch, hooked to the enable input of your EVSE controller.
Install a switch inside your home and connect it to your EVSE controller. It could even be just your RCD switch. Turn on when needed.</li>
</ul>
<h2 id="what-hardware-to-use">What Hardware To Use</h2>
<h3 id="electrical-components">Electrical Components</h3>
<figure>
    <img src="/images/EV_charging/RCD.png" alt="MCBs, RCDs, etc.">
    <figcaption>Image credit: ABB</figcaption>
  </figure>
  
<p>Any charging point needs these at a minimum:</p>
<ul>
<li>
<p><strong>An RCD protection switch, type B.</strong></p>
<p>This is expensive, but important! A regular RCD (type AC) could fail to protect you in case your car charger fails in a way that makes the fault current non-sinusoidal (e.g. leakage on only one leg of the rectifier). A type B RCD is a more expensive, but it will reliably trip for DC fault currents as well, which are unlikely, but possible in case of catastrophic damage of the built-in charger in the EV. It is also required by the relevant standard (IEC 60364-7-722:2018). Current capacity should be equal to or larger to your fuse rating. Trigger current should be according to your country standard (30mA for me).</p>
</li>
<li>
<p><strong>A fuse.</strong></p>
<p>A regular installation fuse with the same nominal current you want the charging point to have, type A or B (no need for starting current protection).</p>
</li>
<li>
<p><strong>A relay.</strong></p>
<p>A relay (or a contactor) that breaks all the phases AND the neutral. E.g. for a 3-phase charging point you need a 4-pole relay. Nominal relay current should be at least 120% of the nominal fuse current. (E.g. for a 16A charging point, use at least a 20A relay.)</p>
</li>
<li>
<p><strong>A surge protection device.</strong></p>
<p>An over-voltage protection device, type 2, 2-pole for 1-phase and 4-pole for 3-phase charging points. If your car is not protected from lightning (e.g. away from the building, no roof, etc.), use T1+T2.</p>
</li>
<li>
<p><strong>An enclosure.</strong></p>
<p>It’s best to put all of these in an enclosure with a DIN rail. If the enclosure is exposed to the elements, it needs at least an IP55 rating. Some people prefer to put their RCD switch inside the house, so they have an easy way to turn off the entire charging point there as a kind of access control.</p>
</li>
<li>
<p><strong>Cable or socket.</strong></p>
<p>Also expensive, but you don’t want to skimp here, since it is subject to wear. I usually put it next to the enclosure and build an extra sturdy bracket for it – it takes some force to plug and unplug the Type 2 cable.</p>
</li>
</ul>
<h3 id="the-evse-controller">The EVSE Controller</h3>
<figure>
    <img src="/images/EV_charging/simpleEVSE.png" alt="The SimpleEVSE controller">
    <figcaption>The SimpleEVSE controller, image credit: GWL</figcaption>
  </figure>
  
<p>You <a href="#disclaimer">could</a> implement everything using a simple ESP8266 board and some peripherals. But unless this is something you dream about doing, I would very much recommend you go modular: Buy a proven EVSE controller to do all the SAE J1772 standard signalling and control (pilot signal, current control, relay control, enable/disable).</p>
<p>A good option is the <a href="https://github.com/kortas87/simple-evse">Simple EVSE open source project</a>. You can build it yourself or buy a built module at <a href="https://shop.gwl.eu/">GWL</a>. I built three chargers using their kit already and can recommend them (they have sockets, cables, relays, etc. too). But there are other options too: <a href="https://github.com/OpenEVSE">OpenEVSE</a>, <a href="https://github.com/sebdehne/DehneEVSE-Firmware">DehneEVSE</a>, <a href="http://www.evse-wifi.de/">smartWB</a>, and others.</p>
<p>You could also buy a ready-made charging point – you can now buy them at hardware stores. But you will a) overpay for the components you get and b) lose a lot of flexibility. Always go open-source when you can, it will save you trouble in the long run (when the supplier decides to stop supporting it).</p>
<h3 id="the-charging-point-controller">The Charging Point Controller</h3>
<p>The EVSE controller will take care of the signalling and turning the relay on or off. But what about current control, access control, automation, supervision, energy metering, cost counting etc.? This is where it gets interesting. You have many options:</p>
<h4 id="no-extra-controller">No Extra Controller</h4>
<p>If you don’t want any of the above options, just build a dumb charging point. You need a RCD, fuse, EVSE controller, relay and a socket/cable. It will work, but read about the options to understand why this may not be a good idea.</p>
<h4 id="ready-made-integrated-solution">Ready Made Integrated Solution</h4>
<p>Some solutions, like OpenEVSE and smartWB, already take care of some of these options. In essence, they contain both the EVSE controller and the charging point controller. But in my opinion the extra cost is not worth it now that we have ESPHome to play with. Just read their githubs and send these awesome guys some beer money via the donation link, they deserve it.</p>
<h4 id="simple-evse-wifihttpsgithubcomcurtrodsimpleevse-wifi"><a href="https://github.com/CurtRod/SimpleEVSE-WiFi">Simple EVSE-WiFi</a></h4>
<p>A simple ESP8266-based solution with an in-built web server for the Simple EVSE charging controller that will take care of:</p>
<ul>
<li>Default functions:
<ul>
<li>Turning the charging point on or off</li>
<li>Setting the charging current</li>
<li>Charge logging</li>
<li>Control of the SimpleEVSE charging controller registers</li>
<li>HTTP API for automation</li>
</ul>
</li>
<li>Optional functions:
<ul>
<li>With added RC522 RFID-Reader:
<ul>
<li>Authentication</li>
<li>User management</li>
</ul>
</li>
<li>With an added S0 or ModBus energy meter:
<ul>
<li>Measuring charging power</li>
<li>Metering charged energy (included in log)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>You will need to control the current via the API if you want current limiting. This is easily achievable e.g. in Home Assistant, where you probably already have a smart energy meter integrated. Use the REST service integration.</p>
<h4 id="esphomehttpsesphomeio"><a href="https://esphome.io/">ESPHome</a></h4>
<p>A truly versatile solution that works with an ESP8266 or ESP32 board. It is extremely convenient, you simply program your ESP board via USB once and then you can change the configuration any time using OTA via WiFi. You simply change the YAML file and run the upload program.</p>
<p>The magic of ESPHome is it can work standalone very well, but it is also EXTREMELY easy to integrate into Home Assistant. Whatever hardware you choose to integrate into your charging point (energy meter, sensors, RFID, display, buzzers, LEDs, etc.), you simply add to the YAML configuration file using provided integrations. You can then link these directly using automations within ESPHome or indirectly in Home Assistant.</p>
<h2 id="conclusion">Conclusion</h2>
<p>These are some of the options and things you need to know if you want to build a homebrew EV charging point. In the <a href="https://nunrg.eu/posts/example_diy_charging_point/" title="Example DIY EV Charging Point">next post</a>, I present an example solution using ESPHome that includes current limiting and works standalone.</p>
<p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item></channel></rss>