<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rafael</title>
    <description>The latest articles on DEV Community by Rafael (@mrcsharp).</description>
    <link>https://dev.to/mrcsharp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F40152%2F20c19bbb-e5bf-4a45-9b52-a5be5049ab26.png</url>
      <title>DEV Community: Rafael</title>
      <link>https://dev.to/mrcsharp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrcsharp"/>
    <language>en</language>
    <item>
      <title>Controlling My AC Unit With nanoFramework</title>
      <dc:creator>Rafael</dc:creator>
      <pubDate>Mon, 17 Jul 2023 05:18:39 +0000</pubDate>
      <link>https://dev.to/mrcsharp/controlling-my-ac-unit-with-nanoframework-1a5k</link>
      <guid>https://dev.to/mrcsharp/controlling-my-ac-unit-with-nanoframework-1a5k</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3inam8zovjczuki43u53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3inam8zovjczuki43u53.png" alt="nanoFramework" width="299" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This blog post first appeared on &lt;a href="https://blog.mrcsharp.dev" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;My room's AC unit (Panasonic) is quite old. It does a great job cooling/heating the place after all these years, but also lacks any "smart" capabilities found on modern units.&lt;/p&gt;

&lt;p&gt;Personally, I am not a fan of making everything at my home internet-connected for reasons I won't delve into now, and I certainly don't want my AC unit to be internet-connected either. However, &lt;em&gt;I want to be able to expand on its functionality as needed&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The unit has a basic set of features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set mode (Cool, Heat, Dry, etc...)&lt;/li&gt;
&lt;li&gt;Set temperature&lt;/li&gt;
&lt;li&gt;Swing&lt;/li&gt;
&lt;li&gt;Fan speed&lt;/li&gt;
&lt;li&gt;ON/OFF Timer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the timer functionality, I can set a time for it to switch itself ON and another time for it to switch OFF. This is helpful, but is quiet limited as it only accepts time as a trigger and doesn't allow specifying a temperature/mode other than the one it was set to before the On/Off timer is set.&lt;/p&gt;

&lt;p&gt;I'd like it to work based on the room temperature instead.&lt;br&gt;
In winter, it should turn on in HEAT mode when the room temperature drops below a specified threshold and off when it goes above another threshold. In summer, it should turn on when the temperature is above a certain threshold and off when it gets below another threshold.&lt;/p&gt;

&lt;p&gt;Yes, it has a thermostat but it is not very good. In summer, it makes the room too cold and in winter, it gets too hot. I suspect that if there was a temperature sensor at the other end of the room, it would help.&lt;/p&gt;

&lt;p&gt;Additionally, Australian weather can be very humid. The AC unit has a "Dry" mode which activates its built-in dehumidifier but there is no built-in way to activate it based on air humidity levels.&lt;/p&gt;

&lt;p&gt;Being a programmer, naturally I thought I could build something to automate the functions of the AC unit based on temperature and humidity parameters. And while I am at it, I could have it report the state to the isolated HomeAssistant instance on my network.&lt;/p&gt;

&lt;p&gt;So to summarize, the goals of this project are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Turn the AC unit on/off and set mode &amp;amp; temperature based on room temperature.&lt;/li&gt;
&lt;li&gt;Turn the AC unit on/off and enable the dehumidifier when humidity goes above a set threshold.&lt;/li&gt;
&lt;li&gt;Nice to have: Report the AC unit status to the internal HomeAssistant instance on my network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post is about my journey to achieve this and all the problems/hurdles I ran into. Spoiler: I actually did it at the end.&lt;/p&gt;
&lt;h2&gt;
  
  
  How
&lt;/h2&gt;

&lt;p&gt;After I had the goals of the project defined, I started thinking about how I could achieve that. The first problem I had to solve was:&lt;/p&gt;
&lt;h3&gt;
  
  
  Communication
&lt;/h3&gt;

&lt;p&gt;Like I mentioned above, the AC unit is old and has no smart features or APIs I could hook into. I didn't want to solder wires into a PCB deep into its guts. I wanted this solution to be completely unobtrusive.&lt;/p&gt;

&lt;p&gt;That left me with the remote control and infrared (IR) channel as the communication interface. My thinking was that if I could somehow capture a signal from the remote, I could then replay it at the AC unit.&lt;/p&gt;

&lt;p&gt;In theory, I could capture an "ON" signal, an "OFF" signal, a "COOL/HEAT" signal, and, somehow, a signal for temperature control. At this point, I only knew how remote controls worked at a high level (Remote has IR LED, IR LED sends signal, IR Receiver receives signal and device does something). I soon learned there is a lot more to IR communication than just that. A lot more.&lt;/p&gt;

&lt;p&gt;For now, the solution was to &lt;em&gt;somehow&lt;/em&gt; send commands through the IR channel.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tech stack
&lt;/h3&gt;

&lt;p&gt;I am a .Net dev. I love .Net and last year I was introduced to &lt;a href="https://www.nanoframework.net/" rel="noopener noreferrer"&gt;nanoFramework&lt;/a&gt;. This is an open-source project to bring C# and .NET to the world of embedded systems. There are various reasons to choose nanoFramework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to get started with as a beginner in the world of IoT and embedded system programming.&lt;/li&gt;
&lt;li&gt;It is a serious, open-source project used in personal and commercial solutions and not only to blink LEDs.&lt;/li&gt;
&lt;li&gt;As a developer, you can be extremely productive with .NET and C# when compared to the traditional C/C++.&lt;/li&gt;
&lt;li&gt;You can also use Visual Studio with nanoFramework to write, deploy, and debug code directly on the target device. It is very easy to place breakpoints, execute the program and step through the code line by line while inspecting variables and other state. All within the Visual Studio IDE.&lt;/li&gt;
&lt;li&gt;Dependency management is available through NuGet where nf and the community publish a lot of libraries.&lt;/li&gt;
&lt;li&gt;nanoFramework is also very easy to extend and add new stuff or improve what's already there.&lt;/li&gt;
&lt;li&gt;There's a great community that's very friendly and supportive.&lt;/li&gt;
&lt;li&gt;But most importantly, it is just so damn fun to work with!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you can see why nf is my choice for this side project. Keep in mind that there are other projects to bring C# &amp;amp; .Net to the micro-controller world such as &lt;a href="https://www.wildernesslabs.co/" rel="noopener noreferrer"&gt;Meadow&lt;/a&gt; &amp;amp; TinyCLR by &lt;a href="https://www.ghielectronics.com/" rel="noopener noreferrer"&gt;GHI&lt;/a&gt; but I don't have much experience with them at this time.&lt;/p&gt;

&lt;p&gt;The next thing I had to figure was probably the easiest in this project:&lt;/p&gt;
&lt;h3&gt;
  
  
  Sensing temperature &amp;amp; humidity
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/post/using-dht22-with-nanoframework/"&gt;In my previous post&lt;/a&gt;, I used the DHT22 sensor to get the room temperature and humidity level.&lt;/p&gt;

&lt;p&gt;DHT22 is simple to use, requires 3-5V power, and provides the measurements using a digital signal on a single pin. Perfect for use with a Micro-Controller Unit.&lt;/p&gt;

&lt;p&gt;Ok, so I know what to use to get the temp &amp;amp; humidity. How can I talk to the DHT22 module?&lt;/p&gt;
&lt;h3&gt;
  
  
  MCU
&lt;/h3&gt;

&lt;p&gt;ESP32 is a very good choice for this project. It has support for IR communication using its built-in RMT module. It is also compatible with nanoFramework making it ideal for what I am doing here.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Journey
&lt;/h2&gt;

&lt;p&gt;The first thing I needed to do was to build something that can capture signals from the AC remote control and store them somehow for further analysis and replay.&lt;/p&gt;

&lt;p&gt;ESP32 has an RMT (Remote Control Transceiver) module and the folks on nanoFramework's lively &lt;a href="https://discord.gg/gCyBu8T" rel="noopener noreferrer"&gt;discord server&lt;/a&gt; pointed me towards a nanoFramework library that exposes the RMT hardware to the managed C# code. The library can be found &lt;a href="https://www.nuget.org/packages/nanoFramework.Hardware.Esp32.Rmt/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wired up an IR Receiver to my ESP32 dev board as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38rpl9kr3gipbkrjvfwt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38rpl9kr3gipbkrjvfwt.jpg" alt="ESP32" width="649" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that in place, the next step is to write some code to capture incoming signals from the AC remote. nanoFramework has a sample project showing how the RMT nuget library can be used. Based on that sample, I wrote something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rxChannel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ReceiverChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RxPinNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClockDivider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1us clock ( 80Mhz / 80 ) = 1Mhz&lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnableFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// filter out 100Us / noise &lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetIdleThresold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;40000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 40ms based on 1us clock&lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReceiveTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;recivedSignal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAllItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Received a signal with &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;receivedSingal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; items:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pulse&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;receivedSignal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Pulse: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pulse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Level0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pulse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pulse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Level1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pulse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does is it initializes a new RMT Receiver Channel and sets some reasonable defaults as per the comments. Then it enters an infinite loop to listen for incoming infrared signals.&lt;/p&gt;

&lt;p&gt;ESP32 RMT module has a simple way of representing the signal. You can read about it in details &lt;a href="https://docs.espressif.com/projects/esp-idf/en/v4.3.5/esp32/api-reference/peripherals/rmt.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The main thing to know is that a signal is made of a collection of RMT Items. Each item represents a single "pulse" and is made of 2 pairs of values. Each pair consists of the duration in ticks and the signal level (HIGH vs. LOW).&lt;/p&gt;

&lt;p&gt;The above code should've outputted something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Received a signal with 64 items:
Pulse: true 500 - false 1000
Pulse: true 250 - false 500
etc...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here, I ran into the first set of issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There was a lot of random, 1-item signals being picked up. (Interferance?)&lt;/li&gt;
&lt;li&gt;The remote signal seemed to have a variable length. Sometimes it would be 48 pulses long, sometimes only 35 (and other "random" numbers) even for the same button press.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wasn't sure why I was getting a lot of 1-pulse signals in my output window. Increasing the &lt;code&gt;EnableFilter&lt;/code&gt; duration parameter improved things but it still let some 1-pulse signals through frequently.&lt;/p&gt;

&lt;p&gt;Doing some research helped me learn that there is usually a lot of infrared interference around us. Sunlight is a big source but there are others. It is why &lt;strong&gt;IR Signals are modulated&lt;/strong&gt;. Manufacturers typically use a 38KHz carrier wave but it can range from 36KHz to 46KHz.&lt;/p&gt;

&lt;p&gt;This didn't explain why I was seeing those short signals. My theory at this point was noise from the circuit and I opted to write some basic code to filter out such signals. After all, this was a temporary device put together just to record some signals for analysis and it was going to get disassembled afterwards. No reason to invest more time into this, right?&lt;/p&gt;

&lt;p&gt;That solution worked and now I was left with the second problem: Is my AC unit remote really sending variable length signals for the same button presses? This made absolutely no sense and it took a while to get this sorted out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variable length signal? No way!
&lt;/h3&gt;

&lt;p&gt;There are various IR protocols out there. Each manufacturer usually uses one of them for their products. These protocols define some signal characteristics including but not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signal carrier frequency&lt;/li&gt;
&lt;li&gt;Header pulse length&lt;/li&gt;
&lt;li&gt;Logical 0 and 1 pulse length&lt;/li&gt;
&lt;li&gt;If the signal should repeat and how&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is up to the manufacturer to define what data to transmit. Some simple remote controls such as TV remotes could have a specific command for every button, while other remotes (like my AC unit's one) send out the entire state of the remote on every button press. More on that later.&lt;/p&gt;

&lt;p&gt;For example, the most popular IR protocol is NEC. This protocols specifies that a signal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consists of 8 bit address &amp;amp; 8 bit command&lt;/li&gt;
&lt;li&gt;uses pulse distance modulation to represent digital data&lt;/li&gt;
&lt;li&gt;has a carrier frequency of 38kHz&lt;/li&gt;
&lt;li&gt;pulse is always 560µs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Logical 1 &amp;amp; 0 are represented as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0avkms62m2vfbm1wlag7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0avkms62m2vfbm1wlag7.png" alt="NEC IR Signal" width="425" height="125"&gt;&lt;/a&gt;&lt;br&gt;
(Source: &lt;a href="https://www.sbprojects.net/knowledge/ir/nec.php" rel="noopener noreferrer"&gt;NEC Protocol&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;But what about my AC Unit's remote control? Is it actually sending variable length signals even for the same button?&lt;/p&gt;

&lt;p&gt;Well, no. According to some arduino IR libraries, Panasonic AC remotes send 216 bits of data. This is not even close to what I was getting above. This led me to believe that there was a problem with my setup: either hardware or software.&lt;/p&gt;

&lt;p&gt;In terms of hardware, there wasn't much going on. An IR receiver connected directly to the MCU. So I decided to turn my attention to the software side of things.&lt;/p&gt;

&lt;p&gt;My theory as to why I was seeing a variable number of pulses was due to some timing issues. I played around with &lt;code&gt;SetIdleThreshold&lt;/code&gt; and found that setting it to &lt;code&gt;12000&lt;/code&gt; ticks worked best. This is equal to waiting for 12ms for edges. If none are detected, then the RMT module would stop listening for more signals and return what it had already detected.&lt;/p&gt;

&lt;p&gt;This change gave me a stable number of pulses. At this point, I was constantly getting 64 pulses on every button press.&lt;/p&gt;

&lt;p&gt;But 64 != 216 bits. So what is going on here?&lt;/p&gt;
&lt;h4&gt;
  
  
  nanoFramework's relationship with ESP IDF
&lt;/h4&gt;

&lt;p&gt;nanoFramework integrates with the ESP IDF framework. This integrations allows nanoFramework to communicate with the various hardware functions that ESP32 MCUs offer (GPIO, RMT, I2C, Wifi, Bluetooth, etc...).&lt;/p&gt;

&lt;p&gt;ESP IDF methods are then exposed by nanoFramework and made available to the C# managed code. Libraries such as the nanoFramework ESP32 RMT one make use of these native methods.&lt;/p&gt;

&lt;p&gt;This fact led me to consult with the ESP IDF documentation where I found this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fks3ee2ayd2ubl1m8y2m7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fks3ee2ayd2ubl1m8y2m7.png" alt="ESP32 RMT Docs" width="659" height="864"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And more clearly, on the web version:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpzr99fa2z7indfapmtw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpzr99fa2z7indfapmtw.png" alt="ESP32 RMT Docs" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What this means is that ESP allocates 8 blocks of 64x32bit of internal RAM shared by the 8 RMT channels. Each channel gets 1 block (64x32bits).&lt;/p&gt;

&lt;p&gt;This perfectly explained why I was now constantly getting 64 pulses.&lt;/p&gt;

&lt;p&gt;Luckily, the docs mention that the number of allocated blocks to a channel can be increased using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;esp_err_t&lt;/span&gt; &lt;span class="n"&gt;rmt_set_mem_block_num&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rmt_channel_t&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;rmt_mem_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is great! I went looking for an equivalent in nanoFramework's RMT library but sadly, it was not implemented.&lt;/p&gt;

&lt;p&gt;nanoFramework's RMT implementation exposed only some of the capabilities of the ESP RMT module and it lacked many options already available in ESP IDF. This was an opportunity for me to contribute an improvement to the nanoFramework project!&lt;/p&gt;

&lt;p&gt;Initially, I thought about adding support only for the method mention above. This would've been a quick contribution and would get me back to working on my project sooner. But it didn't feel right and the more I looked at the implementation the more I leaned towards a simple API refactor and exposing &lt;strong&gt;&lt;em&gt;ALL&lt;/em&gt;&lt;/strong&gt; the RMT configs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Updating the nanoFramework RMT library
&lt;/h4&gt;

&lt;p&gt;As mentioned earler, the nf library relies on native ESP IDF methods exposed by nf. This meant that any changes and improvements I make to the RMT library would first start in the nanoFramework firmware itself.&lt;/p&gt;

&lt;p&gt;The nanoFramework discord is pretty amazing. I had a thread running with a few people (Including the founders of nf) discussing questions I had and how to approach this improvement/refactor. I made it clear that I had no experience with C/C++ code but I was keen on doing what is needed to fix the issue at hand. I was pointed towards the &lt;a href="https://docs.nanoframework.net/content/building/using-dev-container.html" rel="noopener noreferrer"&gt;nanoFramework dev container approach&lt;/a&gt; which makes use of the dev containers feature in Visual Studio Code to setup a fully functional nf development environment within Docker.&lt;/p&gt;

&lt;p&gt;It didn't take much effort to get that setup and I started diving into the codebase and making changes to the RMT implementation.&lt;/p&gt;

&lt;p&gt;The result of the refactoring and improvement work was:&lt;/p&gt;

&lt;p&gt;On the native side (nanoFramework Firmware) most of the changes were about introducing new C methods exposing RMT configs. Additionally, some changes were to add C#/Managed code-compatible types as needed.&lt;/p&gt;

&lt;p&gt;On the managed side (nanoFramework RMT nuget library), the changes were as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduce a new &lt;code&gt;RmtChannelSetting&lt;/code&gt; containing various config options for RMT channels. This is an abstract class with 2 classes inheriting from it:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ReceiverChannelSettings&lt;/code&gt; for receiver-specific channel configs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TransmitterChannelSettings&lt;/code&gt; for transmitter-specific channel configs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Expose all ESP IDF RMT Configs through the various &lt;code&gt;*ChannelSetting&lt;/code&gt; classes.&lt;/li&gt;

&lt;li&gt;Introduce a new &lt;code&gt;RmtChannel&lt;/code&gt; abstract class exposing common functionality and properties used by receiver and transmitter channels. It has 2 inheritors:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TransmitterChannel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReceiverChannel&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These changes meant bumping the library version number from 1.x to 2.x as it introduced breaking changes. However, migrating to the new APIs was very straightforward. We have the following code snippit on the RMT library which shows how easy it is:&lt;/p&gt;

&lt;p&gt;V1.x API Surface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// creating a transmit channel&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;txChannel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TransmitterChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TxPinNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClockDivider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CarrierEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdleLevel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RmtCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// add more commands...&lt;/span&gt;

&lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// creating a receive channel&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rxChannel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ReceiverChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RxPinNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClockDivider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1us clock ( 80Mhz / 80 ) = 1Mhz&lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnableFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// filter out 100Us / noise &lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetIdleThresold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;40000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 40ms based on 1us clock&lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReceiveTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In V2.x the above must be re-written as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;txChannelSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TransmitChannelSettings&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TxChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ClockDivider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;EnableCarrierWave&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;IdleLevel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;txChannel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TransmitterChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txChannelSettings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RmtCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// add more commands...&lt;/span&gt;

&lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rxChannelSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ReceiverChannelSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pinNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RxChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;EnableFilter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;FilterThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;IdleThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;40_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ReceiveTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rxChannel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ReceiverChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rxChannelSettings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;rxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clearBuffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the big change is around declaring the channel configs in a separate class and passing that instance as a parameter to the channel object constructor.&lt;/p&gt;

&lt;p&gt;Some of the new configurations that were added to the library as part of this were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NumberOfMemoryBlocks&lt;/code&gt;: set the number of memory blocks that a channel can own. &lt;strong&gt;&lt;em&gt;This is what I needed&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EnableDemodulation&lt;/code&gt;: enable/disable signal de/modulation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CarrierWaveFrequency&lt;/code&gt;: set the carrier wave frequency used in modulating/demodulating the IR signal.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CarrierWaveDutyPercentage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EnableLooping&lt;/code&gt;: allows to repeat the signal sent&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LoopCount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EnableSignalInverter&lt;/code&gt; enables/disables a built-in ESP32 features that inverts the signal using hardware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once these pull requests were merged in, I was back working on my AC automation project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decoding the Panasonic AC Unit Remote Signal
&lt;/h3&gt;

&lt;p&gt;Through work done by other people in the open source community (referenced at the end) I learned that AC unit remote controls generally transmit the entire state of AC unit that is stored in the remote control.&lt;/p&gt;

&lt;p&gt;This is generally true for AC remotes that have an LCD such as this one:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhmnmv3hfjubkixw0qsp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhmnmv3hfjubkixw0qsp.jpg" alt="Panasonic IR Remote" width="587" height="1146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What this meant is that my plan to simply record a signal from button press and play it back would not work. I need to set the mode &amp;amp; temperature at the same time and in order to do that, I have to replicate the state of a remote control in my code, manipulate it to set the parameters as I need and then transmit that state data via IR.&lt;/p&gt;

&lt;p&gt;The plan has now shifted towards decoding the remote control signal rather than simply replaying it.&lt;/p&gt;

&lt;p&gt;Luckily, I didn't have to start from scratch. The same work I referenced above proved to be a great starting point. It listed out some important facts about the contents of the signal and some details that saved me a lot of time.&lt;/p&gt;

&lt;p&gt;The signal is made of 2 frames of data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frame 1:

&lt;ul&gt;
&lt;li&gt;8 bytes long.&lt;/li&gt;
&lt;li&gt;Constant value equal to &lt;code&gt;0x4004072000000060&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sent in MSB order.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Frame 2: 

&lt;ul&gt;
&lt;li&gt;18 bytes long.&lt;/li&gt;
&lt;li&gt;Transmitted as LSB8 (more info on this later).&lt;/li&gt;
&lt;li&gt;First 5 bytes are equivalent to bytes from Frame 1 but in LSB8.&lt;/li&gt;
&lt;li&gt;The remaining bytes transmit the state of the remote.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CRC:

&lt;ul&gt;
&lt;li&gt;Calculated by adding all 18 bytes of Frame 2.&lt;/li&gt;
&lt;li&gt;Transmitted as LSB8.&lt;/li&gt;
&lt;li&gt;Last byte in the transmission.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to transmit data on this Panasonic Protocol, the follow timings must be adhered to:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Time (micro-second, approx.)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Header&lt;/td&gt;
&lt;td&gt;3500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header Space&lt;/td&gt;
&lt;td&gt;1750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pulse&lt;/td&gt;
&lt;td&gt;435&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;'Zero' Bit Space&lt;/td&gt;
&lt;td&gt;435&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;'One' Bit Space&lt;/td&gt;
&lt;td&gt;1300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modulation&lt;/td&gt;
&lt;td&gt;38Khz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tolerance&lt;/td&gt;
&lt;td&gt;Max. 30%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With this info, I only had to focus on the second frame of the signal.&lt;/p&gt;

&lt;p&gt;I started this by writing an "IR Decoder" program that is deployed to my ESP32 that I connected to an IR receiver (as per the fritzing diagram above).&lt;/p&gt;

&lt;p&gt;The complete utility program can be found on github &lt;a href="https://github.com/MrCSharp22/nanoframework-esp32-panasonic-ir-decoder" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Deploying it to the ESP32 dev board results in the following when a signal is received from the remote control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frame 1:
=== RAW === | === HEX ===
 0100 0000  |     0x40
 0000 0100  |     0x04
 0000 0111  |     0x07
 0010 0000  |     0x20
 0000 0000  |     0x00
 0000 0000  |     0x00
 0000 0000  |     0x00
 0110 0000  |     0x60
Frame 2:
=== RAW === | === HEX === | === LSB8 === | === MSB HEX ===
 0100 0000  |     0x40    | 0000 0010  |    0x02
 0000 0100  |     0x04    | 0010 0000  |    0x20
 0000 0111  |     0x07    | 1110 0000  |    0xE0
 0010 0000  |     0x20    | 0000 0100  |    0x04
 0000 0000  |     0x00    | 0000 0000  |    0x00
 0001 1100  |     0x1C    | 0011 1000  |    0x38
 0000 0100  |     0x04    | 0010 0000  |    0x20
 0000 0001  |     0x01    | 1000 0000  |    0x80
 1000 1100  |     0x8C    | 0011 0001  |    0x31
 0000 0000  |     0x00    | 0000 0000  |    0x00
 0000 0000  |     0x00    | 0000 0000  |    0x00
 0111 0000  |     0x70    | 0000 1110  |    0x0E
 0000 0111  |     0x07    | 1110 0000  |    0xE0
 0000 0000  |     0x00    | 0000 0000  |    0x00
 0000 0000  |     0x00    | 0000 0000  |    0x00
 1000 0001  |     0x81    | 1000 0001  |    0x81
 0000 0000  |     0x00    | 0000 0000  |    0x00
 0000 0000  |     0x00    | 0000 0000  |    0x00
 0111 1110  |     0x7E    | 0111 1110  |    0x7E      &amp;lt;&amp;lt;&amp;lt; CRC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having the signal output formatted like this was very important. I was then able to use &lt;a href="https://winmerge.org/" rel="noopener noreferrer"&gt;WinMerge&lt;/a&gt; I could easily see which bits flipped with each button press and how the state was being represented.&lt;/p&gt;

&lt;p&gt;For example, this shows the difference between sending an OFF command (LEFT) vs. sending an ON command (RIGHT):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpxdbm3oyq8wemzainzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpxdbm3oyq8wemzainzb.png" alt="WinMerge Panasonic IR signal comparision" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see how bit 0 in byte 13 indicates what state (ON or OFF) the AC should be on: 0 == OFF, 1 == ON.&lt;/p&gt;

&lt;p&gt;I used this appraoch for the other buttons and functions on the remote and my results matched what I had seen in other resources online. Here's the breakdown of all the commands and their representation in the data frame:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Byte #&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Description / Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;First 8 bytes are always the same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;This is the device/manufacturer ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Always equal to &lt;code&gt;0x4004072000000060&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FRAME 2&lt;/td&gt;
&lt;td&gt;====================&lt;/td&gt;
&lt;td&gt;====================================&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;The first 5 bytes in frame 2 always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;match the first 4 bytes from frame 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;and equal to 0x4004072000 but in the LSB8 format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Mode, On/Off Switch and Timer&lt;/td&gt;
&lt;td&gt;Bit 0 is On/Off (1/0). Bits 4..7 are mode. Bit 3 is special. More info below.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Temperature&lt;/td&gt;
&lt;td&gt;Divide by 2 to get actual temp in C.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Unknown&lt;/td&gt;
&lt;td&gt;0x80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Swing &amp;amp; Fan&lt;/td&gt;
&lt;td&gt;The 4 MSB bits = FAN / 4 LSB bits = Swing. Refer to table below for codes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;Unknown&lt;/td&gt;
&lt;td&gt;0x00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;Timer (ON)&lt;/td&gt;
&lt;td&gt;Starting value: 0x00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;Timer (ON/OFF)&lt;/td&gt;
&lt;td&gt;Starting value: 0x08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;Timer (OFF)&lt;/td&gt;
&lt;td&gt;Starting value: 0x80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;Profile&lt;/td&gt;
&lt;td&gt;Refer to profile table for info (WIP/Not 100% correct)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;Unknown&lt;/td&gt;
&lt;td&gt;0x00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;Model (Potentially)&lt;/td&gt;
&lt;td&gt;0x80 (Static?)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;Time (Part 1)&lt;/td&gt;
&lt;td&gt;The value in this part is equal to the time in minutes. Max 255 minutes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;Time (Part 2)&lt;/td&gt;
&lt;td&gt;The value in this part is how many 256 minutes are in the current time.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;CRC&lt;/td&gt;
&lt;td&gt;Calculated by adding all the bytes in frame 2.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  On/Off, Mode, and Timer Function
&lt;/h5&gt;

&lt;p&gt;Byte #13&lt;/p&gt;

&lt;p&gt;This byte packs a lot of info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bit #0: On/Off command. 0 = Turn off AC unit. 1 = Turn on AC unit.&lt;/li&gt;
&lt;li&gt;Bit #1: Timer option. 1 = Enable ON Timer option.&lt;/li&gt;
&lt;li&gt;Bit #2: Timer option. 1 = Enable Off Timer option.&lt;/li&gt;
&lt;li&gt;Bit #3: Execute command. 1 = AC unit will execute the command immediately. 0 = AC unit will receive the command but will not execute it.&lt;/li&gt;
&lt;li&gt;Bit #4 to #7 indicates the operation mode as per below table:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit Mask&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0110&lt;/td&gt;
&lt;td&gt;FAN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0010&lt;/td&gt;
&lt;td&gt;DRY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0011&lt;/td&gt;
&lt;td&gt;COOL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0100&lt;/td&gt;
&lt;td&gt;HEAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0000&lt;/td&gt;
&lt;td&gt;AUTO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Swing &amp;amp; Fan
&lt;/h5&gt;

&lt;p&gt;Byte #16. The possible values as per the below tables:&lt;/p&gt;

&lt;h6&gt;
  
  
  Swing
&lt;/h6&gt;

&lt;p&gt;4 LSB bits on Byte #16&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit Mask&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1111&lt;/td&gt;
&lt;td&gt;AUTO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0001&lt;/td&gt;
&lt;td&gt;Horizontal (1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0010&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0011&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0100&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0101&lt;/td&gt;
&lt;td&gt;Vertical or Ground (5)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h6&gt;
  
  
  Fan
&lt;/h6&gt;

&lt;p&gt;4 MSB bits on Byte #16&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit Mask&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1010&lt;/td&gt;
&lt;td&gt;AUTO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0011&lt;/td&gt;
&lt;td&gt;Slowest (1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0100&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0101&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0110&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0111&lt;/td&gt;
&lt;td&gt;Fastest (5)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Current Time
&lt;/h5&gt;

&lt;p&gt;The time is represented in number of minutes since midnight (00:00) by 2 bytes: #24 &amp;amp; #25.&lt;/p&gt;

&lt;p&gt;Byte #24 represents the minutes since midnight. But, because the max value of a byte is 255, byte #24 can't go higher than that and so byte #25 will increment by 1 each time we reach the limit in byte #24.&lt;/p&gt;

&lt;p&gt;So to read the time, do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read byte #25.&lt;/li&gt;
&lt;li&gt;Multiply its value by &lt;code&gt;256&lt;/code&gt; (255 = 0xFF in byte #24 but when the value is 256, byte #24 becomes 0x00 and byte #25 becomes 0x01).&lt;/li&gt;
&lt;li&gt;Read byte #24.&lt;/li&gt;
&lt;li&gt;Add the 2 values.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;TimeSpan&lt;/code&gt; (from the .Net BCL) to get the time in hour:minute format.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;Time is 00:02&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;byte&lt;/th&gt;
&lt;th&gt;value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;0x02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;0x00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Time is 04:43&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;byte&lt;/th&gt;
&lt;th&gt;value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;0x1B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;0x01&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Timer
&lt;/h5&gt;

&lt;h6&gt;
  
  
  On/Off Timer
&lt;/h6&gt;

&lt;p&gt;To enable/disable, set bit #1 in byte #13 to 1 (ON)/0 (OFF). This is for ON Timer. Off timer is the same but the state is stored in bit #2.&lt;/p&gt;

&lt;p&gt;Time is stored in byte #18, #19, and #20 (Yes, this took a while to figure out) in simiar fashion as how Current Time is represented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Byte #18 is number of minutes in increments of 10 (0x10, 0x14, etc...) and used for the ON timer.&lt;/li&gt;
&lt;li&gt;Byte #19 is made of 2 parts:

&lt;ul&gt;
&lt;li&gt;The lower 4 bits increment by one when byte #18 overflows. Each increment equals 256 minutes.&lt;/li&gt;
&lt;li&gt;The upper 4 bits represent the continuation of the Off timer value from byte #20.&lt;/li&gt;
&lt;li&gt;The starting value of byte #19 is 0x08.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Byte #20 is made of 2 parts as well:

&lt;ul&gt;
&lt;li&gt;The lower 4 bits are basically the upper 4 bits of the minutes (upper 4 bits of byte #19). You take these lower 4 bits then put the upper 4 bits next to it to get the value. This is for the OFF timer. 

&lt;ul&gt;
&lt;li&gt;Example: (Byte #20 lower 4 bits) (Byte #19 upper 4 bits)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;When minutes go beyond 256, the upper 4 bits increment by 1. &lt;/li&gt;

&lt;li&gt;The starting value of byte #20 is 0x80.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;ON Timer: 00:00&lt;br&gt;
OFF Timer: 00:00&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Byte&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;0x00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;0x08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;0x80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ON Timer: 05:00&lt;br&gt;
OFF Timer: 04:20&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Byte&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;0x2C&lt;/td&gt;
&lt;td&gt;0x2C = 44. So 44 Minutes on ON Timer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;0x49&lt;/td&gt;
&lt;td&gt;Starting value is 0x08. Ignore the upper 4 bits and value would be 0x09. 0x09 - 0x08 = 0x01 = 256 minutes. Add this to minutes from byte #18 and you have ON Timer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;0x90&lt;/td&gt;
&lt;td&gt;0x90 - 0x80 = 0x10. Take the result and append the upper 4 bits of byte 18 to get the off time in minutes. So 0x104 = 260 (4hrs and 20 minutes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Programmer Guidance:&lt;/p&gt;

&lt;p&gt;For byte #19, to get the upper 4 bits, right shift by 4:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;upper4Bits&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x49&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// result: 0x04&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the lower 4 bits, you can isolate them using an &amp;amp; operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lower4Bits&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x49&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;0xF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// result: 0x09&lt;/span&gt;

&lt;span class="c1"&gt;// this is equal to writing it like: 0b_0100_1001 &amp;amp; 0b_0000_1111&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Temp
&lt;/h5&gt;

&lt;p&gt;Read byte #15 as &lt;code&gt;int8&lt;/code&gt; (&lt;code&gt;byte&lt;/code&gt; in C#) and divide by 2 to get temp in Celsius.&lt;/p&gt;

&lt;h5&gt;
  
  
  Profiles
&lt;/h5&gt;

&lt;p&gt;This is a work in progress as I don't know how the remote is structuring the data when a profile key is pressed. This is not important for my project so I haven't spent much time on it yet.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit Mask&lt;/th&gt;
&lt;th&gt;Profile&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0001 0000&lt;/td&gt;
&lt;td&gt;Normal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0001 0001&lt;/td&gt;
&lt;td&gt;Boost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0011 0000&lt;/td&gt;
&lt;td&gt;Quiet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  LSB8 vs MSB vs LSB Tranformation
&lt;/h5&gt;

&lt;p&gt;Here's an example showing the difference between LSB and LSB8 when encoding data:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MSB&lt;/strong&gt;: 01 02 03 (0000 0001 0000 0010 0000 0011)&lt;br&gt;
&lt;strong&gt;LSB&lt;/strong&gt;: C0 40 80 (1100 0000 0100 0000 1000 0000)&lt;br&gt;
&lt;strong&gt;LSB8&lt;/strong&gt;: 80 40 C0 (1000 0000 0100 0000 1100 0000)&lt;/p&gt;
&lt;h5&gt;
  
  
  Clean and Valid Starting State
&lt;/h5&gt;

&lt;p&gt;The following byte array has a valid starting remote state that can be transmitted as is. I used this to observe the AC unit acknoledging the command properly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;StartingData&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0x02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0x31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x0E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x81&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0x7E&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  CRC Calculation
&lt;/h5&gt;

&lt;p&gt;Here's how to calculate the CRC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="nf"&gt;CalcCrc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;crc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;crc&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;crc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automating my AC Unit
&lt;/h3&gt;

&lt;p&gt;With all that done, I was finally able to write some code to automate my AC unit using the IR signal. You can find the full source code &lt;a href="https://github.com" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Here's how the ESP32 is wired up to the DHT22 and IR sender module:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcu6oxbjd5khjqeiaujv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcu6oxbjd5khjqeiaujv.png" alt="Wiring Diagram" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As for the code, here's the important parts:&lt;/p&gt;

&lt;p&gt;I started by defining the GPIO pins, the GPIO Controller, the DHT22 driver, and finally, the RMT channel I'll use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;dhtEchoPin&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;33&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;dhtTriggerPin&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;dhtWakePin&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;26&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;irWakePin&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;irSignalPin&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gpioController&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GpioController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;statusLed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gpioController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenPin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statusLedPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PinMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dhtWaker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gpioController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenPin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dhtWakePin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PinMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;irWaker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gpioController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenPin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;irWakePin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PinMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dhtSensor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Dht22&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dhtEchoPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="n"&gt;dhtTriggerPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="n"&gt;gpioController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gpioController&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// wake the DHT22 now. This should give it enough time to "warm up"&lt;/span&gt;
    &lt;span class="n"&gt;dhtWaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PinValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;High&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// while the DHT is waking up, I connect the ESP32 to Wifi and to my MQTT instance on my network. This is used to send room temperature and humidity levels to my home assistant instance. Code omitted for brevity.&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;attemptNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attemptNumber&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;dhtSensor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Temperature&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;humidity&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="n"&gt;dhtSensor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Humidity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;dhtSensor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsLastReadSuccessful&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// log error to serial output if enabled and then try again&lt;/span&gt;
            &lt;span class="c1"&gt;// logging code here&lt;/span&gt;
            &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;attemptNumber&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// very basic automation implementation (WIP)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DegreesCelsius&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;irWaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PinValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;High&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;PanasonicIRController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TurnOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PanasonicACMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;irSignalPin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;irWaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PinValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Low&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DegreesCelsius&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;irWaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PinValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;High&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;PanasonicIRController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TurnOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PanasonicACMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Heat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;irSignalPin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;irWaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PinValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Low&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;irWaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PinValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;High&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;PanasonicIRController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TurnOff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;irSignalPin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;irWaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PinValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Low&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// code continues by reporting temperature and then putting the ESP32 to sleep for 15 minutes.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;PanasonicIRController&lt;/code&gt; class contains all the code required to manipulate the clean starting state shown above and sending the signal via the RMT module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PanasonicIRController&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;HeaderPulse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3468&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;HeaderSpace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1767&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;Pulse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;432&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;ZeroSpace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;432&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;OneSpace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1296&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;PauseSpace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;OnOffModeByteIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TempByteIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ProfileByteIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;21&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RmtCommand&lt;/span&gt; &lt;span class="n"&gt;Header&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RmtCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HeaderPulse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HeaderSpace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RmtCommand&lt;/span&gt; &lt;span class="n"&gt;ZeroBit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RmtCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pulse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ZeroSpace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RmtCommand&lt;/span&gt; &lt;span class="n"&gt;OneBit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RmtCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pulse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OneSpace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RmtCommand&lt;/span&gt; &lt;span class="n"&gt;Pause&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RmtCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pulse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PauseSpace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RmtCommand&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RmtCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pulse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TurnOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PanasonicACMode&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;irChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;commandData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetStartingCommandData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// set on flag&lt;/span&gt;
            &lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OnOffModeByteIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;|=&lt;/span&gt; &lt;span class="m"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// set mode&lt;/span&gt;
            &lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OnOffModeByteIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;//set temp&lt;/span&gt;
            &lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TempByteIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nf"&gt;SendIRCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;irChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;TurnOff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;irChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;commandData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetStartingCommandData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// starting command data is by default OFF&lt;/span&gt;
            &lt;span class="nf"&gt;SendIRCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;irChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;TransmitChannelSettings&lt;/span&gt; &lt;span class="nf"&gt;GetTransmitChannelSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;irChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;irChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;ClockDivider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                &lt;span class="n"&gt;EnableCarrierWave&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;CarrierLevel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;CarrierWaveFrequency&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;38_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;CarrierWaveDutyPercentage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                &lt;span class="n"&gt;IdleLevel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;EnableIdleLevelOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

                &lt;span class="n"&gt;NumberOfMemoryBlocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;SignalInverterEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;GetStartingCommandData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cm"&gt;/*  Byte 0 - 7   */&lt;/span&gt; &lt;span class="m"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// frame 1: static&lt;/span&gt;

            &lt;span class="c1"&gt;//  frame 2&lt;/span&gt;
            &lt;span class="cm"&gt;/*  Byte 8 - 12  */&lt;/span&gt; &lt;span class="m"&gt;0x02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// this is static&lt;/span&gt;

            &lt;span class="cm"&gt;/* Byte 13       */&lt;/span&gt; &lt;span class="m"&gt;0x38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// On/Off + Mode&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 14       */&lt;/span&gt; &lt;span class="m"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Temperature&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 15       */&lt;/span&gt; &lt;span class="m"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 16       */&lt;/span&gt; &lt;span class="m"&gt;0x31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 17       */&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 18       */&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 19       */&lt;/span&gt; &lt;span class="m"&gt;0x08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 20       */&lt;/span&gt; &lt;span class="m"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 21       */&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Profile&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 22       */&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 23       */&lt;/span&gt; &lt;span class="m"&gt;0x81&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 24       */&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 25       */&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="cm"&gt;/* Byte 26       */&lt;/span&gt; &lt;span class="m"&gt;0x7E&lt;/span&gt; &lt;span class="c1"&gt;// crc&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddCommands&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;TransmitterChannel&lt;/span&gt; &lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0xFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OneBit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;txChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ZeroBit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="nf"&gt;CalcCrc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;crc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;crc&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;crc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SendIRCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;irChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// setup an IR trasnmitter channel&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;irTxChannelSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetTransmitChannelSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;irChannelPinNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;irTxChannel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TransmitterChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;irTxChannelSettings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ClearCommands&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// add command data to the tx channel buffer&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommands&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// frame 1&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pause&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommands&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// frame 2 without CRC&lt;/span&gt;

            &lt;span class="c1"&gt;// calculate the crc&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;crc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CalcCrc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commandData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommands&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;crc&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// CRC&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// finally, send the command&lt;/span&gt;
            &lt;span class="n"&gt;irTxChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;waitTxDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;PanasonicACMode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// bits already left shifted by 4 (&amp;lt;&amp;lt; 4)&lt;/span&gt;

        &lt;span class="n"&gt;Auto&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;Fan&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;Dry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;Cool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;Heat&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;PanasonicACProfile&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Normal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;Boost&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;Quiet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0x30&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with that, I now have a functioning device to automate my Panasonic AC unit. The automation "algorithm" isn't the most sophisticated there is but it is a great starting point as I go and add more scenarios to it.&lt;/p&gt;

&lt;p&gt;If you have read this far, thank you. I hope you enjoyed the post and found it helpful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github repos:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MrCSharp22/nanoframework-esp32-panasonic-ir-automation" rel="noopener noreferrer"&gt;The project code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MrCSharp22/nanoframework-esp32-panasonic-ir-sender" rel="noopener noreferrer"&gt;nanoFramework Panasonic IR code sender&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MrCSharp22/nanoframework-esp32-panasonic-ir-decoder" rel="noopener noreferrer"&gt;nanoFramework Panasonic IR decoder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References &amp;amp; Literature
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Some of this was confirmed by &lt;a href="https://github.com/r45635/HVAC-IR-Control" rel="noopener noreferrer"&gt;this project&lt;/a&gt;. It is for an different (older or newer) revision of the AC remote so there are some changes.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github1s.com/crankyoldgit/IRremoteESP8266/blob/HEAD/src/ir_Panasonic.h#L99-L101" rel="noopener noreferrer"&gt;Panasonic AC Protocol Library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.hifi-remote.com/johnsfine/DecodeIR.html" rel="noopener noreferrer"&gt;IR Protocols Database (by John S. Fine)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://numericana.com/answer/ir.htm" rel="noopener noreferrer"&gt;Info-rich page about IR protocols&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html" rel="noopener noreferrer"&gt;IR-Send library intro post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sbprojects.net/knowledge/ir/index.php" rel="noopener noreferrer"&gt;IR Remote Control Theory&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nanoframework</category>
      <category>iot</category>
      <category>dotnet</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Uwp IoC Support</title>
      <dc:creator>Rafael</dc:creator>
      <pubDate>Sat, 16 Nov 2019 09:53:02 +0000</pubDate>
      <link>https://dev.to/mrcsharp/uwp-ioc-support-318c</link>
      <guid>https://dev.to/mrcsharp/uwp-ioc-support-318c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This blog post first appeared on &lt;a href="https://blog.mrcsharp.com.au" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have been developing UWP applications for a few years now and I truly enjoy it. It's been the platform where I experiment with and used it to learn a lot about programming.&lt;/p&gt;

&lt;p&gt;In my day job I develop web applications on the ASP.NET MVC framework and one of the things it does right is the native support for IoC containers out of the box.&lt;/p&gt;

&lt;p&gt;When a HTTP request arrives to an action method in a controller, MVC will initialize the controller, inject all dependencies in its constructor and have it ready to handle the request.&lt;/p&gt;

&lt;p&gt;I find MVC and MVVM to be comparable as a software architectures. However, the support for IoC Containers in Microsoft's UWP platform is surprisingly lacking. When navigating to a new page in UWP you are forced to manually initialize the view model and provide all the required dependencies to it.&lt;/p&gt;

&lt;p&gt;There is no native support in UWP for developers to bring in an IoC container and have it inject dependencies in the page constructor.&lt;/p&gt;

&lt;p&gt;This has always been annoying for me personally and when I tried to find a workaround this, it seemed the most popular solution was to rely on the Service Locator pattern in the form of a ViewModel locator.&lt;/p&gt;

&lt;p&gt;Some people consider the Service Locator to be an anti-pattern. I agree with that, but UWP seems to be forcing us to go down that path.&lt;/p&gt;

&lt;p&gt;Recently I had an idea to overcome this limitation and it has worked surprisingly well at least for me. So this post is an invite for other UWP developers to look at this solution, use it, evalaute it, and decide if it is a better replacement for service locators in UWP.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;It works by providing a modified &lt;em&gt;Frame&lt;/em&gt; class that provides a custom handler for the &lt;em&gt;Navigated&lt;/em&gt; event. In the custom handler, this frame attempts to perform a property injection on the new page before the page instance is loaded and displayed.&lt;/p&gt;

&lt;p&gt;I have packaged up this solution in a &lt;a href="https://www.nuget.org/packages/UWPIoC/1.0.0-alpha01" rel="noopener noreferrer"&gt;nuget package&lt;/a&gt;. It is still a pre-release at version 1.0.0-alpha.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;Start by installing the &lt;a href="https://www.nuget.org/packages/UWPIoC/1.0.0-alpha01" rel="noopener noreferrer"&gt;nuget package&lt;/a&gt; in the UWP application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package UWPIoC -Version 1.0.0-alpha01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and then make the &lt;em&gt;App.Xaml.cs&lt;/em&gt; look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static Host ApplicationHost; // (1)

/* ... */

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    IServiceProvider serviceProvider = /* ... */ // (2)
    ApplicationHost = new Host(serviceProvider);

    var rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        rootFrame = ApplicationHost.CreateNewHostedUwpFrame(); // (3)

        /* ... */

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    /* ... */
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the code above is doing:&lt;/p&gt;

&lt;p&gt;(1) Initializes a new instance of the &lt;em&gt;Host&lt;/em&gt; class. This class holds a reference to a &lt;em&gt;IServiceProvider&lt;/em&gt; instance and &lt;em&gt;Frame&lt;/em&gt; instance.&lt;/p&gt;

&lt;p&gt;(2) Depending on what IoC container being used, this line gets a reference to its service provider which is used by &lt;em&gt;IoCFrame&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;(3) Requests a new instance of &lt;em&gt;IoCFrame&lt;/em&gt; from the &lt;em&gt;Host&lt;/em&gt; instance. This is done when the window has no current frame instance (this is always true when the app has just been launched).&lt;/p&gt;

&lt;p&gt;Now, whenever a call is made to &lt;em&gt;Frame.Navigate&lt;/em&gt; from any page, the &lt;em&gt;IoCFrame&lt;/em&gt; will take care of injecting the new page dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency Injection in pages
&lt;/h3&gt;

&lt;p&gt;So how does a page code-behind file looks like when using &lt;em&gt;IoCFrame&lt;/em&gt; to inject properties?&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public sealed partial class MyPage : Page
{
    [ViewModel] // (1)
    public MyPageViewModel ViewModel { get; set; }

    [Dependency] // (2)
    public ILogger&amp;lt;MyPage&amp;gt; Logger { get; set; }

    public MyPage()
    {
        /* ... */
    }

    /* ... */
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! Really. That's all there is to it!&lt;/p&gt;

&lt;p&gt;Here's what's going on in this snippet:&lt;/p&gt;

&lt;p&gt;(1) This the public auto-property holding a reference to a view model instance. the &lt;em&gt;[ViewModel]&lt;/em&gt; attribute tells the &lt;em&gt;IoCFrame&lt;/em&gt; this is a view model and that it should manually construct an instance because it won't be registered in the IoC container. &lt;em&gt;IoCFrame&lt;/em&gt; will use a bit of reflection to find the view model's dependencies, request them from the IoC container and then create the instance.&lt;/p&gt;

&lt;p&gt;(2) The &lt;em&gt;[Dependency]&lt;/em&gt; attribute tells the &lt;em&gt;IoCFrame&lt;/em&gt; that this is a typical service/dependency that is registered in the IoC container. &lt;em&gt;IoCFrame&lt;/em&gt; will then request an instance of this service type from the IoC container and inject it in this property.&lt;/p&gt;

&lt;p&gt;As you can see, the constructor has no custom code, no additional work from you as a developer is required here.&lt;/p&gt;

&lt;p&gt;Now, when some code makes a call to &lt;em&gt;Frame.Navigate(typeof(MyPage))&lt;/em&gt; the app will navigate to the new page, and all of its dependencies will be there and ready to be used as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While this is still not constructor injection, I'd say that in this case, property injection still works just fine and allows you as a developer to focus on building good UI/UX rather than writing biolerplate code for every new page you create to get instances of services/ViewModels you need.&lt;/p&gt;

&lt;h4&gt;
  
  
  Links
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;GitHub repo: (&lt;a href="https://github.com/MrCSharp22/UWP-IoC" rel="noopener noreferrer"&gt;https://github.com/MrCSharp22/UWP-IoC&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Nuget package: (&lt;a href="https://www.nuget.org/packages/UWPIoC/" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/UWPIoC/&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>uwp</category>
      <category>ioc</category>
      <category>tutorial</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Create a Dotnet Framework and Node Build Environment in Docker</title>
      <dc:creator>Rafael</dc:creator>
      <pubDate>Tue, 01 Oct 2019 14:09:34 +0000</pubDate>
      <link>https://dev.to/mrcsharp/create-a-dotnet-framework-and-node-build-environment-in-docker-1ba5</link>
      <guid>https://dev.to/mrcsharp/create-a-dotnet-framework-and-node-build-environment-in-docker-1ba5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This blog post first appeared on &lt;a href="https://blog.mrcsharp.com.au" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Story time
&lt;/h2&gt;

&lt;p&gt;At the company I currently work for, our CI pipelines run on a dedicated server that we own and manage on premise. This build server is using Atlassian Bamboo and is configured to run the builds using agents running directly inside the host OS which means builds share and depend on components installed on the host OS.&lt;/p&gt;

&lt;p&gt;This configuration has been working fine for us and we rarely run into issues with it. Last week, however, one of the CI pipelines started to fail suddenly and at the worst time as it was a day before a hard deadline. We didn't know what went wrong with the build server. We had no idea whether someone made a change to the host OS causing our build to throw this random error and we had no time to fully investigate the issue.&lt;/p&gt;

&lt;p&gt;In the interest of time and to deploy the site before the deadline I used my co-workers dev machine to run the same CI commands we use on the build server in order to deploy the site. This is not great. Trust me, I know. But we didn't have the luxury of time to come up with a more elegant solution. We literally had to fallback to an almost manual deployment.&lt;/p&gt;

&lt;p&gt;This is obviously not great. Having multiple CI pipelines running on a single server is OK. What's not OK is having them share the host OS as this introduces the danger of a new pipeline being created breaking other CI pipelines accidentally.&lt;/p&gt;

&lt;p&gt;So I decided it is time to start containerizing our builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;By containerizing our builds we can be sure that any CI pipeline we have with no matter what kind of configurations it needs, will never mess up other pipelines as each one is running in its own container separately from the others.&lt;/p&gt;

&lt;p&gt;This means that I can run my build knowing that no matter how bad my configs are, they will never affect others. And by containerizing the pipeline, I can store the config files in the git repo and have those configs versioned alongside of the project code-base.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'll cover
&lt;/h2&gt;

&lt;p&gt;This post will cover creating a build environment image in docker and how to use the image to build your code base locally on your own machine. Hopefully in a next post, I will cover how to use this with Atlassian's Bamboo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building our custom docker image
&lt;/h2&gt;

&lt;p&gt;I thought docker image registry would have a pre-made image ready that fits my requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows Based&lt;/li&gt;
&lt;li&gt;Has DotNet Framework 4.X SDK&lt;/li&gt;
&lt;li&gt;Has Node and NPM 10.X&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As far as I can tell, there is no such image on the official docker registry. I don't know if I just didn't look hard enough or because I was a bit lazy. Turns out that creating my own image for this is quite easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;Obviously you'll need docker installed on your machine. You can use the community edition of docker for Windows.&lt;/p&gt;

&lt;p&gt;Make sure that your docker installation is switched to &lt;em&gt;Windows Containers&lt;/em&gt;. The reason for this requirement is DotNet Framework 4.X requires a Windows host and the official SDK image from Microsoft that's hosted on Docker's official registry doesn't run on Linux Containers. To switch your docker instance to Windows Containers, right click on the docker icon in your task bar and then select "Switch To Windows Containers". The Docker engine will restart during this process and will take a minute or so.&lt;/p&gt;

&lt;p&gt;I am using &lt;a href="https://hub.docker.com/_/microsoft-dotnet-framework-sdk/" rel="noopener noreferrer"&gt;this image&lt;/a&gt;. This is the Official .Net Framework SDK Container Image from Microsoft.&lt;br&gt;
This image is based on the Windows Server Core and has the SDK installed on top of it. It also contains nuget and Visual Studio Build Tools (MSBuild).&lt;/p&gt;

&lt;p&gt;What it doesn't have is NodeJS and I need it as the site I'm trying to build requires a build step to run some NPM commands responsible for building the UI assets.&lt;/p&gt;

&lt;p&gt;So how can we modify that image?&lt;/p&gt;

&lt;p&gt;Technically, we can't. Docker only allows us to build new ones. However, the above image will be our base. So we will just add NodeJS on top of it.&lt;/p&gt;

&lt;p&gt;To build your own image, you'll need to create a DockerFile. Here's the DockerFile for the build environment image I created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Specify a base image. In this case, I'm using the .Net SDK image from MS
FROM mcr.microsoft.com/dotnet/framework/sdk AS DOTNET_SDK

# Tell Docker that I want to use PowerShell to run my commands
SHELL ["powershell"]

# Install Scoop (Windows Package Manager) from Scoop.sh (This command is on their homepage)
RUN iwr -useb get.scoop.sh | iex

# Tell Scoop to download and install NodeJS
RUN scoop install nodejs

# Set a working directory for us on the root drive
WORKDIR /app

# DONE
RUN exit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, so what happened here? The base image I'm pulling has everything I need to build the BackEnd code of the site. However, to build the Front-End assets, I need NodeJS. The easiest way I could think of to add NodeJS to the image was to use &lt;a href="https://scoop.sh" rel="noopener noreferrer"&gt;Scoop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next step is to actually build the image. To do this save the above file and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build --tag=my-image-name --file path\to\dockerfile .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will take some time to finish as Docker will have to download the SDK image which is ~1.5GB.&lt;/p&gt;

&lt;p&gt;When done, we can run a quick test to make sure that the image we created has everything we need. To do this, we are going to run a command that will launch a container based on our image and then "SSH" into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -it my-image-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;--rm&lt;/em&gt; tells docker to remove the container once we exit it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;-it&lt;/em&gt; makes this container an interactive process that accepts input from us and display output directly in our shell.&lt;/p&gt;

&lt;p&gt;When you run that command, your shell will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Microsoft Windows [Version 10.0.18362.356]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\app&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you type in &lt;em&gt;MSBuild&lt;/em&gt; and hit enter, you will see MSBuild execute on an empty directory and complain about it.&lt;/p&gt;

&lt;p&gt;Do the same for &lt;em&gt;nuget&lt;/em&gt; and you'll get the help output.&lt;/p&gt;

&lt;p&gt;Finally, type &lt;em&gt;node&lt;/em&gt; and you will start a new NodeJS session.&lt;/p&gt;

&lt;p&gt;At this stage, we have successfully created a Docker image with all the tools we need to build an ASP.NET MVC Project and all of the Front-End assets using NodeJS and NPM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;In the next post, I'll show how to actually compile some code in that container and grab the output from it.&lt;/p&gt;

</description>
      <category>dotnetframework</category>
      <category>tutorial</category>
      <category>docker</category>
      <category>node</category>
    </item>
    <item>
      <title>Dotnet Core Webapi With Mssql in Docker Part 2</title>
      <dc:creator>Rafael</dc:creator>
      <pubDate>Sat, 28 Sep 2019 00:08:44 +0000</pubDate>
      <link>https://dev.to/mrcsharp/dotnet-core-webapi-with-mssql-in-docker-part-2-20o6</link>
      <guid>https://dev.to/mrcsharp/dotnet-core-webapi-with-mssql-in-docker-part-2-20o6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This blog post first appeared on &lt;a href="https://blog.mrcsharp.com.au" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/2019/08/dotnet-core-webapi-with-microsoft-sql-server-in-docker-part-1/"&gt;previous post&lt;/a&gt; I showed how to setup a Dotnet Core WebApi project to run inside docker.&lt;/p&gt;

&lt;p&gt;In part 2, I'll go over what is needed to containerize an SQL server and connect the WebApi to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you need?
&lt;/h2&gt;

&lt;p&gt;This post assumes you have Docker Community Edition installed on your machine. If not, you can grab a copy from &lt;a href="https://docs.docker.com/docker-for-windows/install/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containerizing Microsoft SQL Server
&lt;/h2&gt;

&lt;p&gt;Thankfully, Microsoft has created a base image for the SQL server which I will be using in this blog post. The image is available on the official docker registry. You can find it &lt;a href="https://hub.docker.com/_/microsoft-mssql-server" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will start by creating a &lt;em&gt;docker compose&lt;/em&gt; file. Docker Compose is a tool in docker used to define the configurations of your app's services and then create those services and start them up with a single command. More info can be found in &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker's official documentation page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Name the file&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
. The file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we are using &lt;em&gt;Version 3&lt;/em&gt; of the docker compose file format.&lt;/p&gt;

&lt;p&gt;Next, we need to define and configure the required services for out application. Here, Application encapsulates everything that must be up and running so the WebApi can return results as expected. In this case, we will have the following services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebApi: the dotnet core web api project&lt;/li&gt;
&lt;li&gt;Sql-Server: an instance of Microsoft SQL server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Great! so now that we know what services to define, let's go ahead and define the first one in the docker compose file. We will start with the WebApi service.&lt;/p&gt;

&lt;p&gt;We already have the DockerFile from the previous post which created the WebApi image. Let's see how to use that image to create the first service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# webapi serves as the name of the service&lt;/span&gt;
  &lt;span class="na"&gt;webapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The image name which this service will be based upon.&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MyWebApiProject&lt;/span&gt;

    &lt;span class="c1"&gt;# This defines the ports which should be exposed by this service's container to the outer world&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5001:443"&lt;/span&gt;

    &lt;span class="c1"&gt;# Make this service part of a network called webnet&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;webnet&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;webnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this service, we are using the image name MyWebApiProject which was created by the WebApi DockerFile which we created in the previous post.&lt;br&gt;
And now that we have defined the first service that will be created and launched, we move on to the next service to define: Microsoft SQL Server.&lt;/p&gt;

&lt;p&gt;We don't have a DockerFile for MSSQL and we don't need one. We can specify an image hosted on Docker's official registry and docker compose will pull that image if it is not cached on the machine already. Let's see how we create the MSSQL service in docker compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# sql-server is the name of the service and will also be the name of the server on the network&lt;/span&gt;
  &lt;span class="na"&gt;sql-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="c1"&gt;# this is the image docker compose will pull to create a MSSQL service instance&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcr.microsoft.com/mssql/server&lt;/span&gt;

    &lt;span class="c1"&gt;# this defines the ports to expose&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1433:1433"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1434:1433"&lt;/span&gt;

    &lt;span class="c1"&gt;# this put the MSSQL service instance on the same network as the WebApi service&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;webnet&lt;/span&gt;

    &lt;span class="c1"&gt;# these are required by the image as per the MS documentions&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

      &lt;span class="c1"&gt;# This is the password we will use for this server (passwords must pass the password rules of SQL server)&lt;/span&gt;
      &lt;span class="na"&gt;SA_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YG4ZkkGrn7QSz5&amp;amp;"&lt;/span&gt;

      &lt;span class="c1"&gt;# Required to accept the End User License Agreement&lt;/span&gt;
      &lt;span class="na"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Y"&lt;/span&gt;

      &lt;span class="c1"&gt;# This defines which edition of the server we want to run. We can also use "Express"&lt;/span&gt;
      &lt;span class="na"&gt;MSSQL_PID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Developer"&lt;/span&gt;

  &lt;span class="c1"&gt;# webapi serves as the name of the service&lt;/span&gt;
  &lt;span class="na"&gt;webapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The image name which this service will be based upon.&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MyWebApiProject&lt;/span&gt;

    &lt;span class="c1"&gt;# This defines the ports which should be exposed by this service's container to the outer world&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5001:443"&lt;/span&gt;

    &lt;span class="c1"&gt;# Make this service part of a network called webnet&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;webnet&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;webnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We gave the MSSQL service a name. In this case it is sql-server. This is what we will use in the WebApi connection string to connect to the containerized SQL server. So the connection string in the appsettings.json will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"ConnectionStrings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Server=sql-server;Database=YourDatabaseName;User Id=sa;Password=YG4ZkkGrn7QSz5&amp;amp;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also notice the user credentials. As per the MSSQL base image documentations, we will use the &lt;em&gt;SA&lt;/em&gt; account to login to the server, and the environment variable we defined earlier in the docker compose file: &lt;em&gt;SA_PASSWORD&lt;/em&gt; sets the password for that account which is what we are also using in the connection string.&lt;/p&gt;

&lt;p&gt;Once you save the docker compose file and update the connection string in the WebApi configuration file, then re-build the WebApi image as shown in the previous post. This is so we have an up to date image of WebApi that uses the new connection string.&lt;/p&gt;

&lt;p&gt;Next, we need to make the machine a manager of a docker swarm. To do this, in a command line, execute this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker swarm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a docker swarm and make the current machine a manager for the swarm. In docker, a swarm is the cluster management feature which is required to deploy our multiple services. To read more about docker swarm, start &lt;a href="https://docs.docker.com/engine/swarm/key-concepts/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once we have swarm created, we will then deploy our services. The command for this looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker stack deploy -c docker-compose.yml MyDockerApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might take a few minutes to finis if it is the first time running it as you probably don't have the MSSQL server image cached on your machine. But once done, you can run this command to see all your services created and running inside their containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker stats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have SQL Server Management Studio on your machine, you can connect to the containerized sql server by using &lt;em&gt;localhost,1434&lt;/em&gt; as the host name and same credentials used in the connection string.&lt;/p&gt;

&lt;p&gt;Go ahead and call your WebApi endpoints using &lt;em&gt;localhost:5000&lt;/em&gt; or &lt;em&gt;localhost:5001&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initializing a database
&lt;/h2&gt;

&lt;p&gt;You might already be thinking that the SQL Server we created has no databases and that your WebApi won't work because the specific database you want to connect to does not yet exist.&lt;/p&gt;

&lt;p&gt;Luckily, we can do something about that. We can have docker run a few commands for us as it is creating the MSSQL service. The commands we will write next will use 2 sql scripts to create the database and all of its tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do we do that?
&lt;/h2&gt;

&lt;p&gt;Let's start by creating an SQL file which will create the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;YourDatabaseNameHere&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a very simple script and will be the first to run against the SQL server.&lt;/p&gt;

&lt;p&gt;Next, we will use Entity Framework Core to export the migrations as an SQL script. To do this, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet ef migrations script -o migrations.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that is done, create a Bash script file that will take care of executing those 2 scripts against the SQL server within the docker container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YourDatabaseNameHere"&lt;/span&gt;
&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sa"&lt;/span&gt;
&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YG4ZkkGrn7QSz5&amp;amp;"&lt;/span&gt;

&lt;span class="c"&gt;# wait until the SQL server instance is ready&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; /opt/mssql-tools/bin/sqlcmd &lt;span class="nt"&gt;-S&lt;/span&gt; 0.0.0.0 &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; master &lt;span class="nt"&gt;-Q&lt;/span&gt; &lt;span class="s2"&gt;"select 'hello world'"&lt;/span&gt;
&lt;span class="k"&gt;do
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waiting for SQL server connection..."&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;1

&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SQL server ready!"&lt;/span&gt;

&lt;span class="c"&gt;# this creates all databases specified in the databases.sql script&lt;/span&gt;
/opt/mssql-tools/bin/sqlcmd &lt;span class="nt"&gt;-S&lt;/span&gt; 0.0.0.0 &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; databases.sql
&lt;span class="c"&gt;# this runs the ef core migrations script&lt;/span&gt;
/opt/mssql-tools/bin/sqlcmd &lt;span class="nt"&gt;-S&lt;/span&gt; 0.0.0.0 &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;$database&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; migrations.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place the &lt;em&gt;.sh&lt;/em&gt; and the &lt;em&gt;.sql&lt;/em&gt; files in a sub-directory called &lt;em&gt;data&lt;/em&gt;. We will share this directory later with the sql server container.&lt;/p&gt;

&lt;p&gt;Ok, now that we have everything ready, we need to get docker-compose to run the bash script for us. In the &lt;em&gt;docker-compose.yml&lt;/em&gt; file, find the &lt;em&gt;sql-server&lt;/em&gt; service definition and add the following to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data:/init/scripts&lt;/span&gt;
    &lt;span class="na"&gt;working_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/init/scripts&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sh -c 'chmod +x ./init-database.sh; ./init-database.sh &amp;amp; /opt/mssql/bin/sqlservr'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is telling docker-compose to share the &lt;em&gt;data&lt;/em&gt; folder with the container and place the content under this path: &lt;em&gt;init/scripts&lt;/em&gt;. It then tells it to use the &lt;em&gt;init/scripts&lt;/em&gt;  as the working directory. The &lt;em&gt;command&lt;/em&gt; part is overriding the default command defined by the base image. It is updating the file permissions for our bash script to allow execution, then it executes it and runs the sql server instance. While the sql server instance is being initialized and getting ready, the script would wait for it by attempting to login and query something and sleeping for 1 second.&lt;/p&gt;

&lt;p&gt;Now, when we deploy the docker stack, we should have a database ready in the sql server instance.&lt;/p&gt;

&lt;p&gt;You can deploy the stack again and then attempt to connect to the sql server using the SQL Server Management Studio. You should be able to see the database and the tables inside.&lt;/p&gt;

&lt;p&gt;The Web Api should also be able to access that sql server instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;There is a lot more to docker than what I showed you in the last 2 parts and every project requires a different set of steps to containerize. You should familiarize yourself with the different things docker is capable of and read their documentations.&lt;/p&gt;

&lt;p&gt;This ends this 2 part post series, but there will be more posts in the future with tips and gotcha's as I try more things with my own applications in docker.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dotnet Core Webapi With Microsoft SQL Server in Docker - Part 1</title>
      <dc:creator>Rafael</dc:creator>
      <pubDate>Sun, 25 Aug 2019 04:17:59 +0000</pubDate>
      <link>https://dev.to/mrcsharp/dotnet-core-webapi-with-microsoft-sql-server-in-docker-part-1-2i4l</link>
      <guid>https://dev.to/mrcsharp/dotnet-core-webapi-with-microsoft-sql-server-in-docker-part-1-2i4l</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This blog post first appeared on &lt;a href="https://blog.mrcsharp.com.au"&gt;my blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have been hearing a lot of people talking about docker and all of the benefits it introduces and they got me interested in the technology. However, I only recently managed to get some time to play around with Docker.&lt;/p&gt;

&lt;p&gt;What I attempted to do was to take a DotNet Core WebApi project that I'm building and containerize it. The WebApi project was build initially with no intention of containerizing and it was surprising how easy it is to get it up and running in docker.&lt;/p&gt;

&lt;p&gt;The project also uses a Microsoft SQL database and for me, this was a bit challenging to setup and get the WebApi to communicate with it properly.&lt;/p&gt;

&lt;p&gt;In this post, I'll explain the steps I took to set it up with sample dockerfiles and docker compose configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you need?
&lt;/h2&gt;

&lt;p&gt;To get started, you'll need to install docker on you development machine. I'm using a Windows 10 Machine. You can read the &lt;a href="https://docs.docker.com/docker-for-windows/install/"&gt;install instructions here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the DockerFile for the WebApi Project
&lt;/h2&gt;

&lt;p&gt;The dockerfile will take care of setting up the image for the WebApi project. If you don't know what docker images are, I strongly suggest reading the Docker &lt;a href="https://docs.docker.com/get-started/"&gt;Getting Started section here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this dockerfile we will execute the same commands we do on our machine when building and publishing any dotnet core application via the &lt;em&gt;dotnet&lt;/em&gt; cli.&lt;/p&gt;

&lt;p&gt;Usually we do this (at the root of the project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;gt; dotnet restore
&amp;gt; dotnet publish -c Release -o out

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;then when we inspect the &lt;em&gt;out&lt;/em&gt; directory in the project root, we will find the compiled assemblies of our project and we can then simply execute this command to run the application/webapi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;gt; dotnet MyProjectEntryPointAssembly.dll

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But there are a few extra command we will execute in the dockerfile to setup the base image and the entrypoint for the resulting image.&lt;/p&gt;

&lt;p&gt;First, let's look at my project's folder structure:&lt;/p&gt;

&lt;p&gt;/root&lt;br&gt;
        - src&lt;br&gt;
            - webapi&lt;br&gt;
        - dockerfile&lt;br&gt;
        - docker-compose.yml&lt;/p&gt;

&lt;p&gt;Let's look at my dockerfile and I'll explain what every command is doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM mcr.microsoft.com/dotnet/core/sdk as build
WORKDIR /app
COPY src/. .
RUN dotnet restore
RUN dotnet publish -c Debug -o out
FROM mcr.microsoft.com/dotnet/core/aspnet as runtime
WORKDIR /app
COPY --from=build /app/webapi/out ./
EXPOSE 80
CMD [ "dotnet", "webapi.dll" ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ok, let's start with the first line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM mcr.microsoft.com/dotnet/core/sdk as build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is telling docker that we are going to use the &lt;em&gt;DotNet Core SDK&lt;/em&gt; image from microsoft as our base image. You can find the image &lt;a href="https://hub.docker.com/_/microsoft-dotnet-core-sdk"&gt;here in Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;... as build&lt;/em&gt; is an alias given to a build stage. This makes it easier when you want to reference a build stage from another one as it is more friendly to work with names. By default docker doesn't name build stages. You can still reference build stages that don't have an alias defined by using then integer number which starts from &lt;em&gt;0&lt;/em&gt; for the first build stage.&lt;/p&gt;

&lt;p&gt;The next step in the dockerfile is to set a working directory within the image we just pulled from docker hub. The working directory name can be anything and docker will create that directory if it doesn't already exist in the base image. I chose to name my working directory as &lt;em&gt;App&lt;/em&gt; but feel free to use whatever name you like. To set a working directory, we execute this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKDIR /app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, we need to copy our project files from our local dev environemnt to the base image. You can think of the base image we just pulled as a Virtual Machine (they aren't actually VMs but this might help when working with them) that docker will initialize and run so it won't have access to our files. We copy the files to the base image by executing this comand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY src/. .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is copying everything inside the /src directory into the working directory we specified earlier.&lt;/p&gt;

&lt;p&gt;Ok, so now that we have all the source code in the base image we're ready to build it. Since we are still inside the DotNet Core SDK image, the &lt;em&gt;dotnet&lt;/em&gt; command is available in the shell, so we will tell docker to execute a couple of &lt;em&gt;dotnet&lt;/em&gt; cli commands for us to build the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN dotnet restore
RUN dotnet publish -c Debug -o out
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;RUN&lt;/em&gt; keyword here is telling docker to execute the following commands in the shell of the base image. So this will restore nuget packages and then performs the publish command and outputs the results to the &lt;em&gt;out&lt;/em&gt; directory. Remember, we are still in the work directory we specified so everything is happening under &lt;em&gt;/app&lt;/em&gt;, our code is in &lt;em&gt;/app/webapi/.&lt;/em&gt; and the outout folder will be in &lt;em&gt;/app/webapi/out/.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Awesome! now that we have successfully got our code to compile and publish (inside the base image) we will move to the next build stage.&lt;/p&gt;

&lt;p&gt;The reason we have multiple stages in this dockerfile and the reason to use them (at least in this case) is to avoid creating big images.&lt;/p&gt;

&lt;p&gt;The Microsoft DotNet Core SDK base image is about &lt;em&gt;400MB&lt;/em&gt;. While we can build the project and also run it inside this image, using &lt;em&gt;~400MB&lt;/em&gt; + &lt;em&gt;~75MB&lt;/em&gt; for my webapi project seems like a waste when we can instead use the Microsoft DotNet Core Runtime base image which is much smaller in size to run my webapi.&lt;/p&gt;

&lt;p&gt;To start a new stage in the dockerfile we use the &lt;em&gt;FROM&lt;/em&gt; statement. For this stage, we will pull the Microsoft DotNet Core Runtime image &lt;a href="https://hub.docker.com/_/microsoft-dotnet-core-aspnet/"&gt;found here&lt;/a&gt; by executing this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM mcr.microsoft.com/dotnet/core/aspnet as runtime
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;we name this stage as &lt;em&gt;runtime&lt;/em&gt; and we set its working directory also to &lt;em&gt;/app&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKDIR /app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next step we need to grab the build outout files from the previous stage and copy it over to the currect stage. We do this by executing this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY --from=build /app/webapi/out ./
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;--from=[stage name]&lt;/em&gt; is specifying the source of the files to be from another image/stage. &lt;em&gt;/app/webapi/out&lt;/em&gt; is the source of the files in that step and &lt;em&gt;./&lt;/em&gt; is the destination (the root of the working directory in this step).&lt;/p&gt;

&lt;p&gt;Once the copy is complete, and since this is a webapi project that will run on &lt;em&gt;port 80&lt;/em&gt;, we need to tell docker that we port 80 should be expesed to incoming network traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXPOSE 80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Simple, way too simple!&lt;/p&gt;

&lt;p&gt;The final command we run is literally running our webapi. To do this we execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CMD [ "dotnet", "webapi.dll" ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now that we have a complete dockerfile we can test it by using the docker cli to build the final image and running it.&lt;/p&gt;

&lt;p&gt;To do that, open a terminal, and set the working directory to the root of you project (where the dockerfile is located) and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build --tag=MyWebApiProject .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;the &lt;em&gt;build&lt;/em&gt; command will execute the commands in the dockerfile we created. &lt;em&gt;--tag=[name]&lt;/em&gt; gives the resulting image a name. the period after the command sets the build context to the current directory. docker will attempt to find the dockerfile in the current context and all the commands inside the dockerfile will run within that context. This is important for when we copy the files to the SDK image as we are using relative paths. All relative paths will rely on the build context we set.&lt;/p&gt;

&lt;p&gt;If your build is successful, you will have a docker image stored on your machine.&lt;/p&gt;

&lt;p&gt;Use &lt;em&gt;docker image ls&lt;/em&gt; to list the images on your machine. You can see the image you created and it's size.&lt;/p&gt;

&lt;p&gt;On my machine, the SDK image is &lt;em&gt;1.74GB&lt;/em&gt; but the webapi image is only &lt;em&gt;283MB&lt;/em&gt; because it is using the runtime image as per the multi-stage dockerfile above.&lt;/p&gt;

&lt;p&gt;Now it is time to run the containerized application and this is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -p 5000:80 MyWebApiProject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;docker run&lt;/em&gt; is the CLI command to run a containerized application's image.&lt;/p&gt;

&lt;p&gt;With &lt;em&gt;-p [OS port]:[Container port]&lt;/em&gt; I'm are binding a port on my operating system (in this case port 5000) to the port we exposed earlier in the dockerfile (port 80). This binding will allow us to use HTTP REST Clients such as &lt;a href="https://insomnia.rest/"&gt;Insomnia&lt;/a&gt; to send HTTP requests to the webapi application in the container.&lt;/p&gt;

&lt;p&gt;That's it! This is how I took an existing and under development DotNet Core WebApi project and containerized it in docker.&lt;/p&gt;

&lt;p&gt;In the next part, I'll explain how to setup a containerized MS-SQL server and create a docker swarm to have my WebApi project talking to the containerized MS-SQL server.&lt;/p&gt;

</description>
      <category>dotnetcore</category>
      <category>webapi</category>
      <category>mssql</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
