<?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>Posts on Making Clean Tech</title><link>https://nunrg.eu/posts/</link><description>Recent content in Posts on Making Clean Tech</description><generator>Hugo -- 0.136.5</generator><language>en-us</language><copyright>2024 Franci Kopač</copyright><lastBuildDate>Tue, 30 Dec 2025 07:39:37 +0100</lastBuildDate><atom:link href="https://nunrg.eu/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>A Meaningful Concept of Faith from a Rational Perspective</title><link>https://nunrg.eu/posts/practical-faith/</link><pubDate>Tue, 30 Dec 2025 07:39:37 +0100</pubDate><guid>https://nunrg.eu/posts/practical-faith/</guid><description>A rational, STEM-minded look at faith and religion: what devotion and community do for us, and how to make sense of it without self-deception.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa">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>
<figure>
    <img src="/images/academy.jpg" alt="The fresco depicts a congregation of ancient philosophers, mathematicians, and scientists, with Plato and Aristotle featured in the center.">
    <figcaption>The School of Athens, by Raphael — Stitched together from vatican.va, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4406048</figcaption>
  </figure>
  
<p>As an 48-year-old guy from a STEM background who tries to do the minimal-lying-to-yourself thing, I’ve always been curious about the whole faith-spirituality-devotion-religion-deeper-truth thing. It appears to be important; for one thing, it’s supposed to help you live longer; then there is the fact that over half of the world population seems to take it very seriously. So how do people seem to approach this?</p>
<p>It seems like there are three main directions:</p>
<ul>
<li>The organized religion, which, let’s face it, does not give us much space to think on our own AND it usually pushes us to think of people outside our group as the Other.</li>
<li>The New Age thinking, which is more accepting of otherness, at least on some fronts, but is suffused with magical thinking and requires us to leave our reason at the door too.</li>
<li>Accepting that there is no deeper meaning and we are simply individuals left to fend for ourselves; in other words, nihilism. It looks reasonable, but it is both deeply unsatisfying and lonely. It is also a betrayal of a fundamental truth: we are both social beings and a part of nature. Alienating yourself from the whole living planet, including the 8 billion other humans out there, appears to be the surest possible way to get mentally ill. Only far-right politicians and Amazon-like corporations could ever profit from that.</li>
</ul>
<p>It looks like there is no good way out of this one, is there? You can drink the Kool-Aid and enjoy the community, along with the taste of biting your tongue every so often; some people let go of their individuality entirely. Most of us know people who have done one or the other. Or you can rely on your reason to guide you but basically be on your own forever.</p>
<p>Fortunately, like any good dichotomy, this one is false too. But first, let’s define some words and talk about why faith is relevant for rational people.</p>
<h2 id="what-is-faith">What is Faith?</h2>
<p>When talking about things, it’s good to try to define them at least a bit. There are no bulletproof definitions, but thinking about the meanings of words is usually helpful. This discussion revolves mainly around three things: beliefs, faith, and religion. Let’s try to define these at least a little:</p>
<p>Very briefly, beliefs are what we think is true. Some beliefs are very down-to-earth, everyday things, like believing that brushing teeth is good for you. These are mostly easy to prove or disprove rationally, and they shape our everyday decisions. But some beliefs are unprovable, based on gut feelings or compelling stories. Some are placed in our cradle as we learn to talk and be human, like beliefs of organized religions and national identities. Some are marketed to us skillfully enough to make them our own; brands are a good example. And, every so often, there is no other way but to go with our gut feeling; for example, when we need to decide whether we should trust someone.</p>
<p>Some beliefs become important enough to take a special place in our minds. For example, if you decided to trust that particular person and it worked well for a long time, this belief grows into something more. It becomes faith, a default, something you no longer think about; there is no more need for proof; it’s simply the truth. Many people see faith as something connected to organized religion, but it is both deeper and simpler than that—it is a belief that sat in our mind, unchallenged and compelling, for long enough to become unassailable. An example would be the faith that every human being has intrinsic value, shared among humanists, for example, but other believers as well.</p>
<p>When you have faith in several things at once and start connecting them, seeing in them a deeper combined truth (a system, if you will), this is religion. Again, most people see this term through the lens of organized religion, but it can be entirely secular too, like the Humanist Manifesto. You could say non-organized religion is spirituality.</p>
<h2 id="why-faith-can-be-good-for-you">Why Faith Can Be Good for You</h2>
<p>Faith, as we’ve seen, can be (there are meaningful exceptions) associated with positive life outcomes like better mental and physical health, as well as a longer life. But why?</p>
<p>From what I’ve read and thought about this, most of the positive effects of faith seem to come from two sources: social inclusion and decreased uncertainty. These sources are clearly intentionally addressed by most organized religions: they will ask you to attend religious services and to be charitable, both of which can be a real celebration of community; they will give you a mostly clear set of rules to follow, eliminating much of the uncertainty we face; and they will define clear devotional practices like prayer, fasting, or pilgrimages, often bringing a helpful routine into your life.</p>
<p>It is precisely the concept of devotional practices that interests me the most here, but we will come back to that later.</p>
<p>At this point it should be helpful to mention the concept of fast and slow thinking, as defined by Kahneman, among others. I will oversimplify: we use two mechanisms to think. Most of the time, we use intuitive, fast thinking. Some call it “the gut instinct”; Kahneman calls it System 1. But every so often the intuitive answer is not good enough, and we have to take the time to deliberate the question rationally; he calls this System 2. To me, this connects beautifully with beliefs and faith:</p>
<p>When faced with a situation about which we have an existing belief or even faith, System 1 fires immediately, and the answer is instantly available. There is no need to waste any time or think any longer; you can just deal with it. In the same situation with no strong beliefs, there will be no strong System 1 response, and we have to start thinking.</p>
<p>Now imagine a really difficult situation, maybe a traffic accident, a bereavement, or a relationship coming to an end. When faced with difficult choices and bad feelings, some people will be primed with relevant beliefs or faith, and some will not. For people primed in this way, these situations trigger the System 1 response; the response will be fast and usually fortified by emotion; they will probably deal with it faster. Others will be forced to fall back on System 2 and think long and hard about what is happening and how to respond. This may cause them to react too late and will definitely deepen the impact of the situation on them.</p>
<p>So, beliefs and faith can have a really positive effect on your life, BUT, and this is a ten-ton rocket-powered “but” on steroids, ONLY if your beliefs and faith are good for you. If you are primed by homophobic teachings of some religion, you will react terribly around gay people, causing pain for them and ostracism for you (hopefully). If you believe that vaccines cause autism, you will likely expose your children to easily preventable diseases. If you believe you are not good enough as a human being, it will be really difficult to have healthy relationships.</p>
<p>Examination is the only cure for harmful beliefs. By thinking about what we did wrong in a situation or being prompted to reexamine things in a conversation, we open the door to change. Of course, changing beliefs is far from easy, but still a picnic compared to changing our faith. So how to do it?</p>
<h2 id="about-empowerment">About Empowerment</h2>
<p>In The Sovereignty of Good, Iris Murdoch gives us this gem:</p>
<blockquote>
<p>… if we consider what the work of attention is like, how continuously it goes on, and how imperceptibly it builds up structures of value round about us, we shall not be surprised that at crucial moments of choice most of the business of choosing is already over. This does not imply that we are not free, certainly not. But it implies that the exercise of our freedom is a small piecemeal business which goes on all the time and not a grandiose leaping about unimpeded at important moments.</p>
</blockquote>
<p>I understand it as a brilliant clarification of the age-old maxim “You are what you do.”</p>
<p>As we go about our lives, we do things all the time. We cook dinner for our family, we get up in the morning for work, and we go to the gym or the church or the supermarket. Each time you do something, this becomes easier—doing it shapes you just a little and makes that same act easier to do tomorrow.</p>
<p>But this process is not inherently positive. If you devote a little time every day to help your neighbors, it will become a habit, and everybody involved will be happier for it. If you try to exercise regularly, it will get easier each time, and you will probably be much healthier—but some people will, over time, overdo it to the point of injuries. If you spend hours each day scrolling hateful content on social media, it will become an effortless thing to spout hate at others, even harm them. We are what we do.</p>
<p>So if we want to have better beliefs and steer our lives more intentionally, we have to work on it.</p>
<p>But how?</p>
<p>In some cases, it’s pretty straightforward: If you wanted to get in shape, most people would join a gym and mark it on their calendar. If you hope to be a programmer, you have to dig into the books and start programming; it will take a while, but you can get there with enough practice. And you need to do a LOT of writing to be a decent writer.</p>
<p>Then there are the hard problems: How to live authentically? How to be a better partner? How to escape a feeling of loneliness?</p>
<p>So we start to think about it: What does it mean to be a better partner?</p>
<p>Is it better cooking, better communication, better sex, or doing more of the everyday chores? Probably all of it.</p>
<p>So our thinking deepens: Chores are easy, but how can I be a better cook? Trying some new recipes and cooking more often would surely help. And reading a good book about partner communication should definitely help in that area, as well as setting aside some time to just talk. Better sex requires good communication, and it can’t hurt to read about techniques and positions either.</p>
<p>But is that all? Can we become better partners by just making a to-do list and ticking things off until we get there?</p>
<p>I think most people will agree that it is a good start, but it’s certainly missing something. And here we can borrow from organized religion:</p>
<h3 id="devotional-practices">Devotional Practices</h3>
<p>Organized religions have well-defined devotional practices: prayers, worship, contemplation, fasting, pilgrimages, and offerings. Most of these have some things in common: you are supposed to do them regularly, they cost something—at least some time, but often overcoming some physical discomfort or making a material sacrifice too—and they are devoted to the object of faith. This is a remarkably consistent pattern for many religions all over the world; it must mean something.</p>
<p>It is a well-known rule that we value things more if they cost us something. So this part is clear—the cost of these practices gives them value in our minds.</p>
<p>Doing things regularly makes sense too—repetition or routine makes whatever we are doing into a habit, an automatic thing; we are what we do.</p>
<p>Devotion is the odd one out—it&rsquo;s not something a non-religious person is used to in our everyday lives. What does it mean to devote a practice to something? For me, this means thinking about the what and the why while we are doing it and doing it like we mean it, knowing it’s worth the effort because we thought about it well and decided to do it.</p>
<p>A religious person can recite certain words at a certain time while in a certain position, but that is not yet enough. Add devotion, and you get a prayer. Thinking about who or what you are praying to—imagining it—makes that something a little more tangible. Do it a few thousand times, and your religious faith will become a part of you.</p>
<p>But how is this useful for a non-religious person?</p>
<p>Consider our previous example, wanting to be a better partner and deciding to work on it: How do you do it with devotion?</p>
<p>Every day, as you start cooking, possibly a bit tired and bored, you could first try to remind yourself that you are doing this intentionally—you decided to be a better cook. You could then try to imagine your partner or children enjoying their meal. You could attempt to make this particular meal just a little special. If nothing else, you should have more fun doing it.</p>
<p>Then, when it’s time to just talk—maybe when the children go to bed—try to remind yourself that you want your relationship to thrive. You decided to make it work. You are both tired and probably a bit cranky, but remember, pushing yourself just a little makes the time you have together more significant.</p>
<p>And in the end, being a better lover is about deciding to be there and attempting to feel the other person as well as yourself. It’s deciding to do what feels right for both of you, imagining the ecstasy, and then making it happen together; this has to be one of the purest forms of devotion there is.</p>
<p>And then another day comes, and you have to do it all again.</p>
<p>Effort, repetition, and devotion are a way to build our rational choices into what we are. We start by thinking about what we want and then use these three principles to embed these choices into our automatic thinking mechanisms. This helps us make the right choices faster and to feel confident as it happens. Thus we reshape ourselves and make it easier for us to be and do better. We take the rational choices and turn them into faith.</p>
<p>But it’s worth addressing failure too. Deciding to empower yourself means a lot of work; you have to do it over and over again. There will be setbacks, even outright failures, but that’s ok; it happens to everyone. The only thing that helps is to rest for a while, think for a bit, and then try some more.</p>
<h2 id="conclusion">Conclusion</h2>
<p>For me, empowerment is the ability to think about, decide to do, and then do the thousands of little things that need doing, intentionally and with devotion.</p>
<p>Of course, this is not at all trivial; it takes time and effort; occasional failure is a certainty. But you decided to do it, so you might as well keep at it. It will certainly get easier with time and eventually become a part of you. It will be a spring of faith in yourself, the people you are doing it with, and what you are striving for. It will make you resilient and give you meaning and connection.</p>
<p>It is the only thing any kind of meaningful freedom could ever grow from.</p>
<h2 id="acknowledgment">Acknowledgment</h2>
<p>This is a heavily condensed essay, intended to convey a quite complex concept that took some time to think through. And what got me on this path was the excellent Philosophise This! podcast, specifically episodes 211 through 223—I cannot recommend it enough!</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>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>Solar System Monitor Using Three Analog Dials On E-paper</title><link>https://nunrg.eu/posts/epaper-servo-gauge/</link><pubDate>Thu, 20 Mar 2025 12:58:48 +0100</pubDate><guid>https://nunrg.eu/posts/epaper-servo-gauge/</guid><description>A solar-system monitor that shows live data on three analog-style dials drawn on an e-paper display, driven by an ESP32.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/solar-system-monitor-using-three-analog-dials-on-e-paper-fc45434353f0">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>Last fall I came across an interesting article in <a href="https://hackaday.com/2024/11/25/e-ink-screen-combined-with-analog-dial-is-epic-win/">Hackaday</a>, where <a href="https://arnweb.nl/gitea/arne/EinkAnalog">Arne van Itersen</a> made an interesting solar power display with an analog gauge and an e-paper screen.</p>
<p>It immediately grabbed my attention because it solves the biggest problem with e-paper displays—they need a few seconds to refresh, making them unsuitable for displaying continuously changing data like solar power. A simple analog needle takes care of that problem but is limited to displaying one thing only. But if you put the needle in front of an e-paper screen, you get continuous power information from the needle. You can then use the e-paper display for displaying things that only change every few minutes, like storage battery level, daily production, and system operation mode.</p>
<p>But using an analog dial from a multimeter seemed a bit fiddly to me, and I thought about how to improve on that. Using a small servo instead would be better, because I could use needles of any size. Controlling the servos is quite simple too, I needed the ESP32 to control the e-paper display anyway. It took a weekend to put together a simple prototype for a one gauge display, which I used for my DIY EV-charger.</p>
<figure>
    <img src="/images/epaper/1semafor.jpg" alt="EV-charger display using a needle over an e-paper display">
    <figcaption>The first try, a 1-gauge display for my EV charger</figcaption>
  </figure>
  
<p>The thing worked like a charm and got me thinking about how to improve it. I was thinking about updating my solar system display for a while now, and this seemed like a good solution.</p>
<h2 id="thinking-about-the-design">Thinking About The Design</h2>
<p>One needle wasn&rsquo;t going to cut it this time—I wanted one for solar power, one for power coming from the grid, and one for the power going into the house. After thinking about it for a while, I decided to put the grid and house power servos on the left and right side of the display. The solar servo would be on the bottom. This provides some spatial logic too—power flows from the grid (left) or solar (bottom) to the house (right). I guess putting the solar need on top could also work, but that didn&rsquo;t look right to me for some reason.</p>
<p>I wanted to put the display outside, where it would be exposed to the elements, so it needed a weatherproof housing. So I had to think a bit on how to make a housing that would be both weatherproof and easy to assemble. It&rsquo;s easy to build a two part shell, but how to design it in a way you can assemble and then disassemble it easily while keeping it watertight? I decided to approach it differently. I designed a mounting plate for the servos and the e-paper display first and gave it a flange on the sides and on the bottom. Then I designed a one-piece housing with a window and a simple top cover. This housing had simple guides to hold the flanges of the mounting plate. The window can be closed using some acrylic glued with silicone to make it weatherproof. The top cover is designed to have rainproof downslopes and only needs a drop of superglue on each side to keep it in place. This makes the assembly and testing really simple, you can do everything with the mounting plate alone.</p>
<figure>
    <img src="/images/epaper/mountplate.jpg" alt="The mounting plate with installed servos, e-paper display, and connected ESP32">
    <figcaption>The assembled mounting plate, ready for testing, only the needles are missing</figcaption>
  </figure>
  
<h2 id="the-software">The Software</h2>
<p>I&rsquo;m used to Arduino, so this is what I used. E-paper in Arduino is IMO best used with the GxEPD2 library, which includes the Adafruit GFX functions, the servos are controlled using the Servo library, data exchange is done using Adafruit MQTT, and network connectivity uses WiFi.</p>
<p>The program is subscribed to several MQTT topics, containing the values for grid power, house power, solar power, solar production, battery level, and system mode. Each of these topics is linked to a callback function that triggers on topic updates. The callback functions for grid, house and solar power all move the respective servo needles. The callbacks for battery level and solar production only update the relevant global variables. Screen refresh is only directly triggered by a system mode topic change or every 10 minutes, but this updates the display with the latest data stored in the globals.</p>
<p>Startup makes a full sweep of all the needles, serving as a kind of servo calibration check, then starts the main loop. The main loop is really short and only keeps the MQTT connection alive and resets the whole thing if Wi-Fi drops.</p>
<h3 id="getting-the-data-to-the-topics">Getting the Data To the Topics</h3>
<p>The one remaining job on the software side was to get the data from my solar system to the MQTT topics. The solar system is connected to my Home Assistant installation, so it was really easy to set up some automations that publish the relevant data to MQTT topics when the relevant sensor states change. I used state changes for triggers and the MQTT Publish service to send the data to the MQTT topic. I already had a MQTT broker set up for my other projects, so I just used that one.</p>
<h2 id="build">Build</h2>
<p>Once I was satisfied with the results, it was plain sailing:</p>
<ul>
<li>3D-print the enclosure using ABS</li>
<li>Cut the acrylic to size and glue it to the enclosure using some silicone glue</li>
<li>Drill a hole for the power cable on the appropriate spot, then use some mounting glue to fix the enclosure to the wall</li>
<li>Connect the power and insert the mounting plate in the housing.</li>
</ul>
<figure>
    <img src="/images/epaper/3semafor.jpg" alt="The finished and closed enclosure">
    <figcaption>The end product on a sunny day while charging my EV. Note to self—it&#39;s really easy to scratch acrylic with screw heads 🤦‍♂️</figcaption>
  </figure>
  
<p>You can find all the relevant code and files on <a href="https://github.com/FrenkK/servo-epaper-gauge">Github</a></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>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>A Minimalist Digital Photo Frame Using 7-color E-paper</title><link>https://nunrg.eu/posts/epaper_7color/</link><pubDate>Wed, 13 Nov 2024 12:12:09 +0100</pubDate><guid>https://nunrg.eu/posts/epaper_7color/</guid><description>How I built a minimalist digital photo frame using a 7-colour e-paper display, a Raspberry Pi and Floyd-Steinberg dithering.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/a-minimalist-digital-photo-frame-using-7-color-e-paper-9dacde085464">reading this article on Medium</a></p>
<p><img loading="lazy" src="/images/epaper/7color.jpg" alt="Picture of the finished device"  />
</p>
<p>When researching the e-paper displays for the art project described <a href="https://nunrg.eu/posts/lowpowerepaper/">here</a>, I bought a <a href="https://www.waveshare.com/5.65inch-e-paper-module-f.htm">Waveshare ECP 600x488 7-color E-paper Display</a> (just a normal link, I&rsquo;m not making any money with this), but ended up never using it. I really try to avoid stuff piling up in my drawers, so I decided to use an old (vintage: 2011 😀) Raspberry Pi 1B I had lying around too and build a simple photo frame.</p>
<h2 id="using-raspberry-pi-to-control-the-e-paper-display">Using Raspberry Pi To Control the E-paper Display</h2>
<p>First, <a href="https://www.tomshardware.com/reviews/raspberry-pi-headless-setup-how-to,6028.html">set up a headless Pi</a> using Raspberry Pi OS Lite.</p>
<p>Next, you have to wire the display to the Pi header. The <a href="https://www.waveshare.com/wiki/5.65inch_e-Paper_Module_(F)_Manual#Working_With_Raspberry_Pi">Waveshare Wiki</a> instructions are pretty good and the setup should work out of the box for most people.</p>
<p>I had to make a few changes for my setup, because I wanted to use my old Raspberry Pi 1B to keep it from the e-waste bin. But it has different GPIO pins, so I ended up using the SPI pins on the Pi 1 and GPIO0, GPIO2, and GPIO3 for the DC, RST, and BUSY signals. This required a change in the <code>/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epdconfig.py</code> file. Under <code># Pin definition</code> I changed the defs this way:</p>
<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-python" data-lang="python"><span style="display:flex;"><span> <span style="color:#75715e"># Pin definition</span>
</span></span><span style="display:flex;"><span>    RST_PIN  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI2&#34;</span>
</span></span><span style="display:flex;"><span>    DC_PIN   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI0&#34;</span>
</span></span><span style="display:flex;"><span>    CS_PIN   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI10&#34;</span>
</span></span><span style="display:flex;"><span>    BUSY_PIN <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI3&#34;</span>
</span></span><span style="display:flex;"><span>    PWR_PIN  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI1&#34;</span>
</span></span><span style="display:flex;"><span>    MOSI_PIN <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI12&#34;</span>
</span></span><span style="display:flex;"><span>    SCLK_PIN <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI14&#34;</span>
</span></span></code></pre></div><p>This enabled the Waveshare e-paper library to work with my old Pi. If you will be using a newer Pi (2 or higher, with the 40-pin header), you don&rsquo;t need to do this.</p>
<p>Now just run the <code>/RaspberryPi_JetsonNano/python/examples/epd_5in65f_test.py</code> file to see if everything works.</p>
<h3 id="preparing-the-image-files-for-display">Preparing the Image Files for Display</h3>
<p>The 7-color e-paper display works differently than the normal LCD displays. Instead of every pixel having an RGB value, each pixel can only display one of the seven available colors: black, white, green, blue, violet, yellow, and red. Then there is the fixed 600x488 pixel resolution. Finally, the Waveshare library is written to accept bitmap (*.bmp) files only.</p>
<h4 id="color-conversion">Color Conversion</h4>
<p>The 7 color limitation means we have to do a color conversion to be able to display the pictures on this display. Selecting simply the nearest color for each pixel will give pretty underwhelming results, so we need a better method, called Floyd-Steinberg dithering. <a href="https://scipython.com/blog/floyd-steinberg-dithering/">This is a very good explanation of it.</a></p>
<p>You could code this yourself, but there is a lovely program called <a href="https://www.imagemagick.org/">ImageMagick</a> that will take care of this for us. But first we need an file that contains only the colors we need. Luckily, the Waveshare library comes with example pictures and you only need this one: <code>RaspberryPi_JetsonNano/python/pic/5in65_n0.bmp</code>, just copy it to the folder you will be using.</p>
<h5 id="select-the-files">Select the Files</h5>
<p>This is a pretty obvious first step—collect all the pictures you want displayed on your photo frame. In my case, I did a keyword search in my Google Photo account and simply exported all the results to a temporary directory. Then I did a quick scroll through to delete some photos I did not want to include. Since I was dealing with about 7000 photos, &ldquo;quick&rdquo; was a relative term, but it only took a couple of hours.</p>
<h5 id="do-the-conversion">Do the Conversion</h5>
<p>First install ImageMagick (I used <code>winget install ImageMagick.Q16-HDRI</code> on my windows PC). Next, run the following command-line command (replace the folder name accordingly):</p>
<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-bash" data-lang="bash"><span style="display:flex;"><span>magick *.jpg -resize 600x488 -dither FloydSteinberg -define dither:diffusion-amount<span style="color:#f92672">=</span>85% -remap <span style="color:#e6db74">&#34;folder/your/color/definition/file/is/in/5in65_n0.bmp&#34;</span> BMP3:images.bmp
</span></span></code></pre></div><p>This opens all the JPEG files in your folder, resizes them to max 600x488 pixels (leaving the unused space blank, so the aspect ratio stays correct), applies the Floyd-Steinberg dithering using the specified file, and, finally, saves them as bitmap files using the given filename, to which the sequence number of the file is appended.</p>
<p>Of course, there are many other ways of doing this, but this worked for me.</p>
<h5 id="doing-the-conversion-in-batches">Doing the Conversion in Batches</h5>
<p>ImageMagick is a very powerful program, but once you convert more than about 1000 pics at once, it will create a crazy amount of temporary files and the conversion itself will take a LONG time. So it&rsquo;s best to limit the conversion to batches of about 500-2000 files. This depends on your computer: 2000 pics took almost an hour on my rather new and quite powerful computer, producing over <strong>hundred GBs</strong> of temporary files in the process.</p>
<p>I think the best way is to copy all the image files to a folder called <code>todo</code> first. Then you copy the first batch of, say, 1000 files to your working folder. You then run the conversion, wait until it completes, and then copy everything but the resulting BMP files to the <code>done</code> folder. Then copy the next 1000 files, run the conversion again, but add &ldquo;_1&rdquo; to the result filename. Repeat, incrementing the suffix, until the <code>todo</code> folder is empty.</p>
<p>Congratulations, you now have a  large pile of bitmap files suitable for displaying on the 7 color e-paper display 😀 All you have to do now is copy it to a folder of your choice on the Raspberry Pi.</p>
<h3 id="the-python-code">The Python Code</h3>
<p>The code to display the files we just prepared is quite trivial, I simply copied the <code>/RaspberryPi_JetsonNano/python/examples/epd_5in65f_test.py</code> demo code and removed all the unnecessary parts. Then I added the code to be able to display bitmaps that don&rsquo;t fill the whole screen. Finally, I added random selection to keep the files from repeating too quickly. The code uses Pillow and you should take care to use proper file handling or you will have problems.</p>
<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-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> random
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> logging
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> time
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> PIL <span style="color:#f92672">import</span> Image
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> traceback
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Imports the Waveshare library</span>
</span></span><span style="display:flex;"><span>libdir <span style="color:#f92672">=</span> os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>join(os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>dirname(os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>dirname(os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>realpath(__file__))), <span style="color:#e6db74">&#39;lib&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>exists(libdir):
</span></span><span style="display:flex;"><span>    sys<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>append(libdir)
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> waveshare_epd <span style="color:#f92672">import</span> epd5in65f
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Let&#39;s keep the log short and only collect warnings. Use levels &#34;INFO&#34; or &#34;DEBUG&#34; for setup.</span>
</span></span><span style="display:flex;"><span>logging<span style="color:#f92672">.</span>basicConfig(level<span style="color:#f92672">=</span>logging<span style="color:#f92672">.</span>WARN)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>    logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Random files&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Initialize the display driver</span>
</span></span><span style="display:flex;"><span>    epd <span style="color:#f92672">=</span> epd5in65f<span style="color:#f92672">.</span>EPD()
</span></span><span style="display:flex;"><span>    logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;init&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#The forever loop</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
</span></span><span style="display:flex;"><span>        logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;read bmp file&#34;</span>)
</span></span><span style="display:flex;"><span>        image_folder<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/the/folder/with/the/bitmaps/&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#Randomly select the file from the folder</span>
</span></span><span style="display:flex;"><span>        random_file<span style="color:#f92672">=</span>random<span style="color:#f92672">.</span>choice(os<span style="color:#f92672">.</span>listdir(image_folder))
</span></span><span style="display:flex;"><span>        logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Chosen file: &#34;</span> <span style="color:#f92672">+</span> image_folder <span style="color:#f92672">+</span> random_file)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#This is important: if you don&#39;t use proper file handling using &#34;with&#34;,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#the file stays open and displaying the next file will fail</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">with</span> Image<span style="color:#f92672">.</span>open(image_folder <span style="color:#f92672">+</span> random_file)<span style="color:#f92672">.</span>convert(<span style="color:#e6db74">&#39;RGBA&#39;</span>) <span style="color:#66d9ef">as</span> bitmap:
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">#First set up a white background</span>
</span></span><span style="display:flex;"><span>            Himage <span style="color:#f92672">=</span> Image<span style="color:#f92672">.</span>new(<span style="color:#e6db74">&#39;RGB&#39;</span>, (epd<span style="color:#f92672">.</span>width, epd<span style="color:#f92672">.</span>height), <span style="color:#ae81ff">0xffffff</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">#Get image dimensions from the bitmap file</span>
</span></span><span style="display:flex;"><span>            width, height <span style="color:#f92672">=</span> bitmap<span style="color:#f92672">.</span>size
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">#Paste the image on the middle of the white background</span>
</span></span><span style="display:flex;"><span>            Himage<span style="color:#f92672">.</span>paste(bitmap, (int((<span style="color:#ae81ff">600</span><span style="color:#f92672">-</span>width)<span style="color:#f92672">/</span><span style="color:#ae81ff">2</span>), int((<span style="color:#ae81ff">488</span><span style="color:#f92672">-</span>height)<span style="color:#f92672">/</span><span style="color:#ae81ff">2</span>), int((<span style="color:#ae81ff">600</span><span style="color:#f92672">-</span>width)<span style="color:#f92672">/</span><span style="color:#ae81ff">2</span>)<span style="color:#f92672">+</span>width, int((<span style="color:#ae81ff">488</span><span style="color:#f92672">-</span>height)<span style="color:#f92672">/</span><span style="color:#ae81ff">2</span><span style="color:#f92672">+</span>height)))
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#Initialize the display</span>
</span></span><span style="display:flex;"><span>        epd<span style="color:#f92672">.</span>init()    
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#Display the bitmap on background</span>
</span></span><span style="display:flex;"><span>        epd<span style="color:#f92672">.</span>display(epd<span style="color:#f92672">.</span>getbuffer(Himage))
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#Turn off the display internals and wait 10 minutes before you show the next pic</span>
</span></span><span style="display:flex;"><span>        logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Goto Sleep...&#34;</span>)
</span></span><span style="display:flex;"><span>        epd<span style="color:#f92672">.</span>sleep()
</span></span><span style="display:flex;"><span>        time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">600</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> e:
</span></span><span style="display:flex;"><span>    logging<span style="color:#f92672">.</span>info(e)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">except</span> <span style="color:#a6e22e">KeyboardInterrupt</span>:    
</span></span><span style="display:flex;"><span>    logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;ctrl + c:&#34;</span>)
</span></span><span style="display:flex;"><span>    epd5in65f<span style="color:#f92672">.</span>epdconfig<span style="color:#f92672">.</span>module_exit(cleanup<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span>    exit()
</span></span></code></pre></div><p>Insert the image path as needed, then run and check if it works.</p>
<h3 id="set-up-the-pi-to-run-unattended">Set Up the Pi To Run Unattended</h3>
<p>If everything works as intended, <a href="https://www.dexterindustries.com/howto/run-a-program-on-your-raspberry-pi-at-startup/">set up the Pi to auto-run the code on startup using this handy guide</a>. I like the SYSTEMD method best.</p>
<p>Once everything works well after you reboot the Pi, don&rsquo;t forget to <a href="https://learn.adafruit.com/read-only-raspberry-pi/overview">turn on the Overlay File System</a> to prevent SD card corruption if the power goes out unexpectedly.</p>
<h2 id="putting-everything-together">Putting Everything Together</h2>
<p>You now have an e-paper display showing beautiful pictures next to a Raspberry Pi. But it&rsquo;s not a digital photo frame yet, it needs some kind of a housing first. I decided to use a simple <a href="https://www.thingiverse.com/thing:922740">3D-printed VESA 75 mm case</a> for my Pi and then designed a <a href="https://www.printables.com/model/1072040-stand-for-565-7-color-waveshare-e-paper-with-vesa">simple stand to hold everything together</a>.</p>
<figure>
    <img src="/images/epaper/stand.png" alt="The stand from the back">
    <figcaption>You need to glue the two parts together using a drop of superglue</figcaption>
  </figure>
  
<h2 id="conclusion">Conclusion</h2>
<p>The first thing anybody will probably ask is: Why? Why use a strange display with a rather low resolution and only 7 colors to display your family photos? Without even keeping it low-power?</p>
<p>The answer is a bit complicated, but I can explain it best like this:</p>
<ul>
<li>Constraints spur creativity.</li>
<li>It&rsquo;s not about the resolution or color fidelity, it&rsquo;s about the pictures.</li>
<li>The grainy dithered images have a nice rough and timeless vintage feel to them. They give a new quality to the images I saw many times before.</li>
<li>The slow tempo of changing images does not demand your attention. It is a decor item, not another gadget to waste time with.</li>
</ul>
<p>And I got to rescue an 13-year old Raspberry Pi from the drawer in the process 😄</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>EV Charging With Rooftop Solar and Storage</title><link>https://nunrg.eu/posts/ev_charging_solar/</link><pubDate>Thu, 31 Oct 2024 18:00:02 +0100</pubDate><guid>https://nunrg.eu/posts/ev_charging_solar/</guid><description>How to charge an EV with rooftop solar and battery storage, using Home Assistant to maximise self-consumption and cut charging costs.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/ev-charging-with-rooftop-solar-and-storage-a0e1e91a7d62">reading this article on Medium</a></p>
<p>In the <a href="https://nunrg.eu/posts/ev_charging_strategies/">previous article</a> we looked at how to rationally charge EVs if you have the option to install your home own charger (<a href="https://en.wikipedia.org/wiki/Charging_station">EVSE</a>). But if you can install your home charger, chances are you can install a rooftop solar system too. And once you have that, a home storage battery is the logical next step.</p>
<p>These two investments finally let you take matters into your own hands and, combined with a heat-pump, effectively decarbonize the majority of your energy use. You will still be dependent on the grid, but instead of being dependent all the time, your dependence shrinks to 3-4 months a year, depending on the sizing and where you live. 🥳</p>
<p>But let&rsquo;s stick to EV charging for now:</p>
<h2 id="the-basic-concept">The Basic Concept</h2>
<p>The best way to explain this is to pretend you are a farmer 😃 Your rooftop solar is a bit like a farm in that it&rsquo;s dependent on the weather. When the sun is shining, your solar plant is producing kilowatts of power. When it&rsquo;s heavily overcast, rainy, or at night, you get next to no production. So when you see the sun shining brightly, charge your car!</p>
<p>This brings all kinds of advantages:</p>
<ul>
<li>The solar energy is effectively free, except for installation costs. You should recover the installation costs in <a href="https://energyefficiency.ie/blog/solar-panels-as-a-financial-investment/">6-7 years</a>.</li>
<li>Any energy you charge from your solar system is carbon neutral, expect for the carbon debt of the installation (I don&rsquo;t like greenwashing, but this happens only once as well and with rational use, it is <a href="https://hcb-solar.com.au/understanding-the-carbon-footprint-of-solar-panels/">negligible in comparison to the avoided emissions</a>).</li>
<li>You are actively contributing to your grid stability. Not only will you be a smaller burden on your grid in general, but with some automation, you can actively act as a good citizen and act to reduce grid load in peak times. Why push power into the grid if you can charge your car instead? Why charge your car with grid power at night if you could have taken advantage of the sun during the day?</li>
</ul>
<figure>
    <img src="/images/EV_charging/solarEVcharger.jpg" alt="Solar EV charging station with storage">
    <figcaption>A simple picture of the concept, credit: Electrek</figcaption>
  </figure>
  
<h2 id="the-setup">The Setup</h2>
<p>The base for this article are a solar system with storage and a controllable car charger (EVSE), connected to a Home Assistant system. I&rsquo;m going to describe a solution with a hybrid inverter, but solutions with a separate power-wall will be similar, depending on the way your power-wall is controlled.</p>
<p>The charging logic will implemented using a Python script in AppDaemon, the rest are normal integrations, automations and entities in Home Assistant.</p>
<h2 id="the-charging-strategy">The Charging Strategy</h2>
<p>There are several things to take care of here:</p>
<ul>
<li>
<p>We need to supervise the total current of the installation to prevent overloads. If you have a 3x 20A home installation, you need to take care not to exceed 20A on any of the phases at any time, regardless of the resulting grid current including solar.</p>
</li>
<li>
<p>We need to prepare separate options at least for green charging and low-cost charging.</p>
<ul>
<li>Green charging will only turn on when you have an excess of energy available and only charge the excess energy without taking any from the grid.</li>
<li>Low-cost charging will charge the car when the energy prices are low using all the available power.</li>
<li>You could also add an &ldquo;express&rdquo; mode which will give you all available power at any time while still ensuring you don&rsquo;t blow any fuses.</li>
</ul>
</li>
<li>
<p>We want to monitor the following at all times:</p>
<ul>
<li>The phase currents. If any one of them is above the installation limit, we need to reduce charging current of the EVSE.</li>
<li>The storage battery charge level. Our strategy will depend on it.</li>
<li>The power coming from/going into the storage battery. This will help us set the EVSE charging power in the green mode.</li>
<li>The power coming from/going into the grid. This will help us avoid the grid utilization penalties.</li>
<li>Status of car connection to the EVSE. This is one of the inputs when deciding whether to turn on charging.</li>
<li>The grid utilization time-slots and limits. This is again helping us to avoid the grid utilization penalties.</li>
<li>Information about consumed energy at the EVSE. This is just to know how much charge was delivered to the car.</li>
</ul>
</li>
</ul>
<h3 id="limiting-currents">Limiting Currents</h3>
<p>This is the simplest part of it all.</p>
<ol>
<li>
<p>We set a current limit, 15A in my case due to limits of the inverter. I could actually go up to 20A, but there are other things to think about outside this scope&hellip;</p>
</li>
<li>
<p>We check the phase currents of the installation every minute.</p>
<p>The interval is important: Too fast and the EVSE does not have the time to respond to current changes. Too slow and we run the risk of blowing a fuse. Fuses work relatively slowly, so you should have about 10 minutes before anything bad happens. But it&rsquo;s better to do it as fast as your car can follow. You see, when the car charger sets the current, it basically tells your car what the maximum current it can take from the EVSE. The on-board charger of the car (OBC) will then honor this limit with a certain delay depending on the manufacturer. But one minute should be ok for most cars.</p>
</li>
<li>
<p>If any of the phase currents is above the limit, we:</p>
<ol>
<li>Calculate the excess current. E.g. if we set the limit to 15A and the biggest phase current is 18A, the excess is 3A.</li>
<li>Then we check the EVSE set curent (e.g. 10A) and subtract the excess (3A) to get the needed current setting (7A).</li>
<li>Most cars can only charge with a minimum of 6A. So if the new current setting is lower than that, we need to turn off the EVSE for a while (e.g. 5 minutes). This will give the fuses some time to cool down.</li>
<li>If limit is over minimum, we set the new current limit (7A) on the EVSE.</li>
</ol>
</li>
<li>
<p>Conversely, if all the phase currents are well below the limit (more than a tolerance we set, normally just the setting resolution, i.e. 1A), we do the above steps in the other direction, increasing the EVSE current limit as needed or turning it on if we had to turn it off previously.</p>
</li>
<li>
<p>Rinse and repeat forever.</p>
</li>
</ol>
<p>This process needs to have an absolute top priority, because it is the only part that can cause problems with our installation. Any strategy we implement will be using it.</p>
<h4 id="example-code">Example Code</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-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">currentControl</span>(self, limit):    
</span></span><span style="display:flex;"><span>    headroom <span style="color:#f92672">=</span> limit <span style="color:#f92672">+</span> CURRENT_TOLERANCE <span style="color:#f92672">-</span> INSTALLATION_MAXIMUM
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> headroom <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">#Current too high, calculate wanted charging current to compensate</span>
</span></span><span style="display:flex;"><span>      wanted_current <span style="color:#f92672">=</span> int(self<span style="color:#f92672">.</span>set_current <span style="color:#f92672">+</span> int(headroom))
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> wanted_current <span style="color:#f92672">&gt;=</span> MIN_CHARGE_CURRENT:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>setEVSE(wanted_current)
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">elif</span> <span style="color:#f92672">not</span> self<span style="color:#f92672">.</span>powerOff:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Turn off to cool off if no headroom</span>
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>powerOff <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">elif</span> headroom <span style="color:#f92672">&gt;</span> CURRENT_TOLERANCE:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">#Current too low, calculate wanted charging current to compensate</span>
</span></span><span style="display:flex;"><span>      wanted_current <span style="color:#f92672">=</span> int(self<span style="color:#f92672">.</span>set_current <span style="color:#f92672">+</span> headroom)
</span></span><span style="display:flex;"><span>      self<span style="color:#f92672">.</span>setEVSE(wanted_current)
</span></span></code></pre></div><p><em>This function will be called every minute, regardless of the chosen charging mode, as long as charging is active.</em></p>
<h3 id="green-charging">Green Charging</h3>
<p>Now that the current limiting is out of the way, we can start thinking about green charging.</p>
<p>The first thing to consider is whether the storage battery is at least partially charged. It will act as our buffer when setting the charging power, taking or receiving any power we will not be able to fully compensate by setting the charger current. The storage battery voltage is not an exact measurement of charge level, but close enough. So I simply decided on the lower storage battery voltage limit to enable green charging, corresponding to about 20% of it&rsquo;s capacity. I also set a hysteresis so the charging does not turn on/off repeatedly when we are near the limit. And the charging power near the limit is minimal to avoid spikes.</p>
<p>Next we must take care of proper turning on and off of the car charger. We do this by looking at the car connection status of the charger (in my case 1 means nothing is connected, 2 means connected but not charging, 3 means connected and charging). If the car is connected, but not charging, we should turn on the charger. If the car is not corrected, we should turn it off.</p>
<p>Most importantly, we need to look at the power being charged to the storage battery. This is effectively the excess solar power available and we should charge the car with it. The approach I took is simple, but works well: If the excess power exceeds a certain value (1A of charging current, i.e. 690W + losses), increase the charging current by 1A. If the excess power drops below zero (i.e. we are draining the storage battery), decrease the charging current by 1A. This approach is really simple, but it also helps to smooth out spikes that happen in the solar system due to e.g. clouds shifting quickly.</p>
<p>If you were careful, you might wonder how this works together with the current limiting. For simplicity I decided to adapt the current limiting function to take two values, the current control maximum and the charging logic maximum resulting from excess power.</p>
<p>Finally, I think it&rsquo;s useful to try to speed up the charging when the sun is plentiful. So if the storage battery is more than half-full, I simply charge with full power until it gets down to half-full. This makes the charging faster.</p>
<h4 id="example-code-1">Example Code</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-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">doGreenCharge</span>(self):
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Set current to minimum at start of charge, only start if voltage high</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># enough, only start if enough headroom</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>battery_voltage <span style="color:#f92672">&gt;</span> (LOW_BATTERY_LIMIT <span style="color:#f92672">+</span> VOLTAGE_HYSTERESIS):
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (self<span style="color:#f92672">.</span>vehicle_state <span style="color:#f92672">==</span> <span style="color:#ae81ff">2</span>) <span style="color:#f92672">and</span> ((MAX_INVERTER_CURRENT <span style="color:#f92672">-</span> self<span style="color:#f92672">.</span>highest_inverter_current) <span style="color:#f92672">&gt;=</span> MIN_CHARGE_CURRENT):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (self<span style="color:#f92672">.</span>set_current <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#f92672">or</span> (self<span style="color:#f92672">.</span>set_current <span style="color:#f92672">&gt;</span> MIN_CHARGE_CURRENT):
</span></span><span style="display:flex;"><span>          self<span style="color:#f92672">.</span>setEVSE(MIN_CHARGE_CURRENT)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># If the battery if full enough, charge</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (self<span style="color:#f92672">.</span>battery_voltage <span style="color:#f92672">&gt;</span> LOW_BATTERY_LIMIT) <span style="color:#f92672">and</span> (self<span style="color:#f92672">.</span>vehicle_state <span style="color:#f92672">==</span> <span style="color:#ae81ff">3</span>):
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Do the excess power control</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (self<span style="color:#f92672">.</span>battery_power<span style="color:#f92672">&lt;-</span><span style="color:#ae81ff">800</span>) <span style="color:#f92672">and</span> (self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">&gt;</span>MIN_CHARGE_CURRENT):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (self<span style="color:#f92672">.</span>battery_voltage <span style="color:#f92672">&lt;</span> (LOW_BATTERY_LIMIT <span style="color:#f92672">+</span> VOLTAGE_HYSTERESIS)):
</span></span><span style="display:flex;"><span>          self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">=</span>MIN_CHARGE_CURRENT
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>          self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">elif</span> (self<span style="color:#f92672">.</span>battery_power<span style="color:#f92672">&gt;</span><span style="color:#ae81ff">800</span>) <span style="color:#f92672">and</span> (self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">&lt;</span>MAX_INVERTER_CURRENT):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (self<span style="color:#f92672">.</span>battery_voltage <span style="color:#f92672">&lt;</span> (LOW_BATTERY_LIMIT <span style="color:#f92672">+</span> VOLTAGE_HYSTERESIS)):
</span></span><span style="display:flex;"><span>          self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">=</span>MIN_CHARGE_CURRENT
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>          self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">+</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># Do full power with full storage battery</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">elif</span> (self<span style="color:#f92672">.</span>battery_voltage <span style="color:#f92672">&gt;</span> HIGH_BATTERY_LIMIT):
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>greenCurrent<span style="color:#f92672">=</span>MAX_CHARGE_CURRENT
</span></span><span style="display:flex;"><span>      self<span style="color:#f92672">.</span>currentControl(MAX_INVERTER_CURRENT, self<span style="color:#f92672">.</span>greenCurrent)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">currentControl</span>(self, limit, preference):    
</span></span><span style="display:flex;"><span>    headroom <span style="color:#f92672">=</span> limit <span style="color:#f92672">+</span> CURRENT_TOLERANCE <span style="color:#f92672">-</span> MAX_INVERTER_CURRENT
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> headroom <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">#Current too high, calculate wanted charging current to compensate</span>
</span></span><span style="display:flex;"><span>      wanted_current <span style="color:#f92672">=</span> int(self<span style="color:#f92672">.</span>set_current <span style="color:#f92672">+</span> int(headroom))
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> wanted_current <span style="color:#f92672">&gt;=</span> MIN_CHARGE_CURRENT:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> wanted_current <span style="color:#f92672">&gt;</span> preference:
</span></span><span style="display:flex;"><span>          self<span style="color:#f92672">.</span>setEVSE(preference)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>          self<span style="color:#f92672">.</span>setEVSE(wanted_current)
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">elif</span> <span style="color:#f92672">not</span> self<span style="color:#f92672">.</span>powerOff:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Turn off to cool off if no headroom</span>
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>powerOff <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">elif</span> headroom <span style="color:#f92672">&gt;</span> CURRENT_TOLERANCE:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">#Current too low, calculate wanted charging current to compensate</span>
</span></span><span style="display:flex;"><span>      wanted_current <span style="color:#f92672">=</span> int(self<span style="color:#f92672">.</span>set_current <span style="color:#f92672">+</span> headroom)
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> wanted_current <span style="color:#f92672">&gt;</span> preference:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>setEVSE(preference)
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>setEVSE(wanted_current)
</span></span></code></pre></div><p><em>Called every minute, calls adapted currentControl function to control the charge current</em></p>
<h2 id="results">Results</h2>
<p>Let&rsquo;s look at a typical mostly sunny day using green charging:</p>
<figure>
    <img src="/images/EV_charging/curves.png" alt="The curves when the system is working">
    <figcaption>The EVSE Charging Power, the Maximum Phase Current and the Storage Battery Level When Charging on a Sunny Day</figcaption>
  </figure>
  
<p>It was a sunny day and I decided to charge up the car in green mode at around 12:20:</p>
<ul>
<li>You can see how the charging power ramps up quickly due to there being so much solar power available at around 12:30, around 8kW.</li>
<li>The power stays almost constant until around 13:20, when another large load (the oven) turns on. This in turn causes the charging power to drop to about 3.5kW due to current limiting until the oven is done. During this time, the system is unable to take all available power (the oven only works on one phase), so the storage battery gets charged up a bit.</li>
<li>At around 13:40 the oven thermostat turns off and the charging power ramps up again. There is another cycle of the oven thermostat with power reducing again due to current control. The oven is finally turned off at about 13:50.</li>
<li>After 14:00 the solar power begins to slowly drop as the afternoon begins. It is clear to see how the system reduces charging power accordingly until about 16:00. There are some short current spikes, but current control takes quick care of them.</li>
<li>At 16:30 the solar power finally drops to a value below minimal charging power and we can finally see a clear trend in the storage battery voltage. The minimal charging power is maintained until the shutoff limit is reached at about 18:15 when the charging shuts off for the day.</li>
</ul>
<p>This was in late October, so I was able to charge only 11.6kWh that day (my personal best was 39,6kWh, almost 2/3 of my Kona), but this is still enough charge to do two errands. It was free in all important aspects—I didn&rsquo;t have to pay anything, it caused no carbon emissions and the power control prevented unnecessary cycling of the storage battery. It is a win-win-win 😀
If I needed more charge that day, I could still simply turn on the cheap charging option and charge to full at night. But it turns out that with my driving, the green option is all I need from about March to about October.</p>
<h2 id="what-if-you-dont-have-storage">What If You Don&rsquo;t Have Storage?</h2>
<p>This same approach would still work if you didn&rsquo;t have a storage battery in your solar system. Instead of looking at the storage battery charging power you would have to look at grid power, but all the rest would be the same.</p>
<p>The only difference is that you would be using the grid as your battery. This means you are not worried about wear on the battery, but you can still be a good citizen and reduce your usage during the maximum grid load times. Sadly, the old-style net-metering with constant energy price does not give you any financial incentive to do that, but I&rsquo;m assuming this will change soon.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This was a short description of the principles of EV charging control in a smart home installation with solar, storage battery, and a car charger. As you can see, home automation is an interesting way to reduce your carbon footprint, save energy (costs), and reduce grid load.</p>
<p>To keep thing simple, I did not discuss the grid utilization limit handling here. If there is interest, I&rsquo;ll write a separate article about that.</p>
<p>The code examples are only snippets and intended as an illustration of principle. If you are interested in details, add a comment below and let&rsquo;s discuss!</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>EV Charging Constraints and Strategies</title><link>https://nunrg.eu/posts/ev_charging_strategies/</link><pubDate>Sun, 27 Oct 2024 08:53:00 +0100</pubDate><guid>https://nunrg.eu/posts/ev_charging_strategies/</guid><description>The technical and economic constraints of EV charging, and practical strategies to charge cheaper, smarter and more sustainably.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/ev-charging-constraints-and-strategies-bf8a11e4f7d7">reading this article on Medium</a></p>
<p>Charging an EV can be really straightforward—have a charger (EVSE) installed, plug the car in and wait until the battery is full. But there is a lot more to it than that—the technical constraints, the varying costs, the desire for sustainability all play a role.</p>
<figure>
    <img src="/images/EV_charging/evplugged.jpg" alt="An EV Plugged Into a Home Charger">
    <figcaption>Looks simple, doesn&#39;t it? (Picture credit: CarPro)</figcaption>
  </figure>
  
<h2 id="technical-constraints">Technical Constraints</h2>
<h3 id="grid-connection-capacity">Grid Connection Capacity</h3>
<p>Many European single-family houses will have a hard limit on on grid connection capacity. For my own home this amounts to 230V, 3-phase, 20A per phase, i.e. 13.8kVA. Plug in any more than that and the distribution fuse blows, leaving you in the dark with an expensive addition to your next electrical bill.</p>
<p>The usual car chargers (EVSE) for family homes in Europe will have a 3x 16A (11kW) charging capacity. This is enough to charge a car at home, but it&rsquo;s not much more than the grid connection capacity of my house. So if the charger is dumb and I try to charge my car while baking some cookies, the combined current on one phase will shoot to 16A (charger) + 8A (oven) = 24 A and I&rsquo;m going to end up in the dark, with an empty battery and no cookies 🫠</p>
<h4 id="solutions">Solutions</h4>
<h5 id="buy-more-capacity">Buy More Capacity</h5>
<p>The old way of solving this would be to install bigger distribution fuses, but this is not an optimal solution and it will be expensive:</p>
<ul>
<li>Your monthly capacity cost will be bigger.</li>
<li>You might have to rewire a part of your house wiring for bigger current.</li>
<li>Depending on the utilization of the transformer in your neighborhood, you might not get the extra capacity even if you are willing to pay for it.</li>
<li>You will be paying for mostly unused capacity.</li>
</ul>
<h5 id="use-a-smart-charger">Use a Smart Charger</h5>
<p>A much better solution is to simply look at the current you are taking from the grid and take action when it exceeds the limit.</p>
<p>You can do this manually to some extent, you just need to be the nagging presence that scolds anyone who even thinks about turning on any big load while you charge your car. This should also immensely improve your relationships! 🤪</p>
<p>A much better solution is to automate this. It&rsquo;s called &ldquo;dynamic charging&rdquo; and most modern home charging stations are able to do it at least to some extent. It works like this: You have a current meter installed on the main electrical connection to your home and a mechanism of some sort regularly checks the load. If the load exceeds your limit, this mechanism (a smart home automation or a function of the charger itself) commands to charger to temporarily reduce the charging current until everything is back to normal. Your car will charge a bit slower sometimes, but you get to eat your cookies, stay warm and keep the lights on. 😃</p>
<p>These days, you can buy an off the shelf home car charger with dynamic charging support and <a href="https://www.carplug.eu/wallbox">there are a lot of models available</a>. But if you are interested in DIY solutions, I wrote an article <a href="https://nunrg.eu/posts/example_diy_charging_point/">about building one yourself</a>.</p>
<h2 id="varying-costs">Varying Costs</h2>
<h3 id="time-based-energy-costs">Time-Based Energy Costs</h3>
<p>Depending on where you live, the cost of electric energy will vary through the day. Where I live, daytime electrical energy is about 50% more expensive, so charging your car will be much cheaper at night and on weekends here.</p>
<h3 id="grid-utilization-costs">Grid Utilization Costs</h3>
<p>Depending on where you live, you might have time-slot based grid utilization costs charged extra. We just got these added to our system and they can make a major difference, a couple hundred Euro or more per year.</p>
<p>It works like this: you choose a connection capacity for each time slot and pay an upfront fixed cost for that. The busy time-slots (during office hours and daytime in winter) are really expensive per kilowatt, so it makes sense to book just enough capacity during the expensive time-slots and all the available capacity during the cheap ones. But there is a catch: if you don&rsquo;t book enough capacity, you will pay a couple of Euros for each 15 minutes you have exceeded it. It will add up quickly!</p>
<p>At first glance, this boils down to the same thing as varying energy costs—charging from grid on off-peak times is cheaper. But this cost basically forces you to really think about your energy use. If you book low capacity during expensive time-slots, you can save hundreds of Euro per year, but if you then blow through those limits, you will pay much more in penalties.</p>
<h3 id="solution">Solution</h3>
<p>Both of these issues can be taken care of manually by being careful about when you decide to charge your car. Of course, having a bit of automation can help a lot, especially with the grid utilization costs and the varying capacities in individual time slots.</p>
<p>A good automated solution will automatically limit the charging power to the capacity limits of the individual time-slots and give you an option to allow or disallow daytime charging. Most cars also have built-in charge schedulers that can help you charge them when the energy is cheaper.</p>
<h2 id="sustainability">Sustainability</h2>
<p>If you care about the climate emergency, you care about carbon intensity of the electric energy you use. In an ideal world, this would have been taken care of by the utilities for you, but we are still a long way from that.</p>
<p>Of course, many utilities are happy to sell you an &ldquo;All Renewables&rdquo; package for your electricity bill, where you pay a little extra so the energy is bought from renewable sources. I support this wholeheartedly, because it gives the energy producers the right incentives to build more renewables. On the other hand, we have to be realistic and acknowledge that you are always consuming electric energy with the average carbon intensity your grid is producing at any given time. There is no way to select for less-carbon intensive electrons, unfortunately 😞</p>
<p>This means you must think about the energy mix in your area and this is something your utility is compelled by law to report transparently. You should get a diagram showing the percentages of energy that come from coal, natural gas, nuclear, solar, hydro, wind etc with all your electricity bills.</p>
<figure>
    <img src="/images/EV_charging/energy_mix.png" alt="Energy Mix in Slovenia in 2023">
    <figcaption>The Energy Mix in Slovenia in 2023, credit SURS</figcaption>
  </figure>
  
<p>But keep in mind, this mix is an average and the actual values vary:</p>
<ul>
<li><strong>Depending on time of day:</strong> If you have a big nuclear backbone, like my country does, you will be consuming mostly nuclear at night, which is low carbon. If you don&rsquo;t, the cheaper night-time energy will probably have a much bigger carbon footprint, coming from coal and natural gas. But some countries have a lot of hydro&hellip;</li>
<li><strong>Depending on the weather:</strong> If your country has a lot of solar, daytime electricity will be almost carbon free unless the weather is bad. A rainy day can cut solar production by 80% or more.</li>
<li><strong>Then there are the seasons:</strong> Hydroelectric plants produce much more energy when the water is plentiful, e.g. in spring and fall. Solar is less plentiful in winter and the opposite holds for wind power.</li>
</ul>
<p>The good thing is, you don&rsquo;t really have to work all of this out yourself. There are services that will give you a good estimate of current carbon intensity in your area, like <a href="https://www.electricitymaps.com/">Electricity Maps</a>, which also has an API and there is even a Home Assistant integration for it.</p>
<figure>
    <img src="/images/EV_charging/carbon_intensity.png" alt="My Electricity Maps Readout">
    <figcaption>My Electricity Maps Readout</figcaption>
  </figure>
  
<h3 id="solution-1">Solution</h3>
<p>You can do a lot here if you just behave rationally. Find the data and act accordingly. But there is also the potential to automate things using the publicly available services like Electricity Maps mentioned above.</p>
<h2 id="conclusion">Conclusion</h2>
<p>EVs have the potential to strongly reduce your carbon footprint and cost per kilometer driven, if you use them rationally. But this is not trivial and there are many factors to consider.</p>
<p>If you want to do EV-charging right, you need to think at least about:</p>
<ul>
<li>Dynamic charging</li>
<li>Time-based energy costs</li>
<li>Grid utilization costs</li>
<li>The energy mix in your area</li>
<li>The variations of the energy mix because of time of day, weather and seasons</li>
</ul>
<p>Keep in mind that you can still think about your energy mix even if you are not able to charge at home. You will, of course, make sure to keep yourself mobile first, but once you take care of that, you can start thinking about other things like carbon intensity.</p>
<p>To keep this article somewhat manageable, I did not discuss other very important factors, like your driving habits, choosing an efficient car and, most importantly, alternative mobility solutions. But let&rsquo;s face it, these are way more important than any charging strategy and the one thing you should think about at the very beginning. Our dependence on cars is toxic to us and the planet, so the best car is still the one that never got built.</p>
<h2 id="the-second-conclusion">The Second Conclusion</h2>
<p>You may have noticed  I did not mention rooftop solar or home energy storage. This is intentional, for two reasons: Most people do not have rooftop solar systems. And of those that do, only few have storage. I wanted to focus on a regular home owner buying their first EV, trying to help them understand the potential and behave rationally, both in terms of costs and environmental concerns.</p>
<p>But rooftop solar, especially combined with storage, opens a whole new dimension of options. I explored these in the next article, <a href="https://nunrg.eu/posts/ev_charging_solar/">EV Charging With Rooftop Solar and Storage</a>.</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>The Importance of Topology in a Smart Home Installation</title><link>https://nunrg.eu/posts/smarthome_topology/</link><pubDate>Sat, 19 Oct 2024 09:47:22 +0200</pubDate><guid>https://nunrg.eu/posts/smarthome_topology/</guid><description>Why topology matters in a smart home: comparing centralised and decentralised wiring approaches, their costs, cabling and trade-offs.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/how-to-connect-things-the-importance-of-topology-in-a-smart-home-installation-90ce5bd84485">reading this article on Medium</a></p>
<h2 id="so-what-do-i-mean-by-topology">So What do I Mean by Topology?</h2>
<p><a href="https://en.wikipedia.org/wiki/Topology">Topology</a> can be inexpertly defined as the science of how things are connected.</p>
<p>The basic concept of our smart home installation (what is connected to what and where to put the controller(s)) turns out to be immensely important in terms of costs, flexibility, complexity, and, consequently, reliability.</p>
<h2 id="a-modern-home">A Modern Home</h2>
<p>To help explain the concept I prepared a simple schematic of a small modern home. It only has 5 residential rooms, a machine room and some external installations. Most homes will tend to be bigger (e.g. a room for the kids, etc.) and more complicated (e.g. separate fuses for outlets, solar systems, dimmable lights, etc.), but it&rsquo;s a good starting point for our discussion.</p>
<figure>
    <img src="/images/smarthome/house.png" alt="The Basics of a Modern Home">
    <figcaption>The Basics of a Modern Home</figcaption>
  </figure>
  
<p>The schematic shows the equipment of our model home, divided into seven units: five residential rooms, one machine room and the outside equipment.</p>
<p>Each unit contains the minimum equipment it needs, e.g. lights, switches, sensors, actuators, etc.</p>
<p>At the top the units are marked with the wiring requirements. E.g. 24/5/4 means a unit needs 24 wires in total (power, control and I/O), 5 of those will need control (relays), and 4 are general I/O (digital inputs, outputs, and field busses).</p>
<h2 id="the-approaches-to-smart-home-automation">The Approaches to Smart Home Automation</h2>
<h3 id="the-one-large-plc-approach">The &ldquo;One Large PLC&rdquo; Approach</h3>
<p>Years ago, smart homes were really expensive and so rich people hired contractors used to industrial automation to build them. Our model home may look complicated, but it&rsquo;s about on par with a medium-sized machine in a factory. These were traditionally built around one big PLC in a central cubicle and everything was wired from there.</p>
<figure>
    <img src="/images/smarthome/bigplcwiring.png" alt="Schematic of a smart home wired with one big PLC">
    <figcaption>Schematic of a Smart Home Wired With One Big PLC (PLC image credit: Industrial Shields)</figcaption>
  </figure>
  
<p>This seems like a pretty straightforward way of doing things, but it has some serious drawbacks:</p>
<h4 id="wiring-length-and-cost">Wiring Length and Cost</h4>
<p>Every. Single. Wire. Has to be routed from the machine room with the giant PLC cubicle to the switch, sensor, or load that needs to be connected to it. This means a lot of cable: 2.2km for our simple example, but more like 8-10km for a medium size smart home with all the bells and whistles.</p>
<p>But wire is pretty cheap compared to the whole house, right? Not really: You need to route it throughout the house using cable conduits. In a brick-built European home, this means A LOT of carving of spaces for conduits and holes into the walls, which is expensive. These conduits also need to be quite large. The wires themselves also become a noticeable expense once you go beyond a kilometer or two.</p>
<h4 id="complexity">Complexity</h4>
<p>Programming PLCs is not trivial and this is not a small installation. If you don&rsquo;t have extensive experience, this approach way will take a professional programmer and their time is expensive. You will also need to rehire them for changes to the system. This effectively means you ceded control to your smart home to somebody else. You also lost one of it&rsquo;s main advantages—the ability to easily reconfigure it.</p>
<h4 id="reliability">Reliability</h4>
<p>Industrial PLCs are really reliable, so it&rsquo;s unlikely that the control system itself will break. It&rsquo;s much more likely that a relay will fail. Depending on your PLC this can mean you have to replace the whole PLC or just an output module. The option to replace individual relays is rare.</p>
<p>The main problem with replacement of the PLC is not the cost, but the fact that you will need to rewire everything and program the PLC. This both takes time and is possibly something you will not be able to do yourself, see &ldquo;Complexity&rdquo; above. And maybe, in 10 years, your PLC model will not be available anymore and you are looking at a complete redesign of the PLC cubicle. The same goes if you decide to do a major reconfiguration (e.g. increase the number of outputs).</p>
<h4 id="reconfigurability">Reconfigurability</h4>
<p>Say you wanted to change the way you use a room or do a renovation, maybe add an extension to the house? You will most likely have to replace (<a href="https://nunrg.eu/posts/smarthome_topology/#reliabilty">Reliability</a>), rewire, and reprogram (<a href="https://nunrg.eu/posts/smarthome_topology/#complexity">Complexity</a>) the PLC, and find a good way to integrate the new wiring with the existing system. All of this is non-trivial.</p>
<h3 id="the-distributed-approach">The Distributed Approach</h3>
<p>Looking at the drawbacks of the &ldquo;One Large PLC&rdquo; approach, we can see that most of them stem from one basic feature: centralization. Everything is done in one place and this is inefficient (most centralized systems are). So how can we approach this in a better way?</p>
<p>The answer is to use several small controllers instead of the one big PLC and put them close to where we need them, typically in a small control cubicle in the room it controls. This way we only need to route an Ethernet (or other control bus) cable, and power wiring to the controllers. The individual devices are then wired directly to the local controllers, which is much simpler and shorter.</p>
<figure>
    <img src="/images/smarthome/distributed.png" alt="Schematic of a Smart Home Wired With Distributed Controllers">
    <figcaption>Schematic of a Smart Home Wired With Distributed Controllers (Raspberry Pi image credit: Botland)</figcaption>
  </figure>
  
<h4 id="wiring-length-and-cost-1">Wiring Length and Cost</h4>
<p>Let&rsquo;s compare the wiring here with the &ldquo;One Big PLC&rdquo; scenario:</p>
<ul>
<li>The total wiring length comes out to about 1.5km, which is about a third less than with the centralized approach (I count the ethernet cables as single wires, nobody would ever run individual twisted pairs separately in a conduit).</li>
<li>You can reuse the Ethernet cables for the network infrastructure you are going to need anyway (for computers, NAS, home cinema etc.)</li>
<li>The cable conduits to the local controllers can be much smaller.</li>
<li>You don&rsquo;t need the big PLC and cubicle in the machine room any more (I put the main controller, a Raspberry Pi 4 SBC, inside the Ethernet switch cubicle I needed anyway), but you do need several smaller controllers with mini installation cubicles, one in each room.</li>
</ul>
<p>All in all, you should be looking at about a 50% reduction in wiring costs.</p>
<p>Connecting the main controller and local controllers is trivial, they just need power and networking.</p>
<p>The bulk of the wiring work is needed at the local controllers. You still need to connect each switch, sensor and load directly to the local controller, but the cable runs are much shorter now, so at least wiring is less labor intensive and you can afford to be more lax when tagging the individual wires and cables.</p>
<p>With local cubicles, you get the additional benefit of having the option to do RCDs and other protections at the room level. This means that most faults will not trip the whole house, only the room affected. Lightning protectors will work better this way too, the wiring resistance between the primary and the secondary suppressor will help reduce the voltage spikes.</p>
<h4 id="complexity-1">Complexity</h4>
<p>The programming gets easier here. You can use &ldquo;smart&rdquo; (i.e. with some internal logic) or &ldquo;dumb&rdquo; (simple Modbus or MQTT relay/input boxes). Depending on the approach you take, you need to program either several small and simple controllers and/or implement some or all of the logic in your main smart home controller (e.g. Home Assistant).</p>
<p>If something fails, you deal with one room/block at the time and so the troubleshooting is much easier: Is it the local controller, programming, wiring or one of the devices? You go from hundreds to tens of potential sources of errors, especially on the code side.</p>
<h4 id="reliability-1">Reliability</h4>
<p>Both &ldquo;smart&rdquo; and &ldquo;dumb&rdquo; local controllers, as well as Linux SBCs are really reliable, as are Ethernet switches. And if something does break, they are all really cheap to replace. And if you use &ldquo;dumb&rdquo; local controllers, replacing them  simply means wiring them into the cubicle and configuring the new local controller address, a trivial repair.</p>
<h4 id="reconfigurability-1">Reconfigurability</h4>
<p>I already listed some important benefits of the distributed approach, but they pale in comparison to the improved reconfigurability:</p>
<ul>
<li>Want to build an annex or furnish an attic? Just run some Ethernet and power to the new room and add a local controller—the rest of the system stays exactly the same.</li>
<li>Want to add some new hard-wired equipment (e.g. home cinema with motorized screen, new lighting, ceiling fans, etc.) to an existing the room? If you have enough inputs/outputs on the local controller, it&rsquo;s simply a matter of wiring. If you run out of these, replacing the existing controller is much simpler that with a centralized PLC. But the simplest option of all is most often just to add an additional local controller, possibly in the same cubicle, if there is enough space, or near the new equipment.</li>
</ul>
<p>The flexibility of this approach is simply incredible.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Building a smart home is not a trivial matter, but you can make choices that make it significantly easier, cheaper, and more useable. Thinking about architecture/topology first will save you a lot of money and frustration in the long run. If you have a contractor, make sure you are on the same page on this!</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>Some Notes On Using E-paper Displays</title><link>https://nunrg.eu/posts/epaperesp32/</link><pubDate>Sat, 14 Sep 2024 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/epaperesp32/</guid><description>Practical notes on using e-paper displays: how they work, their advantages and drawbacks, and tooling for ESP32 development.</description><content:encoded><![CDATA[<h2 id="what-is-e-paper">What Is E-paper?</h2>
<p>E-paper displays are a niche display technology that <a href="https://en.wikipedia.org/wiki/Electronic_paper#Microencapsulated_electrophoretic_display">uses electric fields to move colored particles in a controlled way</a> to make a very paper-like display.</p>
<p>Most people first saw it in the smart price tags in supermarkets, another widespread use is for e-book readers, and there are used more and more in signage.</p>
<figure>
    <img src="/images/epaper/pricetag.png" alt="A modern e-paper price tag">
    <figcaption>A modern e-paper price tag (credit: Minew)</figcaption>
  </figure>
  
<figure>
    <img src="/images/epaper/kindle.jpg" alt="A Kindle e-book reader">
    <figcaption>A Kindle E-book Reader (Credit: Amazon)</figcaption>
  </figure>
  
<figure>
    <img src="/images/epaper/busstop.png" alt="A bus stop sign using e-paper">
    <figcaption>A bus stop sign using e-paper (credit: Papercast)</figcaption>
  </figure>
  
<p>These are all very good ways to use a technology with some pronounced advantages and disadvantages:</p>
<h3 id="advantages">Advantages:</h3>
<ul>
<li><strong>Extremely low power requirements:</strong> They only need power during refresh and consume no power once the image is displayed. This is perfect e.g. for a price tag that normally only needs to be changed once a day or less.</li>
<li><strong>Paper-like display:</strong> They really do look like printed paper with all the advantages this brings: no need for backlight, extremely broad viewing angle, no eye strain.</li>
</ul>
<h3 id="disadvantages">Disadvantages:</h3>
<ul>
<li><strong>Low refresh rate:</strong> You can forget playing video games on e-paper, it takes anywhere from hundreds of milliseconds (smaller black and white) to tens of seconds (large multi-color) to refresh the display.</li>
<li><strong>Limited color display options:</strong> E-paper display are monochrome (black and white) by default. Lately, gray-scale (4 or 16 level) and multicolor e-paper displays became available, but these are still limited to 3, 4 or 7 colors. You can&rsquo;t control them in the same way as RGB displays, so these displays are best for art that looks like comics, i.e. with clear lines and large single-colored surfaces.</li>
<li><strong>Full refresh involves blinking:</strong> When you initialize e-paper, this involves fast blinking; the duration depends on number of colors and size. Black and white displays can do partial refresh with no blinking, but even these must do a blinking full refresh from time to time.</li>
<li><strong>Not a mainstream technology yet:</strong>
When you decide to use e-paper, there are resources available, from libraries to displays themselves. It is a technology that has seen some widespread use, especially in e-readers. But e-paper displays are much less standardized and more diverse than other display technologies. One display might allow partial refresh, the other doesn&rsquo;t. A display that supports partial refresh might make a clean update or blink just once. The libraries I&rsquo;ve seen are sometimes poorly documented. You might stumble upon unforeseen problems and will have to study some issues for sure. It&rsquo;s doable, but using an OLED or LCD display is much more straightforward.</li>
</ul>
<h2 id="how-to-use-it">How To Use It?</h2>
<p>E-paper is normally controlled by a small, low-power micro controller to take advantage of the low power requirements. It does not require much computing power and I&rsquo;ve seen systems running on a simple Arduino Nano. Most systems will use a modern micro controller like a Pico or ESP32, but some people also use a Raspberry Pi, eg. to display computer- or AI-generated art.</p>
<p>I chose to use the ESP32 micro controller.</p>
<h3 id="why-use-an-esp32-for-e-paper">Why Use an ESP32 for E-paper?</h3>
<ul>
<li>It&rsquo;s ubiquitous: There are many development tools available and a big community of developers to help you on your way.</li>
<li>It has all the periphery you need: WiFi, Bluetooth, SPI, a lot of flash</li>
<li>It will work with very low power in deep sleep. This enables compact battery- and solar-powered systems with no need for extra wiring and power supply.</li>
</ul>
<h3 id="development-tools">Development Tools</h3>
<p>There are several options to develop apps for ESP 32:</p>
<ul>
<li>The <a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/index.html">ESP IDF</a>: A very powerful development environment that includes an IDE, but you can use it in VSCode and Platformio too.</li>
<li>The <a href="https://docs.espressif.com/projects/arduino-esp32/en/latest/">Arduino ESP32 core</a>: An Arduino core for ESP32 that you can use in Arduino and Platformio environments.</li>
</ul>
<h4 id="pros-and-cons-of-esp-idf">Pros and Cons of ESP IDF</h4>
<p>ESP IDF is the native development environment of the manufacturer, so you can use all the options the chips offer. I would prefer to use it, but there is a problem: Libraries for e-paper are limited.</p>
<p>I found only one promising library for e-paper, the <a href="https://github.com/martinberlin/cale-idf">CALE EPD</a>, but the GitHub project is not active lately (since 2023). This is a problem due to changes being introduced in the ESP IDF by ongoing development which mean you will have to fork and repair the library. The range of e-papers seems to be somewhat limited as well, even if it is still the most versatile option. In the end, I wasn&rsquo;t able to get it working, which is mostly due to me not being an experienced developer, but should still tell you something.</p>
<h4 id="pros-and-cons-of-the-arduino-core">Pros and Cons of the Arduino Core</h4>
<p>It looks like the Arduino core is not prioritized by Espressif, but it is reasonably up to date and does not have many limitations. Arduino is a mature environment that puts a lot of emphasis on being backwards compatible, which I really like, but the main advantage in this case is the fact you have a great e-paper library available:</p>
<p><a href="https://github.com/ZinggJM/GxEPD2">GxEPD2</a> is a really comprehensive e-paper library with very broad coverage of e-paper displays. It builds on Adafruit GFX, so the interface should be somewhat familiar if you worked with displays in Arduino before. It should work with all the chips Arduino supports, from the old Atmel ones, to the STM, Pico, ESP32 etc.</p>
<p>It&rsquo;s main disadvantage is the lack of comprehensive documentation There are extensive examples that will enable you to get where you are going, but there will be pitfalls.</p>
<p>The very best thing about this library is that you simply take the example project, wire the display, and select the options in the configuration files: it should work on the first try. There is also a lot of information about it in the internet.</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>How to Decide What You Need in an E-bike</title><link>https://nunrg.eu/posts/e_bike_selection/</link><pubDate>Sun, 23 Jun 2024 14:41:53 +0200</pubDate><guid>https://nunrg.eu/posts/e_bike_selection/</guid><description>A practical guide to choosing an e-bike: city, cargo, touring, mountain or gravel — and how to match the type and motor to your needs.</description><content:encoded><![CDATA[<p>Are you looking for a city bike? A cargo bike? Something to travel with? A mountain bike? A gravel bike? A moped resembling a bike? Don&rsquo;t really know? Read the descriptions below to decide:</p>
<h2 id="city-bikes">City Bikes</h2>
<figure>
    <img src="/images/bikes/citybike.jpg" alt="E-bike using a conversion kit from ESOULBIKE">
    <figcaption>A typical city bike, image credit: Freepik</figcaption>
  </figure>
  
<p>A city bike will normally have smoother tires and a heavier frame, your posture will be rather upright, they can have a cargo basket in the front and the back. Most people will use it for commuting, shopping or errands around the city, but you can do road trips too, as long as it is mostly on paved roads and the road is not extremely steep. It&rsquo;s a wonderful replacement for a scooter and can even replace your car, if you live in a city and your winters are not too harsh. This kind of e-bike works best with a small (250W) front hub motor and a small to medium size battery (~300Wh). You should probably buy a ready-made one, there is not much advantage to DIY here and they are the cheapest kind of e-bikes sold.</p>
<h2 id="cargo-bikes">Cargo Bikes</h2>
<figure>
    <img src="/images/bikes/cargobike.jpg" alt="A cargo bike with large front storage">
    <figcaption>Cargo bikes are really practical, image credit: Riese &amp; Müller</figcaption>
  </figure>
  
<p>A cargo bike is much like a city bike, but will have an even heavier frame and more storage space. Many even have a trailer or dedicated storage units. If you live in a large European city to the north of say, Adriatic, you probably see them every day. People use them for work (transport and deliveries for small businesses) and shopping. Like city bikes, there are many ready-made options here, but there is more space for DIY, since the needs are more diverse: Do you need a stronger motor for your heavy load? Do you drive a lot each day and need to make the battery larger? Would you like to convert your city bike to a cargo bike? Do you already have a cargo bike, but just want to electrify it?</p>
<p>Depending on your needs you could with a hub motor or a mid-motor setup. A (bigger) hub motor should still be enough for the lighter cargo bikes, as long as there are no very steep hills to climb. If this is not the case, a mid-motor setup is a must. You will need a bigger battery: Take the weight of a fully laden bike with you on it in kilograms (e.g. 200 kg) and multiply by 3-5 to get an estimate of what you need in Watt-hours. A somewhat oversized battery on a cargo bike is less of an issue weight-wise than on other types of bikes.</p>
<h2 id="mountain-bikes">Mountain Bikes</h2>
<figure>
    <img src="/images/bikes/mountainbike.jpg" alt="Male Cyclist Riding Mountain Bike On a Sunny Day">
    <figcaption>You can go anywhere with a mountain bike, 😎 image credit: Freepik</figcaption>
  </figure>
  
<p>A mountain bike should be familiar to you already. The posture is intermediate and the tires are usually thicker with more profile for a better grip on gravel and mud. Almost all have front suspension and some have rear suspension too. Mountain bikes with front suspension are a good candidate to DIY-electrify, but beware the rear suspension: it is likely it will be hard to work around the linkages and there is less space for the motor/battery. It&rsquo;s probably best to buy a ready made fully-suspended e-bike instead, but it can be expensive. It makes sense to pick a slightly larger frame size for an e-bike conversion, but if you like your existing bike, just go for it.</p>
<p>A mid-motor setup is a must with mountain bikes, the hub motors simply do not work well on very steep climbs. When I look at people electrifying mountain bikes, I see a clear tendency to oversize the motors and batteries. People will install 1kW or larger motors and huge 72V batteries, but this is not a bike anymore—we will discuss mopeds later. Instead, think about your physical fitness and size:</p>
<ul>
<li>A thin and fit person will hardly need the motor in any case, except for steep climbs. A 250W or 350W mid motor is more than enough for them, along with a small 350Wh battery. The reward for not over-sizing is a lighter bike and a much cheaper conversion.</li>
<li>Most people (medium height/build, not totally sedentary) should choose a 350W or 500W motor with ~500Wh battery.</li>
<li>Bigger people (tall and/or overweight, sedentary) will need a 500W or 750W motor. Choose the battery size by taking the combined weight of the fully laden bike (saddlebags and all) and the rider in kilograms (e.g. 150-170kg), and multiply by 4-5 to get the battery size in Wh. This should last you the whole day.</li>
</ul>
<h2 id="gravel-bikes">Gravel Bikes</h2>
<figure>
    <img src="/images/bikes/gravelbike.jpg" alt="Man Riding a Gravel Bike">
    <figcaption>Image credit: Future</figcaption>
  </figure>
  
<p>A gravel bike is a bit of a cross between a mountain and a road bike, with no suspension, but a sturdier build. The posture is usually quite low. Most of my comments about mountain bikes apply, but these bikes are normally chosen by fitter people and you will likely want to keep the motor and battery size on the lower end of the ranges I mention there.</p>
<h2 id="e-mopeds">E-mopeds</h2>
<figure>
    <img src="/images/bikes/3kwebike.jpg" alt="A big DIY e-bike with two large hub motors and an oversized battery">
    <figcaption>A big very DIY 3kW e-bike with two large hub motors and an oversized battery, image credit: DIY Electric Car Forums</figcaption>
  </figure>
  
<p>An e-moped is an e-bike with a massively oversized motor and battery. Several kilowatts of motors and thousands of watt-hours of batteries are not uncommon—all bets are off here. To me, these are simply electric motorbikes and out of scope here. But they do have (mostly unused) pedals, obviously make for a fascinating hobby, and seem to have a lively community of people building them.</p>
<h2 id="what-if-you-just-want-to-build-something">What If You Just Want To Build Something?</h2>
<figure>
    <img src="/images/bikes/conversionkit.jpg" alt="A typical mid-drive e-bike conversion kit">
    <figcaption>A typical Bafang-style mid-drive e-bike conversion kit, this one is from Dillenger</figcaption>
  </figure>
  
<p>What if you have no clue about what to build, but still want a DIY e-bike? The answer is simple: Get a second-hand mountain bike and buy a decent mid-drive kit in line with my recommendations for a mountain bike. It will be a cool thing to build, quite useful for anything from commuting to bike trips to errands, but it should not break the bank. After you ride it for a while, you will know what you want/need, and if you need a different bike, you will be able to recoup most of the costs by selling what you made.</p>
]]></content:encoded></item><item><title>My Journey With E-bikes</title><link>https://nunrg.eu/posts/e_bike_intro/</link><pubDate>Sun, 23 Jun 2024 11:08:40 +0200</pubDate><guid>https://nunrg.eu/posts/e_bike_intro/</guid><description>A personal journey from motorcycles to e-bikes — why I switched, what I learned and what to expect from electric bikes.</description><content:encoded><![CDATA[<p>I used to ride a motorcycle (a Kawasaki ER-5 for some 4 years and then a Honda CBF-1000 for 12 more) and for me, it was always about going places. I rode through most of the Alps, but also Pyrenees, Balkans, some Eastern Europe, almost always with a girlfriend and many times a biker friend was along too. You would fill up the panniers with some clothes and equipment and you could travel freely for a week, booking places to stay each afternoon as you went, making one big loop through as many Alpine passes as you could 😇 I still think this is one of the best ways to travel, period.</p>
<figure>
    <img src="/images/bikes/motorbike.jpg" alt="Picture of a two people on a Honda CBF1000 motorbike rounding a curve in the mountains">
    <figcaption>Fun in the Alps</figcaption>
  </figure>
  
<p>During that cool period people started writing about e-bikes and I got curious. I wanted to build one and check out all these cool new technologies on my own. It was a bit of a journey and I&rsquo;ll simplify somewhat:</p>
<ul>
<li>
<p>My first e-bike was a used city bike with a front hub motor and rear hub gearbox.</p>
<figure>
        <img src="/images/bikes/bike1.jpg" alt="Picture of a city e-bike with a big battery and me on it">
        <figcaption>Note the zip-tie and since departed hair 😄</figcaption>
      </figure>
      
<p>I got it from my neighbor as payment for helping them run some wires in their new guest house. It was missing a battery and the motor controller was one of the brand-locked ones where the manufacturer will not let you use it unless you had their battery too. So I ditched it and bought a cheap chinese motor controller and battery off e-bay.</p>
<p>It was a minimal investment to start with and so I 3d-printed a custom battery mount with controller housing. It worked well, considering, and I started using it for daily exercise; it lasted a couple of seasons until the old hub motor started developing insulation faults. I guess it was just a bit underpowered for the controller I bought. I removed all the electrics from the bike and sold it to a friend who still uses it.</p>
</li>
<li>
<p>Next I bought a used midsize mountain bike and converted it to rear hub motor system, building my first homebrew battery as well.</p>
<figure>
        <img src="/images/bikes/bike2.jpg" alt="Picture of a rear-motor mountain e-bike with a very angular battery housing and me on it">
        <figcaption>My design skills improved gradually 🥸</figcaption>
      </figure>
      
<p>It was similar to the city bike, but it had some more torque and could go up steep hills better. But the main advantage was not the motor, it was the mountain bike: I could finally be more relaxed on gravel and mud I sometimes encountered on my drives.</p>
<p>But the bike had a fatal flaw, the combined weight was too much for the rear wheel spokes and I spent more time repairing these than driving. Turns out rear spokes are a limiting factor for an e-bike with a big guy like me driving it and it gets worse with a rear hub motor. It also still had limited torque on the really steep climbs.</p>
</li>
<li>
<p>After rethinking everything, I decided to buy a larger used mountain bike and go with a mid-drive system. I was by now used to building homebrew batteries, so I only had to buy a Bafang BBS system and the rest was homebrew.</p>
<figure>
        <img src="/images/bikes/bike3.jpg" alt="Picture of a mid-drive mountain e-bike with partially dismantled battery and me holding the cover">
        <figcaption>I since learned to hide the ugly parts 😎</figcaption>
      </figure>
      
<p>This was the jackpot—mid drive (which lets you use gears better) and larger bike were the combination that let me drive anywhere. I bought some saddlebags and suddenly I was able to do the same kind of summer vacations I did with my motorbike, just with slightly shorter daily distances. The motorbike soon lost my interest, so I sold it after a few seasons :)</p>
<p>The bike needed just a few more upgrades: 4-piston disc brakes and a stronger rear wheel to handle the extra weight, and larger tires to make it even easier to drive anywhere I liked. Now, everything just works!</p>
</li>
</ul>
<p>I love e-bikes! I love how they make you a superman and give you the option to just go almost anywhere. I love how I can do vacations with them. When you drive and see an attraction at the side of the road, a motorbike requires extra work: first you need to stop safely, then get out of your hot protective gear, lock it up, the shoes are not really built for longer walks etc. With a bike, you just stop, hang the helmet on handlebars and throw a lock on the bike; 20 seconds and you can do whatever you like. (E-)bikes are pure freedom!</p>
<figure>
    <img src="/images/bikes/bike4.jpg" alt="Picture of me with my mountain e-bike on a trail">
    <figcaption>I love E-bikes! 🥰</figcaption>
  </figure>
  
<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>3D Design: Minimalist Bird Feeder</title><link>https://nunrg.eu/posts/bird_feeder/</link><pubDate>Thu, 21 Dec 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/bird_feeder/</guid><description>A minimalist 3D-printable bird feeder shared on Printables — easy to print and mount in a garden or on a balcony.</description><content:encoded><![CDATA[<p>I posted a new design on Printables, check it out: <a href="https://www.printables.com/model/688517-bird-feeder">Minimalist bird feeder</a></p>
<p><img loading="lazy" src="/images/feeder.png" alt="Minimalist bird feeder"  />
</p>
]]></content:encoded></item><item><title>3D Design: Cover for Ikea EKET Cube</title><link>https://nunrg.eu/posts/cube_cover/</link><pubDate>Tue, 05 Dec 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/cube_cover/</guid><description>A 3D-printable cover for the Ikea EKET cube — a tidy, customisable way to hide cables and clutter, free to download on Printables.</description><content:encoded><![CDATA[<p>This was long overdue, but I finally found a minute to post it on Printables: <a href="https://www.printables.com/model/668275-cover-for-ikea-eket-cube">Cover for Ikea EKET cube</a></p>
<p><img loading="lazy" src="/images/cube.jpg" alt="3d Design: Cover for Ikea EKET Cube"  />
</p>
]]></content:encoded></item><item><title>3D Design: Flexible Beer Cozy</title><link>https://nunrg.eu/posts/beer_cozy/</link><pubDate>Tue, 05 Dec 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/beer_cozy/</guid><description>A free, flexible 3D-printable beer cozy (koozie) design shared on Printables — a quick, fun print that keeps drinks cold.</description><content:encoded><![CDATA[<p>After a while, I posted a new design on Printables, check it out: <a href="https://www.printables.com/model/657879-beer-cozy-flexible-by-design">Beer cozy, flexible by design</a></p>
<p><img loading="lazy" src="/images/cozy.jpg" alt="Beer Cozy"  />
</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><item><title>Wiznet220IO Configuration Tool Problems in Windows 11</title><link>https://nunrg.eu/posts/wiznet/</link><pubDate>Thu, 16 Feb 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/wiznet/</guid><description>The Wiznet220IO configuration tool 1.1.2 fails on Windows 11 — a quick fix using compatibility mode to get the legacy tool running again.</description><content:encoded><![CDATA[<h2 id="wiznet220io-configuration-tool-112-does-not-work-in-windows-11-but-you-can-fix-it-using-compatibility-mode">Wiznet220IO Configuration Tool 1.1.2 Does Not Work in Windows 11, But You Can Fix it Using Compatibility Mode</h2>
<p><img loading="lazy" src="/images/wiznet.png" alt="Wiznet config page"  />
</p>
<p>If you happen to use the Wiznet220UI modules like me, you may have noticed that the configuration tool stopped working in Windows 11. As it starts up, there is a strange error message (it was probably originally written in Chinese and my OS does not know how to display that properly).</p>
<p>You can fix this by right-clicking the tool shortcut, choosing <em>Open in folder</em>, then right-clicking the shortcut, selecting <em>Properties</em> and then selecting the <em>Compatibility</em> tab. In my case, I set the compatibility to <em>Windows 8</em> and it started up normally. But it did not find any modules when I clicked <em>Search</em>. As I closed the files, Windows asked me if compatibility settings worked. I answered <em>No</em> and it set some different compatibility settings that solved all the problems.</p>
<p>It’s not rocket science, but it works 🙂</p>
<p>It is also a good reminder that most software tools will simply stop working in about a decade or so, unless the author is really diligent about updating them regularly. This will usually not matter for computer software like image and document editors, but it becomes <em>very</em> relevant when you are, for example, building a smart home; you probably don&rsquo;t want to strip all the hardware out every 10 years or so just because the configuration software is out of date.</p>
]]></content:encoded></item><item><title>One Picture To Explain Why Hydrogen Cars Are a Pipe Dream</title><link>https://nunrg.eu/posts/hydrogen_cars/</link><pubDate>Tue, 02 Nov 2021 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/hydrogen_cars/</guid><description>One chart explains why hydrogen cars are a pipe dream: the energy losses of the hydrogen path make battery EVs far more efficient.</description><content:encoded><![CDATA[<h2 id="hydrogen-cars-are-a-pipe-dream">Hydrogen Cars Are a Pipe Dream</h2>
<p>Specifically – it’s a dream of people who have the most pipes built: the oil and natural gas industry. They want to keep using these pipes, climate crisis or not, so they are trying their best to confuse us, using tactics perfected by the tobacco industry.</p>
<p>If you are not an expert, it is hard to understand these complex issues, but there are some shortcuts, like this one simple illustration:</p>
<figure>
    <img src="/images/240248459_10225660890164983_71627496691070094_n-1.jpg" alt="Hydrogen car to BEV efficiency comparison">
    <figcaption>Bossel, Ulf. “Does a Hydrogen Economy Make Sense?” Proceedings of the IEEE. Vol. 94, No. 10, October 2006.</figcaption>
  </figure>
  
<p>What does it all mean? If you want to drive about 7km with your car, you need about 1kWh of energy brought to your wheels. For a battery-powered EV, this means a power plant somewhere must produce about 1.5kWh of energy. On the other hand, if you have a typical hydrogen-powered car, it must produce about 4.3kWh (almost three times as much) of energy to produce, pump, deliver and convert the hydrogen needed to travel the same distance.</p>
<p>An EV wastes less than a third of energy while a hydrogen car wastes more than three quarters. Unfortunately, it is impossible to improve this in any significant way because of the underlying rules of physics. And keep in mind that this is just the main issue with hydrogen (there is a myriad of others; for starters, you will never ever be able to fill your car with hydrogen at home, period).</p>
<p>To be fair, hydrogen does have it’s uses, but these are all extremely capital-intensive and not something you can do at home. <a href="https://cleantechnica.com/2021/09/01/cleantech-talk-chemical-engineer-paul-martin-reflects-on-liebreichs-hydrogen-ladder-hopium-part-1/">This article</a> is a good starting point, if you want to learn more.</p>
<h2 id="but-what-about-synthetic-fuels">But What About Synthetic Fuels?</h2>
<p>Synthetic fuels are an extension of the hydrogen pipe dream.</p>
<p>Remember how inefficient hydrogen is? All synthetic fuels proposed so far (e-fuels, ammonia, etc.) use hydrogen as feedstock. This means you lose half the energy before you even start making the fuel itself from hydrogen, lose even more to the chemical conversion, and then you burn it in an internal combustion engine that wastes two thirds of the little energy you have left. Compared to that, even hydrogen is a less worse solution.</p>
<p>There are some use cases where it’s hard to use any other energy sources than hydrocarbons (long-haul aviation and shipping), but these would be better served by using bio-fuels, once we stop wasting them on fuel cars and trucks.</p>
<p>A good source of nuanced info about viable green energy sources is <a href="https://medium.com/@thefutureiselectric">Michael Barnard on Medium</a>.</p>
]]></content:encoded></item></channel></rss>