<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://terencegolla.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://terencegolla.com/" rel="alternate" type="text/html" /><updated>2026-01-10T21:53:29-06:00</updated><id>https://terencegolla.com/feed.xml</id><title type="html">Terence Golla</title><subtitle>Golla Labs where the strange and unusual is an everyday occurance.</subtitle><author><name>Terence Golla</name></author><entry><title type="html">Aerobic Septic System Monitor - Part 2 - Beta Testing</title><link href="https://terencegolla.com/arduino/aerobic-septic-system-monitor-part-2-beta-testing/" rel="alternate" type="text/html" title="Aerobic Septic System Monitor - Part 2 - Beta Testing" /><published>2024-04-23T07:23:23-05:00</published><updated>2024-04-23T07:23:23-05:00</updated><id>https://terencegolla.com/arduino/aerobic-septic-system-monitor-part-2-beta-testing</id><content type="html" xml:base="https://terencegolla.com/arduino/aerobic-septic-system-monitor-part-2-beta-testing/"><![CDATA[<p>The beta testing chapter is a look at each of the different components that make up the Aerobic Septic System Monitor. Each section is an exercise in connecting and programming a component.  If you are new to the Arduino/ESP32 you will want to work through each section to gain a better understanding of each component attached to the ESP32. This chapter is optional and if you want to get directly to monitoring your aerobic septic system you can jump to “v1.0 The Basics”.</p>

<h3 id="blink-esp32">Blink (ESP32)</h3>

<p>If you are new to the Arduino/ESP32 world, blink is the classic “Hello World!” program for the Arduino developer.  This exercise involves using the build-in LED on pin 13 of many Arduino boards or wiring to an LED to an one of the many digital pins with a 220 ohm ballast resistor to limit the current flow. Since the ESP development board does not have a built-in LED I used the following wiring example.</p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/BlinkSchematic.png" alt="Blink Schematic" /></p>

<p>I my case, I used the breakout board and an Ideal Lever Wire Connector to connect the LED and 220 ohm resistor without the need to solder.</p>

<table>
  <thead>
    <tr>
      <th>ESP32</th>
      <th>LED</th>
      <th>220 ohm Resistor</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>GND (-)</td>
      <td>Cathode (-)</td>
      <td> </td>
    </tr>
    <tr>
      <td> </td>
      <td>Anode (+)</td>
      <td>Lead</td>
    </tr>
    <tr>
      <td>GPIO 4</td>
      <td> </td>
      <td>Opposite Lead</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/BlinkPhoto.jpg" alt="Blink Photo" /></p>

<p>The blink sketch is included in this project in the folder labeled <code class="language-plaintext highlighter-rouge">/Blink</code>.  With the ESP32 Development Board I chose there is no built in LED so I had to manually redefine the LED_BUILTIN constant in the code. In my case I used pin 4. If you are new to programming an Arduino/ESP32 I recommend starting by reading “<a href="https://docs.arduino.cc/software/ide-v2/tutorials/getting-started-ide-v2/" target="_blank">Getting Started with Arduino IDE 2</a>”. You can also search the internet for “Arduino blink” or “programming the Arduino” to find a great deal of articles and tutorials.</p>

<h3 id="beyond-blink-esp32">Beyond Blink (ESP32)</h3>

<p>Beyond Blink is an exercise to look at the simplicity and power the ESP32 has when it comes to connecting to your Wi-Fi network and the Internet. The code was adapted from the <a href="https://lastminuteengineers.com/" target="_blank">Last Minute Engineers - Learn Electronics the Easy Way</a> article <a href="https://lastminuteengineers.com/creating-esp32-web-server-arduino-ide/" target="_blank">In-depth: Create A Simple ESP32 Web Server In Arduino IDE (lastminuteengineers.com)</a> and demonstrated how to connect your ESP32 to a Wi-Fi network hosing a web page that presents a button that can be used to turn the LED on and off.</p>

<p>This exercise uses the same wired LED from the Blink exercise. You will again need to edit the code to modify LED_BUILTIN if you are using a pin other than 4 for the LED. You will also need to edit the <code class="language-plaintext highlighter-rouge">configuration.h</code> header file to add your Wi-Fi SSID and password. The code can be found in the <code class="language-plaintext highlighter-rouge">/BeyondBlink</code> folder.</p>

<h3 id="is-the-power-on-220v-optocoupler-module">Is the Power On (220V Optocoupler Module)</h3>

<p>As part of this project I need to be able to see if the power is on. In particular, I want to monitor the pump timer, the air compressor pump timer, the high water alarm and the air compressor alarm.</p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/AC220VOptocouplerModule.jpg" alt="AC 220V Optocoupler Module" /></p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/AC220VOptocouplerModuleSchematic.png" alt="AC 220V Optocoupler Module Schematic" /></p>

<p>To do this I will use a simple <a href="https://www.amazon.com/HiLetgo-Optocoupler-Alternating-Current-Channel/dp/B0CHJLYY3D/ref=sr_1_4?crid=3NMY4ZOHAL63&amp;keywords=220V%2BOptocoupler%2BModule&amp;qid=1707616569&amp;s=industrial&amp;sprefix=220v%2Boptocoupler%2Bmodule%2Cindustrial%2C115&amp;sr=1-4&amp;th=1" target="_blank">220V optocoupler module</a> (pictured above) which can be connected to the hot 120V lead (‘L’ for load, typically a black wire) of any of the items I previously listed and common (‘N’ for neutral, typically a white wire) on one side of the module. On the other side I connect the module to 3.3 voltage (VCC - positive voltage), ground (GND - ground, negative) and one of the GPOI (General-Purpose Input-Output) pins of the ESP32 (OUT - signal). You can learn more about the ESP32 GPOI pins at <a href="https://randomnerdtutorials.com/esp32-pinout-reference-gpios/" target="_blank">ESP32 Pinout Reference: Which GPIO pins should you use? - Random Nerd Tutorials</a>. Below is a diagram for a test circuit.</p>

<table>
  <thead>
    <tr>
      <th>220V Optocoupler Module</th>
      <th>ESP32</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>VCC</td>
      <td>3V3 (+3.3V)</td>
    </tr>
    <tr>
      <td>GND</td>
      <td>GND (-)</td>
    </tr>
    <tr>
      <td>OUT (signal)</td>
      <td>GPIO 34</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/IsThePowerOnSchematic.png" alt="Is The Power On Schematic" /></p>

<p>The following is a photo of the AC circuit test board I built for testing based on the test circuit above. When plugged in and the the switch is on, the plug has power and the optocoupler module (green PCB (Printed Circuit Board), far right of photo) will sense voltage. In the next module I will discuss the current sensor (blue PCB, bottom of photo).</p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/ACTestCircuit.jpg" alt="" /></p>

<p>The code can be found in the <code class="language-plaintext highlighter-rouge">/IsThePowerOn</code> folder and is extremely simplistic. Basically when power is sensed the LED I wired in the previous exercises is turned on and a message is sent to the serial port.  As before you will need to change the <code class="language-plaintext highlighter-rouge">LED_BUILTIN</code> constant if you use a pin other than GPIO 4.  You will also need to change the <code class="language-plaintext highlighter-rouge">POWER_ON_OPTOCOUPLER_MODULE</code> constant if you use a GPIO pin other than 34. In the photo below VCC on the optocoupler is wired to 3V3, OUT to GPIO pin 34 and GND to GND on the development breakout board.</p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/IsThePowerOnWiring.jpg" alt="Is The Power On Wiring" /></p>

<h3 id="is-the-pump-running-current-sensor">Is the Pump Running (Current Sensor)</h3>

<p>While determining if the pump has been turned on by either the pump timer or the high water override float is a simple mater of using a 220V optocoupler module to detect voltage, I can’t always be certain that the pump is running since a low water float is tied directly to the pump circuit after the control box wiring. The low water float is wired in to the circuit to keep the pump from running and possibly burning out when there is no water in the septic clear water tank.</p>

<p>Now I could loop back a wire from the low water float/pump connection and wire it to a 220V optocoupler module to determine if the pump was running, but depending on the distance of the control box from the pump this could be extremely difficult. Also, since the low water float is most likely wired directly to one of the pump leads, it is also most likely connected with a special water proof heat shrink connector which complicates things, and making this project more complicated is not an objective.</p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/GravityAnalogACCurrentSensor(20A).jpg" alt="Gravity Analog AC Current Sensor (20A)" /></p>

<p>Using a current sensor that can be easily clipped around the power lead leaving the control box is much easier.  For this reason I chose the <a href="https://www.dfrobot.com/product-1486.html" target="_blank">Gravity 20A Analog AC Current Sensor from DFRobot</a>. I did originally look at using a cheaper current sensor based on the ZMCT103C, but after several hours of trying to get a reading, the fact that it had a 5A maximum, requires 5V to power and did not easily clip on to a wire I moved on to this sensor.</p>

<p>The DFRobot current sensor is easy to clip on to a wire, connect to the ESP32 and the example code was easy to implement with only one hitch discovering that the ESP32 12 bit analog requires you divide by 4096 verses 1024. In the photo below the sensor is connect with the positive lead (+) connected to the ESP32 3.3V (3V3), the negative lead (-) to ground (GND), the signal (A) connected to GPIO pin 35 and the AC transformer probe clipped on to one power lead.</p>

<table>
  <thead>
    <tr>
      <th>Gravity 20A Analog AC Current Sensor from DFRobot</th>
      <th>ESP32</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>+ (positive)</td>
      <td>3V3 (+3.3V)</td>
    </tr>
    <tr>
      <td>- (negative)</td>
      <td>GND</td>
    </tr>
    <tr>
      <td>A (Amperage signal)</td>
      <td>GPIO 35</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/IsThePumpRunning.jpg" alt="Is The Pump Running" /></p>

<p>The code can be found in the <code class="language-plaintext highlighter-rouge">/IsThePumpRunning</code> folder and is based off of the <a href="https://wiki.dfrobot.com/Gravity_Analog_AC_Current_Sensor__SKU_SEN0211_" target="_blank">vendors example code</a>. The LED will blink to indicate activity and the amperage will be displayed on the serial port about once every second. As before you will need to change the <code class="language-plaintext highlighter-rouge">LED_BUILTIN</code> constant if you use a pin other than GPIO 4.  You will also need to change the <code class="language-plaintext highlighter-rouge">AC_CURRENT_SENSOR</code> constant if you us a GPIO pin other than 35.</p>

<h3 id="where-is-my-data-sd-card-reader">Where is my Data (SD Card Reader)</h3>

<p>Later in this project I will look at logging data. To do this I will use an SD card. SD cards can store large amounts of data in a file format that can be read by any personal computer. The <a href="https://www.amazon.com/gp/product/B0989SM146/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&amp;psc=1" target="_blank">SD card reader</a> I chose to use is operated on 3.3V and you should note the description on Amazon is incorrect about it operating on 5V. You will find other SD card reader modules that work on 5V which are compatible with 3.3V inputs. I simple chose the 3.3V version to stock my workbench with modules I could use with future ESP32 projects that might run off of batteries.</p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/MicroSDSDHCTFCardAdapterReaderModulewithSPIInterface.jpg" alt="Micro SD Card Reader" /></p>

<p>The SD card reader module communicates using SPI communication protocol. You can connect it to the ESP32 using the default SPI pins.</p>

<table>
  <thead>
    <tr>
      <th>SD Card Reader</th>
      <th>ESP32</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>3V3</td>
      <td>3V3 (+3.3V)</td>
    </tr>
    <tr>
      <td>GND</td>
      <td>GND (-)</td>
    </tr>
    <tr>
      <td>CS</td>
      <td>GPIO 5</td>
    </tr>
    <tr>
      <td>MOSI</td>
      <td>GPIO 23</td>
    </tr>
    <tr>
      <td>CLK</td>
      <td>GPIO 18</td>
    </tr>
    <tr>
      <td>MISO</td>
      <td>GPIO 19</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/WhereIsMyData.jpg" alt="Where Is My Data" /></p>

<p>To test the module you will first want to format the micro SD card using your personal computer. Most laptops have an SD card slot you can use with an micro SD card adaptor or you may need to purchase a USB SD card reader. On a Windows machine you will open file manager, select the card and format it as FAT32.</p>

<p>The test code can be found in the <code class="language-plaintext highlighter-rouge">/WhereIsMyData</code> folder and is pulled from the ESP32 SD example code <code class="language-plaintext highlighter-rouge">SD_Test</code> with the <code class="language-plaintext highlighter-rouge">SD_CS</code> set to GPIO pin 5. The following are two good tutorials on using SD card readers with the ESP32.</p>

<p><a href="https://randomnerdtutorials.com/esp32-microsd-card-arduino/" target="_blank">ESP32: Guide for MicroSD Card Module Arduino - Random Nerd Tutorials</a></p>

<p><a href="https://lastminuteengineers.com/arduino-micro-sd-card-module-tutorial/" target="_blank">In-Depth Tutorial to Interface Micro SD Card Module with Arduino (lastminuteengineers.com)</a></p>

<h3 id="what-time-is-it-rts-ds3231">What Time is It? (RTS DS3231)</h3>

<p>As part of data logging and determining if the pump timer is correctly set, I need to know what time it is. To do this I will use a real-time clock module (RTC) with the DS3231 RTC chip and an AT24C32 EEPROM chip.</p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/DS3231AT24C32ClockModuleRealTimeClockModuleIICRTCModule.jpg" alt="DS3231 Real Time Clock Module" /></p>

<p>The RTC module communicates using the I2C interface for which the ESP32 has two build in IC2 controllers. The DS3231S RTC chip’s fixed I2C address is 0x68.</p>

<table>
  <thead>
    <tr>
      <th>RTC module</th>
      <th>ESP32</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>GND</td>
      <td>GND (-)</td>
    </tr>
    <tr>
      <td>3V3</td>
      <td>3V3 (+3.3V)</td>
    </tr>
    <tr>
      <td>SDA</td>
      <td>GPIO 21 (I2C SDA)</td>
    </tr>
    <tr>
      <td>SCL</td>
      <td>GPIO 22 (I2C SCL)</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/WhatTimeIsIt.jpg" alt="What Time is It" /></p>

<p>The test code can be found in the <code class="language-plaintext highlighter-rouge">/WhatTimeIsIt</code> folder and is pulled from the article <a href="https://lastminuteengineers.com/ds3231-rtc-arduino-tutorial/" target="_blank">In-Depth: Interface DS3231 Precision RTC Module with Arduino (lastminuteengineers.com)</a>. You will also need to load the <a href="https://github.com/Naguissa/uRTCLib" target="_blank">uRTCLib by Naguissa</a> library. The code can be used to set the chips time and then display the current time. Read the comments in the code for setting the current time.</p>

<h3 id="dont-let-me-forget-at24c32">Don’t Let Me Forget (AT24C32)</h3>

<p>One of the nice things about the real-time clock module (RTC) is that in addition to the DS3231 RTC chip, it also has an AT24C32 EEPROM chip which can hold 32K of information even when the power is off. For this project I will use this memory to store settings like, during what time should the pump be on, the Wi-Fi SSID and password and user names, passwords and roles.</p>

<p>The test code can be found in the <code class="language-plaintext highlighter-rouge">/DontLetMeForget</code> folder and is also pulled from the article <a href="https://lastminuteengineers.com/ds3231-rtc-arduino-tutorial/" target="_blank">In-Depth: Interface DS3231 Precision RTC Module with Arduino (lastminuteengineers.com)</a>. You will also need to load the <a href="https://github.com/Naguissa/uEEPROMLib" target="_blank">uEEPROMLib by Naguissa</a> library. The code writes an integer, float, character, and string to the 24C32 EEPROM and then reads them back.</p>

<h3 id="show-me-oled-display">Show Me (OLED Display)</h3>

<p>The last module to look at is an SSD1306 OLED 0.96 inch display with 128×64 pixels with an I2C interface. This module can be optional, but I added it because I’m not worked with one of these and I thought it would be useful to have some local visual feedback.</p>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/I2COLEDDisplayModule.jpg" alt="I2C OLED Display Module" /></p>

<p>Just like the RTC and EEPROM the display connects through the I2C interface. One thing to note is that while the silk screen printing on the back of the module indicates an I2C address of 0x78, the correct address 0x3C.</p>

<table>
  <thead>
    <tr>
      <th>OLED module</th>
      <th>ESP32</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>GND</td>
      <td>GND (-)</td>
    </tr>
    <tr>
      <td>VCC</td>
      <td>3V3 (+3.3V)</td>
    </tr>
    <tr>
      <td>SCL</td>
      <td>GPIO 22 (I2C SCL)</td>
    </tr>
    <tr>
      <td>SDA</td>
      <td>GPIO 21 (I2C SDA)</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/AerobicSepticSystemMonitor/ShowMe.jpg" alt="Show Me" /></p>

<p>The test code can be found in the <code class="language-plaintext highlighter-rouge">/ShowMe</code> folder and is pulled from the article <a href="https://lastminuteengineers.com/oled-display-arduino-tutorial/" target="_blank">In-Depth: Interface OLED Graphic Display Module with Arduino (lastminuteengineers.com)</a>. You will also need to load the <a href="https://github.com/adafruit/Adafruit_SSD1306" target="_blank">Adafruit SSD1306</a>,  <a href="https://github.com/adafruit/Adafruit-GFX-Library" target="_blank">Adafruit GFX</a>, and <a href="https://github.com/adafruit/Adafruit_BusIO" target="_blank">Adafruit Bus IO</a> libraries. The code displays various different text messages on the display. You can find additional examples in the article <a href="https://randomnerdtutorials.com/esp32-ssd1306-oled-display-arduino-ide/" target="_blank">ESP32 OLED Display with Arduino IDE - Random Nerd Tutorials</a>.</p>

<h3 id="more-to-come">More to Come…</h3>
<p>If you can’t wait for the next article “v1.0 The Basics”, you can get a sneak peek at the <a href="https://github.com/tgolla/AerobicSepticSystemMonitor" target="_blank">GitHub repository</a>.</p>]]></content><author><name>Terence Golla</name></author><category term="Arduino" /><category term="ESP32" /><category term="Aerobic-Septic" /><category term="Septic" /><category term="Current-Sensor" /><category term="Voltage-Sensor" /><category term="SD-Card" /><category term="Real-Time-Clock" /><category term="EEPROM" /><category term="OLED-Display" /><summary type="html"><![CDATA[The beta testing chapter is a look at each of the different components that make up the Aerobic Septic System Monitor. Each section is an exercise in connecting and programming a component. If you are new to the Arduino/ESP32 you will want to work through each section to gain a better understanding of each component attached to the ESP32. This chapter is optional and if you want to get directly to monitoring your aerobic septic system you can jump to “v1.0 The Basics”. Blink (ESP32) If you are new to the Arduino/ESP32 world, blink is the classic “Hello World!” program for the Arduino developer. This exercise involves using the build-in LED on pin 13 of many Arduino boards or wiring to an LED to an one of the many digital pins with a 220 ohm ballast resistor to limit the current flow. Since the ESP development board does not have a built-in LED I used the following wiring example. I my case, I used the breakout board and an Ideal Lever Wire Connector to connect the LED and 220 ohm resistor without the need to solder. ESP32 LED 220 ohm Resistor GND (-) Cathode (-)     Anode (+) Lead GPIO 4   Opposite Lead The blink sketch is included in this project in the folder labeled /Blink. With the ESP32 Development Board I chose there is no built in LED so I had to manually redefine the LED_BUILTIN constant in the code. In my case I used pin 4. If you are new to programming an Arduino/ESP32 I recommend starting by reading “Getting Started with Arduino IDE 2”. You can also search the internet for “Arduino blink” or “programming the Arduino” to find a great deal of articles and tutorials. Beyond Blink (ESP32) Beyond Blink is an exercise to look at the simplicity and power the ESP32 has when it comes to connecting to your Wi-Fi network and the Internet. The code was adapted from the Last Minute Engineers - Learn Electronics the Easy Way article In-depth: Create A Simple ESP32 Web Server In Arduino IDE (lastminuteengineers.com) and demonstrated how to connect your ESP32 to a Wi-Fi network hosing a web page that presents a button that can be used to turn the LED on and off. This exercise uses the same wired LED from the Blink exercise. You will again need to edit the code to modify LED_BUILTIN if you are using a pin other than 4 for the LED. You will also need to edit the configuration.h header file to add your Wi-Fi SSID and password. The code can be found in the /BeyondBlink folder. Is the Power On (220V Optocoupler Module) As part of this project I need to be able to see if the power is on. In particular, I want to monitor the pump timer, the air compressor pump timer, the high water alarm and the air compressor alarm. To do this I will use a simple 220V optocoupler module (pictured above) which can be connected to the hot 120V lead (‘L’ for load, typically a black wire) of any of the items I previously listed and common (‘N’ for neutral, typically a white wire) on one side of the module. On the other side I connect the module to 3.3 voltage (VCC - positive voltage), ground (GND - ground, negative) and one of the GPOI (General-Purpose Input-Output) pins of the ESP32 (OUT - signal). You can learn more about the ESP32 GPOI pins at ESP32 Pinout Reference: Which GPIO pins should you use? - Random Nerd Tutorials. Below is a diagram for a test circuit. 220V Optocoupler Module ESP32 VCC 3V3 (+3.3V) GND GND (-) OUT (signal) GPIO 34 The following is a photo of the AC circuit test board I built for testing based on the test circuit above. When plugged in and the the switch is on, the plug has power and the optocoupler module (green PCB (Printed Circuit Board), far right of photo) will sense voltage. In the next module I will discuss the current sensor (blue PCB, bottom of photo). The code can be found in the /IsThePowerOn folder and is extremely simplistic. Basically when power is sensed the LED I wired in the previous exercises is turned on and a message is sent to the serial port. As before you will need to change the LED_BUILTIN constant if you use a pin other than GPIO 4. You will also need to change the POWER_ON_OPTOCOUPLER_MODULE constant if you use a GPIO pin other than 34. In the photo below VCC on the optocoupler is wired to 3V3, OUT to GPIO pin 34 and GND to GND on the development breakout board. Is the Pump Running (Current Sensor) While determining if the pump has been turned on by either the pump timer or the high water override float is a simple mater of using a 220V optocoupler module to detect voltage, I can’t always be certain that the pump is running since a low water float is tied directly to the pump circuit after the control box wiring. The low water float is wired in to the circuit to keep the pump from running and possibly burning out when there is no water in the septic clear water tank. Now I could loop back a wire from the low water float/pump connection and wire it to a 220V optocoupler module to determine if the pump was running, but depending on the distance of the control box from the pump this could be extremely difficult. Also, since the low water float is most likely wired directly to one of the pump leads, it is also most likely connected with a special water proof heat shrink connector which complicates things, and making this project more complicated is not an objective. Using a current sensor that can be easily clipped around the power lead leaving the control box is much easier. For this reason I chose the Gravity 20A Analog AC Current Sensor from DFRobot. I did originally look at using a cheaper current sensor based on the ZMCT103C, but after several hours of trying to get a reading, the fact that it had a 5A maximum, requires 5V to power and did not easily clip on to a wire I moved on to this sensor. The DFRobot current sensor is easy to clip on to a wire, connect to the ESP32 and the example code was easy to implement with only one hitch discovering that the ESP32 12 bit analog requires you divide by 4096 verses 1024. In the photo below the sensor is connect with the positive lead (+) connected to the ESP32 3.3V (3V3), the negative lead (-) to ground (GND), the signal (A) connected to GPIO pin 35 and the AC transformer probe clipped on to one power lead. Gravity 20A Analog AC Current Sensor from DFRobot ESP32 + (positive) 3V3 (+3.3V) - (negative) GND A (Amperage signal) GPIO 35 The code can be found in the /IsThePumpRunning folder and is based off of the vendors example code. The LED will blink to indicate activity and the amperage will be displayed on the serial port about once every second. As before you will need to change the LED_BUILTIN constant if you use a pin other than GPIO 4. You will also need to change the AC_CURRENT_SENSOR constant if you us a GPIO pin other than 35. Where is my Data (SD Card Reader) Later in this project I will look at logging data. To do this I will use an SD card. SD cards can store large amounts of data in a file format that can be read by any personal computer. The SD card reader I chose to use is operated on 3.3V and you should note the description on Amazon is incorrect about it operating on 5V. You will find other SD card reader modules that work on 5V which are compatible with 3.3V inputs. I simple chose the 3.3V version to stock my workbench with modules I could use with future ESP32 projects that might run off of batteries. The SD card reader module communicates using SPI communication protocol. You can connect it to the ESP32 using the default SPI pins. SD Card Reader ESP32 3V3 3V3 (+3.3V) GND GND (-) CS GPIO 5 MOSI GPIO 23 CLK GPIO 18 MISO GPIO 19 To test the module you will first want to format the micro SD card using your personal computer. Most laptops have an SD card slot you can use with an micro SD card adaptor or you may need to purchase a USB SD card reader. On a Windows machine you will open file manager, select the card and format it as FAT32. The test code can be found in the /WhereIsMyData folder and is pulled from the ESP32 SD example code SD_Test with the SD_CS set to GPIO pin 5. The following are two good tutorials on using SD card readers with the ESP32. ESP32: Guide for MicroSD Card Module Arduino - Random Nerd Tutorials In-Depth Tutorial to Interface Micro SD Card Module with Arduino (lastminuteengineers.com) What Time is It? (RTS DS3231) As part of data logging and determining if the pump timer is correctly set, I need to know what time it is. To do this I will use a real-time clock module (RTC) with the DS3231 RTC chip and an AT24C32 EEPROM chip. The RTC module communicates using the I2C interface for which the ESP32 has two build in IC2 controllers. The DS3231S RTC chip’s fixed I2C address is 0x68. RTC module ESP32 GND GND (-) 3V3 3V3 (+3.3V) SDA GPIO 21 (I2C SDA) SCL GPIO 22 (I2C SCL) The test code can be found in the /WhatTimeIsIt folder and is pulled from the article In-Depth: Interface DS3231 Precision RTC Module with Arduino (lastminuteengineers.com). You will also need to load the uRTCLib by Naguissa library. The code can be used to set the chips time and then display the current time. Read the comments in the code for setting the current time. Don’t Let Me Forget (AT24C32) One of the nice things about the real-time clock module (RTC) is that in addition to the DS3231 RTC chip, it also has an AT24C32 EEPROM chip which can hold 32K of information even when the power is off. For this project I will use this memory to store settings like, during what time should the pump be on, the Wi-Fi SSID and password and user names, passwords and roles. The test code can be found in the /DontLetMeForget folder and is also pulled from the article In-Depth: Interface DS3231 Precision RTC Module with Arduino (lastminuteengineers.com). You will also need to load the uEEPROMLib by Naguissa library. The code writes an integer, float, character, and string to the 24C32 EEPROM and then reads them back. Show Me (OLED Display) The last module to look at is an SSD1306 OLED 0.96 inch display with 128×64 pixels with an I2C interface. This module can be optional, but I added it because I’m not worked with one of these and I thought it would be useful to have some local visual feedback. Just like the RTC and EEPROM the display connects through the I2C interface. One thing to note is that while the silk screen printing on the back of the module indicates an I2C address of 0x78, the correct address 0x3C. OLED module ESP32 GND GND (-) VCC 3V3 (+3.3V) SCL GPIO 22 (I2C SCL) SDA GPIO 21 (I2C SDA) The test code can be found in the /ShowMe folder and is pulled from the article In-Depth: Interface OLED Graphic Display Module with Arduino (lastminuteengineers.com). You will also need to load the Adafruit SSD1306, Adafruit GFX, and Adafruit Bus IO libraries. The code displays various different text messages on the display. You can find additional examples in the article ESP32 OLED Display with Arduino IDE - Random Nerd Tutorials. More to Come… If you can’t wait for the next article “v1.0 The Basics”, you can get a sneak peek at the GitHub repository.]]></summary></entry><entry><title type="html">Aerobic Septic System Monitor - Part 1 - Overview</title><link href="https://terencegolla.com/arduino/aerobic-septic-system-monitor-part-1-overview/" rel="alternate" type="text/html" title="Aerobic Septic System Monitor - Part 1 - Overview" /><published>2024-03-30T15:54:00-05:00</published><updated>2024-03-30T15:54:00-05:00</updated><id>https://terencegolla.com/arduino/aerobic-septic-system-monitor-part-1-overview</id><content type="html" xml:base="https://terencegolla.com/arduino/aerobic-septic-system-monitor-part-1-overview/"><![CDATA[<p>Recently I had an incident where sewer gas had forced it’s way into the house. Among the question of how this happened was the obvious question “Is the aerobic septic system working?” Having recently found the joy of IoT that the ESP8266 and ESP32 bring with the ease of programming like an Arduino and simplified connection to Wi-Fi and the Internet I thought “My dishwasher, pet feeder, vacuum, and light switches are all connected to the Internet, why not my septic system!”</p>

<p>Thus the birth of this project which will look at monitoring an existing mechanical system (AC sensing), logging usage data and alerting system failures all through a Wi-Fi Internet connection.</p>

<h3 id="what-is-an-aerobic-septic-system">What is an Aerobic Septic System</h3>

<p>An an Aerobic Septic System, also referred to as an Aerobic Wastewater Treatment System, is a small-scale sewage treatment system similar to a septic tank, but which uses an aerobic process (adding air or oxygen) for digestion rather than the anaerobic (without air) process used in septic systems.</p>

<p>Adding air promotes the growth of organisms that break down the solids, which are put through a clarifier and chlorinated for disinfection which produces a cleaner, more environmentally friendly discharge. This cleaner discharge eliminates the need for a drainage field (leach field) allowing the system to be installed on a small property where a standard septic tank system would not have been possible. Ref: <a href="https://jtsepticco.com/aerobic-septic-systems/" target="_blank">Aerobic Septic Systems Explained - JT Septic Co - NE Oklahoma Septic Experts</a></p>

<h3 id="components">Components</h3>

<p>The first and obvious thing you are going to need is an ESP32 board. I chose to go with a development board as they are easy to prototype with, exposing all the pins along with a USB-UART bridge, reset- and boot-mode buttons, an LDO regulator and a micro-USB connector. I chose a ESP32-WROOM-32U. The U designates an external IPEX MHF4 antenna connector. While my aerobic septic control box (timers, alarms, etc.) is mounted on the house relatively close to the WiFi router many control boxes get mounted far from the house (100-200ft) at the aerobic tank.  An external antenna in the design made more sense since I was still dealing with a a reasonable distance from the router and a concreate wall.  Better safe, than sorry.</p>

<p>For an antenna I purchased a simple 8dBi antenna with a IPEX MHF4 to RP-SMA female pigtail cable that allowed for through hole mounting of the RP-SMA connector such that the antenna can be mounted outside a waterproof enclosure box. if you need more range I would suggest that you look at using a high gain Yagi directional antenna which can go up to 20dBi.</p>

<p>While building a PCB to mount the ESP32 and other components was appealing, this project is a prototype and many of the sensor components are not designed well to place on a PCB board. So, I decided it would be best to make all connections to the ESP32 through a breakout board. This will also make the design more modular should a component need to be replaced.  Remember, I’m connecting sensors to AC circuits which are much more susceptible to power surges (i.e. lighting!).</p>

<p>Note that not all ESP32 development boards are equal when it comes to board width pin spacing. Some come in a 0.9” pin spacing, while others in a 1.0” pin spacing.  The HiLetgo board I chose comes in a 1.0” pin spacing. Not realizing this and thinking that spacing was the same for all development boards I initially bought a breakout board with the smaller 0.9” pin spacing. The one suggested in following parts list will accept both the 0.9” and 1.0” pin spacing.</p>

<p>The basic function of this project is to measure if the power is on/off and if the pump is actually running.  To determine if the power is on (i.e. the timer has switched on) I found some 220V optocoupler modules that can output a 3-5 volt TTL (digital) signal.  Connected to any GPIO (general-purpose input/output) pin you get 0 for on and 1 for off.</p>

<p>While the power may be turned on, there is no guarantee that the pump is actually running since the pump circuit contains a low water level float switch designed to turn the pump off when the tank is empty. This means that I have to monitor the current flow in the circuit to determine if the pump is really running. I originally chose a cheaper current sensor that only measures 0-5 amps as I didn’t care about what the current was, I only needed to know current was being pulled.  Turns out cheaper is not always better and after hours of trying to get a reading I decided to go with a current sensor that could read a full 20 amp range. The better sensor will also allow me to monitor the pump motor health by specifying an operational current range which could be used to indicate problems like a clogged intake or sprinkler outlet.</p>

<p>To log data I will need an SD card and real-time clock(RTC) to correctly timestamp the data. An Arduino compatible SD card reader with a SPI interface are extremity easy to find , as well as an I2C DS3231 real-time clock module. The real-time clock module suggested below also contains an AT24C32 EPROM memory chip which I will need to store things like the Wi-Fi SSID/password and administrator users/passwords. If you don’t buy a RTC with a memory chip you can add a separate memory module.</p>

<p>While it’s not necessary I thought it would be nice to add an OLED display. These are small, cheap and run on I2C. For this project the plan is to display things like the IP address (maybe even a QR code) and other useful status Information for when you open the enclosure to check/program.</p>

<p>To power the ESP32 and added modules I will need a USB wall charger and cable. The plan is to run power into the added enclosure using an extension cord with the female plug side in the box and male plug side cut off and spliced into the aerobic system power terminals. This way any standard USB wall charger can be plugged in to the female plug. The reason for the 6 foot cable is that it allows you to walk out to the septic system with your laptop and plug into ESP32 to diagnose and/or program as needed.</p>

<h4 id="parts-list">Parts List</h4>

<table>
  <thead>
    <tr>
      <th>Quantity</th>
      <th>Description</th>
      <th>Source Suggestion</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>ESP32 Development Board</td>
      <td><a href="https://www.amazon.com/dp/B09KLS2YB3?psc=1&amp;ref=ppx_yo2ov_dt_b_product_details" target="_blank">Amazon.com: HiLetgo 2pcs ESP32-DevKitC ESP32-WROOM-32U Core Board ESP32 ESP-32 ESP-WROOM-32U Development Board for Arduino : Electronics</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>Antenna</td>
      <td><a href="https://www.amazon.com/dp/B07R21LN5P?psc=1&amp;ref=ppx_yo2ov_dt_b_product_details" target="_blank">Amazon.com: 2 x 8dBi WiFi RP-SMA Male Antenna 2.4GHz 5.8GHz Dual Band +2 x 15CM U.FL/IPEX to RP-SMA Female Pigtail Cable for Mini PCIe Card Wireless Routers, PC Desktop, Repeater, FPV UAV Drone and PS4 Build : Electronics</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>Breakout Board</td>
      <td><a href="https://www.amazon.com/dp/B0BYS6THLF?psc=1&amp;ref=ppx_yo2ov_dt_b_product_details" target="_blank">Amazon.com: naughtystarts for ESP32 Breakout Board 3.5mm / 0.14” Terminal GPIO Expansion Board for 0.9” / 1.0” Size ESP32 Module ESP-WROOM-32 ESP32-DevKitC (Pack of 2pcs) : Electronics</a></td>
    </tr>
    <tr>
      <td>4</td>
      <td>220V Optocoupler Module</td>
      <td><a href="https://www.amazon.com/HiLetgo-Optocoupler-Alternating-Current-Channel/dp/B0CHJLYY3D/ref=sr_1_4?crid=3NMY4ZOHAL63&amp;keywords=220V%2BOptocoupler%2BModule&amp;qid=1707616569&amp;s=industrial&amp;sprefix=220v%2Boptocoupler%2Bmodule%2Cindustrial%2C115&amp;sr=1-4&amp;th=1" target="_blank">HiLetgo AC 220V Optocoupler Module AC Optocoupler Alternating Current Detect Module 3-5V or PLC 24V Level Power for PLC MCU(1 Channel): Amazon.com: Industrial &amp; Scientific</a><br /><a href="https://www.amazon.com/dp/B0C73GGKHX?psc=1&amp;ref=ppx_yo2ov_dt_b_product_details" target="_blank">Amazon.com: JESSINIE 3Pcs 1Channel AC 220V Optocoupler Module 220V Voltage Detect 220V Optocoupler Isolation Microcontroller TTL Level 3V-5V : Industrial &amp; Scientific</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>Current Sensor</td>
      <td><a href="https://www.dfrobot.com/product-1486.html" target="_blank">Gravity: Analog AC Current Sensor (20A) - DFRobot</a><br /><a href="https://www.electromaker.io/shop/product/gravity-analog-ac-current-sensor" target="_blank">Gravity Analog Ac Current Sensor - SEN0211 (electromaker.io)</a><br /><a href="https://www.amazon.com/Gravity-Conversion-Transformer-Non-contact-Measurement/dp/B0834T9T7M/ref=sr_1_1?crid=37R01SIE984NI&amp;dib=eyJ2IjoiMSJ9.pQZF_7XWzg2r8BOWxppsEw.fQGJyUnb38UZE81QAZmL_Af6NJ-5q_uVt92-crwQPYU&amp;dib_tag=se&amp;keywords=SEN0211&amp;qid=1711410513&amp;sprefix=sen0211%2Caps%2C100&amp;sr=8-1&amp;th=1" target="_blank">Amazon.com: Gravity: Analog AC Current Sensor (20A) - AC Current Signal Conversion Module and Open Type AC Transformer Probe Included - Non-contact Measurement : Industrial &amp; Scientific</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>SD Card Reader</td>
      <td><a href="https://www.amazon.com/dp/B07BJ2P6X6?psc=1&amp;ref=ppx_yo2ov_dt_b_product_details" target="_blank">Amazon.com: HiLetgo 5pcs Micro SD TF Card Adater Reader Module 6Pin SPI Interface Driver Module with chip Level Conversion for Arduino UNO R3 MEGA 2560 Due : Electronics</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>Real-Time Clock w/EEPROM Memory</td>
      <td><a href="https://www.amazon.com/dp/B00LX3V7F0?psc=1&amp;ref=ppx_yo2ov_dt_b_product_details" target="_blank">Amazon.com: HiLetgo 5pcs DS3231 AT24C32 Clock Module Real Time Clock Module IIC RTC Module for Arduino Without Battery : Industrial &amp; Scientific</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>EEPROM Memory (only necessary if the real-time clock module does not include memory){:target=”_blank”}</td>
      <td><a href="https://www.amazon.com/dp/B0C73CRV1G?ref=ppx_yo2ov_dt_b_product_details&amp;th=1" target="_blank">EC Buying 5Pcs AT24C02 Module I2C IIC Interface EEPROM Memory Module Intelligent Car Accessories with Dupont Wire at Amazon.com</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>Display (optional)</td>
      <td><a href="https://www.amazon.com/dp/B08VNRH5HR?ref=ppx_yo2ov_dt_b_product_details&amp;th=1" target="_blank">Amazon.com: Frienda 10 Pieces I2C OLED Display Module OLED Display Screen Driver IIC I2C Tabellone Seriale con Display Auto-Luminoso Compatible with Arduino/Raspberry PI : Electronics</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>USB Wall Charger</td>
      <td><a href="https://www.amazon.com/dp/B0BDWYL28S?ref=ppx_yo2ov_dt_b_product_details&amp;th=1" target="_blank">Amazon.com: (3 Pack) USB C Wall Charger, Dual Port PD Power Adapter Fast Charging Block for iPhone 15/15 Pro/15 Pro Max/15 Plus/14/13/12/11,X, Pad, Google Pixel, Samsung Galaxy and More : Cell Phones &amp; Accessories</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>6’ USB Cable</td>
      <td><a href="https://www.amazon.com/dp/B071H25C43?ref=ppx_yo2ov_dt_b_product_details&amp;th=1" target="_blank">Amazon.com: AILKIN Micro USB Cable, 5-Pack 6ft High Speed Nylon Braided Android Charging Cables for Samsung Galaxy J8/J7/S7/S6/Edge/Note5, Sony, Motorola, HTC, LG Android Tablets and More USB to Micro USB Cords : Electronics</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>220 Ω 1/4W Resistor (optional, beta testing)</td>
      <td><a href="https://www.amazon.com/" target="_blank">Amazon</a>, <a href="https://www.digikey.com/" target="_blank">DigiKey</a>, or <a href="https://www.mouser.com/" target="_blank">Mouser Electronics</a></td>
    </tr>
    <tr>
      <td>1</td>
      <td>5mm LED (optional, beta testing)</td>
      <td><a href="https://www.amazon.com/" target="_blank">Amazon</a>, <a href="https://www.digikey.com/" target="_blank">DigiKey</a>, or <a href="https://www.mouser.com/" target="_blank">Mouser Electronics</a></td>
    </tr>
  </tbody>
</table>

<h3 id="visual-studio-code">Visual Studio Code</h3>

<p>While the <a href="https://docs.arduino.cc/software/ide-v2/tutorials/getting-started-ide-v2/" target="_blank">Arduino IDE 2</a> has come a long way from what is now referred to as the Arduino Legacy IDE or Arduino IDE 1, a lot of developers including myself prefer to use VS Code, short for <a href="https://code.visualstudio.com/" target="_blank">Visual Studio Code</a>. VS Code is an extremely versatile editor that through the implementation of extensions can work across multiple languages and platforms. To use VS code you will need to install the <a href="https://marketplace.visualstudio.com/items?itemName=vsciot-vscode.vscode-arduino" target="_blank">Arduino</a> extension. You might also want to look at the <a href="https://platformio.org/" target="_blank">PlatformIO IDE for VS Code</a> extension.</p>

<h3 id="more-to-come">More to Come…</h3>
<p>Check out <a href="/arduino/aerobic-septic-system-monitor-part-2-beta-testing/">Part 2 - Beta Testing</a>.</p>]]></content><author><name>Terence Golla</name></author><category term="Arduino" /><category term="ESP32" /><category term="Aerobic-Septic" /><category term="Septic" /><category term="Current-Sensor" /><category term="Voltage-Sensor" /><category term="SD-Card" /><category term="Real-Time-Clock" /><category term="EEPROM" /><category term="OLED-Display" /><summary type="html"><![CDATA[Recently I had an incident where sewer gas had forced it’s way into the house. Among the question of how this happened was the obvious question “Is the aerobic septic system working?” Having recently found the joy of IoT that the ESP8266 and ESP32 bring with the ease of programming like an Arduino and simplified connection to Wi-Fi and the Internet I thought “My dishwasher, pet feeder, vacuum, and light switches are all connected to the Internet, why not my septic system!” Thus the birth of this project which will look at monitoring an existing mechanical system (AC sensing), logging usage data and alerting system failures all through a Wi-Fi Internet connection. What is an Aerobic Septic System An an Aerobic Septic System, also referred to as an Aerobic Wastewater Treatment System, is a small-scale sewage treatment system similar to a septic tank, but which uses an aerobic process (adding air or oxygen) for digestion rather than the anaerobic (without air) process used in septic systems. Adding air promotes the growth of organisms that break down the solids, which are put through a clarifier and chlorinated for disinfection which produces a cleaner, more environmentally friendly discharge. This cleaner discharge eliminates the need for a drainage field (leach field) allowing the system to be installed on a small property where a standard septic tank system would not have been possible. Ref: Aerobic Septic Systems Explained - JT Septic Co - NE Oklahoma Septic Experts Components The first and obvious thing you are going to need is an ESP32 board. I chose to go with a development board as they are easy to prototype with, exposing all the pins along with a USB-UART bridge, reset- and boot-mode buttons, an LDO regulator and a micro-USB connector. I chose a ESP32-WROOM-32U. The U designates an external IPEX MHF4 antenna connector. While my aerobic septic control box (timers, alarms, etc.) is mounted on the house relatively close to the WiFi router many control boxes get mounted far from the house (100-200ft) at the aerobic tank. An external antenna in the design made more sense since I was still dealing with a a reasonable distance from the router and a concreate wall. Better safe, than sorry. For an antenna I purchased a simple 8dBi antenna with a IPEX MHF4 to RP-SMA female pigtail cable that allowed for through hole mounting of the RP-SMA connector such that the antenna can be mounted outside a waterproof enclosure box. if you need more range I would suggest that you look at using a high gain Yagi directional antenna which can go up to 20dBi. While building a PCB to mount the ESP32 and other components was appealing, this project is a prototype and many of the sensor components are not designed well to place on a PCB board. So, I decided it would be best to make all connections to the ESP32 through a breakout board. This will also make the design more modular should a component need to be replaced. Remember, I’m connecting sensors to AC circuits which are much more susceptible to power surges (i.e. lighting!). Note that not all ESP32 development boards are equal when it comes to board width pin spacing. Some come in a 0.9” pin spacing, while others in a 1.0” pin spacing. The HiLetgo board I chose comes in a 1.0” pin spacing. Not realizing this and thinking that spacing was the same for all development boards I initially bought a breakout board with the smaller 0.9” pin spacing. The one suggested in following parts list will accept both the 0.9” and 1.0” pin spacing. The basic function of this project is to measure if the power is on/off and if the pump is actually running. To determine if the power is on (i.e. the timer has switched on) I found some 220V optocoupler modules that can output a 3-5 volt TTL (digital) signal. Connected to any GPIO (general-purpose input/output) pin you get 0 for on and 1 for off. While the power may be turned on, there is no guarantee that the pump is actually running since the pump circuit contains a low water level float switch designed to turn the pump off when the tank is empty. This means that I have to monitor the current flow in the circuit to determine if the pump is really running. I originally chose a cheaper current sensor that only measures 0-5 amps as I didn’t care about what the current was, I only needed to know current was being pulled. Turns out cheaper is not always better and after hours of trying to get a reading I decided to go with a current sensor that could read a full 20 amp range. The better sensor will also allow me to monitor the pump motor health by specifying an operational current range which could be used to indicate problems like a clogged intake or sprinkler outlet. To log data I will need an SD card and real-time clock(RTC) to correctly timestamp the data. An Arduino compatible SD card reader with a SPI interface are extremity easy to find , as well as an I2C DS3231 real-time clock module. The real-time clock module suggested below also contains an AT24C32 EPROM memory chip which I will need to store things like the Wi-Fi SSID/password and administrator users/passwords. If you don’t buy a RTC with a memory chip you can add a separate memory module. While it’s not necessary I thought it would be nice to add an OLED display. These are small, cheap and run on I2C. For this project the plan is to display things like the IP address (maybe even a QR code) and other useful status Information for when you open the enclosure to check/program. To power the ESP32 and added modules I will need a USB wall charger and cable. The plan is to run power into the added enclosure using an extension cord with the female plug side in the box and male plug side cut off and spliced into the aerobic system power terminals. This way any standard USB wall charger can be plugged in to the female plug. The reason for the 6 foot cable is that it allows you to walk out to the septic system with your laptop and plug into ESP32 to diagnose and/or program as needed. Parts List Quantity Description Source Suggestion 1 ESP32 Development Board Amazon.com: HiLetgo 2pcs ESP32-DevKitC ESP32-WROOM-32U Core Board ESP32 ESP-32 ESP-WROOM-32U Development Board for Arduino : Electronics 1 Antenna Amazon.com: 2 x 8dBi WiFi RP-SMA Male Antenna 2.4GHz 5.8GHz Dual Band +2 x 15CM U.FL/IPEX to RP-SMA Female Pigtail Cable for Mini PCIe Card Wireless Routers, PC Desktop, Repeater, FPV UAV Drone and PS4 Build : Electronics 1 Breakout Board Amazon.com: naughtystarts for ESP32 Breakout Board 3.5mm / 0.14” Terminal GPIO Expansion Board for 0.9” / 1.0” Size ESP32 Module ESP-WROOM-32 ESP32-DevKitC (Pack of 2pcs) : Electronics 4 220V Optocoupler Module HiLetgo AC 220V Optocoupler Module AC Optocoupler Alternating Current Detect Module 3-5V or PLC 24V Level Power for PLC MCU(1 Channel): Amazon.com: Industrial &amp; ScientificAmazon.com: JESSINIE 3Pcs 1Channel AC 220V Optocoupler Module 220V Voltage Detect 220V Optocoupler Isolation Microcontroller TTL Level 3V-5V : Industrial &amp; Scientific 1 Current Sensor Gravity: Analog AC Current Sensor (20A) - DFRobotGravity Analog Ac Current Sensor - SEN0211 (electromaker.io)Amazon.com: Gravity: Analog AC Current Sensor (20A) - AC Current Signal Conversion Module and Open Type AC Transformer Probe Included - Non-contact Measurement : Industrial &amp; Scientific 1 SD Card Reader Amazon.com: HiLetgo 5pcs Micro SD TF Card Adater Reader Module 6Pin SPI Interface Driver Module with chip Level Conversion for Arduino UNO R3 MEGA 2560 Due : Electronics 1 Real-Time Clock w/EEPROM Memory Amazon.com: HiLetgo 5pcs DS3231 AT24C32 Clock Module Real Time Clock Module IIC RTC Module for Arduino Without Battery : Industrial &amp; Scientific 1 EEPROM Memory (only necessary if the real-time clock module does not include memory){:target=”_blank”} EC Buying 5Pcs AT24C02 Module I2C IIC Interface EEPROM Memory Module Intelligent Car Accessories with Dupont Wire at Amazon.com 1 Display (optional) Amazon.com: Frienda 10 Pieces I2C OLED Display Module OLED Display Screen Driver IIC I2C Tabellone Seriale con Display Auto-Luminoso Compatible with Arduino/Raspberry PI : Electronics 1 USB Wall Charger Amazon.com: (3 Pack) USB C Wall Charger, Dual Port PD Power Adapter Fast Charging Block for iPhone 15/15 Pro/15 Pro Max/15 Plus/14/13/12/11,X, Pad, Google Pixel, Samsung Galaxy and More : Cell Phones &amp; Accessories 1 6’ USB Cable Amazon.com: AILKIN Micro USB Cable, 5-Pack 6ft High Speed Nylon Braided Android Charging Cables for Samsung Galaxy J8/J7/S7/S6/Edge/Note5, Sony, Motorola, HTC, LG Android Tablets and More USB to Micro USB Cords : Electronics 1 220 Ω 1/4W Resistor (optional, beta testing) Amazon, DigiKey, or Mouser Electronics 1 5mm LED (optional, beta testing) Amazon, DigiKey, or Mouser Electronics Visual Studio Code While the Arduino IDE 2 has come a long way from what is now referred to as the Arduino Legacy IDE or Arduino IDE 1, a lot of developers including myself prefer to use VS Code, short for Visual Studio Code. VS Code is an extremely versatile editor that through the implementation of extensions can work across multiple languages and platforms. To use VS code you will need to install the Arduino extension. You might also want to look at the PlatformIO IDE for VS Code extension. More to Come… Check out Part 2 - Beta Testing.]]></summary></entry><entry><title type="html">Typora - A New Type of Markdown Editor</title><link href="https://terencegolla.com/developer-tools/typora-a-new-type-of-markdown-editor/" rel="alternate" type="text/html" title="Typora - A New Type of Markdown Editor" /><published>2024-03-13T23:00:00-05:00</published><updated>2024-03-13T23:00:00-05:00</updated><id>https://terencegolla.com/developer-tools/typora-a-new-type-of-markdown-editor</id><content type="html" xml:base="https://terencegolla.com/developer-tools/typora-a-new-type-of-markdown-editor/"><![CDATA[<p><a href="https://typora.io/" target="_blank"><img src="/assets/images/posts/Typora-Icon-256x256.png" alt="Image" title="Typora" class="align-right" /></a> Markdown is a lightweight markup language for creating formatted text using a plain-text editor. It has most commonly been associated with the <a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes" target="_blank">README.md</a> file that you find in most GitHub repositories which automatically renders the file in a formatted view. As a result, markdown has become commonly used for documentation inside code repositories. In fact, this website is hosted on <a href="https://pages.github.com/" target="_blank">GitHub Pages</a> which uses markdown files for all of the site content. You can find out more about markdown at <a href="https://www.markdownguide.org/" target="_blank">Markdown Guide</a>  and <a href="https://docs.github.com/en/get-started/writing-on-github" target="_blank">Writing on GitHub</a> .</p>

<p>Typora is a new type of markdown editor. Where most all markdown editors, like VS Code have one window in which you type the plain text markdown and another window in which you can preview that text in a formatted view. Typora removes the preview window, mode switcher, syntax symbols of markdown source code, and all other unnecessary distractions. The result is a seamless experience in an editor that serves as both a reader and a writer. And yes, I’m using Typora to type this article.</p>

<p>If you do any kind of markdown editing, I strongly encourage you to <a href="https://typora.io/" target="_blank">download</a> the editor for a 15-day trial and I guarantee you will be sold on paying $14.99. And did I mention, it runs on Mac, Linux and Windows.</p>]]></content><author><name>Terence Golla</name></author><category term="Developer-Tools" /><category term="Typora" /><category term="Markdown" /><category term="Editor" /><category term="Windows" /><category term="Linux" /><category term="Mac" /><summary type="html"><![CDATA[Markdown is a lightweight markup language for creating formatted text using a plain-text editor. It has most commonly been associated with the README.md file that you find in most GitHub repositories which automatically renders the file in a formatted view. As a result, markdown has become commonly used for documentation inside code repositories. In fact, this website is hosted on GitHub Pages which uses markdown files for all of the site content. You can find out more about markdown at Markdown Guide and Writing on GitHub . Typora is a new type of markdown editor. Where most all markdown editors, like VS Code have one window in which you type the plain text markdown and another window in which you can preview that text in a formatted view. Typora removes the preview window, mode switcher, syntax symbols of markdown source code, and all other unnecessary distractions. The result is a seamless experience in an editor that serves as both a reader and a writer. And yes, I’m using Typora to type this article. If you do any kind of markdown editing, I strongly encourage you to download the editor for a 15-day trial and I guarantee you will be sold on paying $14.99. And did I mention, it runs on Mac, Linux and Windows.]]></summary></entry><entry><title type="html">SphereBot</title><link href="https://terencegolla.com/arduino/spherebot/" rel="alternate" type="text/html" title="SphereBot" /><published>2024-01-07T18:18:00-06:00</published><updated>2024-01-07T18:18:00-06:00</updated><id>https://terencegolla.com/arduino/spherebot</id><content type="html" xml:base="https://terencegolla.com/arduino/spherebot/"><![CDATA[<p>Meet the SphereBot, an Eggbot clone. This project started many years ago when my daughter decide she was going to show chickens at the county fair. For those of you who don’t live in the country, showing chickens is really a thing. While we never did get to the county fair with a chicken, and the daughter is now in college, the chickens are still around. And after building 4 chicken coops, dad has almost completed this project.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/cl23-D_daTk?si=g14S1Tx--WUTAQjp" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p><br /></p>

<p>Now when I say almost completed, I mean it’s only in pre-release because I have yet to complete the assembly documentation. I have been printing eggs for over 7 years and other than plans to write a simpler/user friendly version option for the non-hobbyist who has no desire to connect the SphereBot to a computer, the software is complete.</p>

<h2 id="whats-an-eggbot">What’s an EggBot?</h2>

<p>The EggBot is a compact, easy to use open-source art robot that can draw on spherical or egg-shaped objects. (ref: <a href="https://egg-bot.com/" target="_blank">The Original EggBot - Simple, fun, &amp; open source CNC art robot (egg-bot.com)</a>)</p>

<h2 id="what-is-a-spherebot">What is a SphereBot?</h2>

<p>The SphereBot, an Eggbot clone. Since the conception of the EggBot hobbyist have been trying to build their own version of the EggBot. Just search Thingiverse for <a href="https://www.thingiverse.com/search?q=eggbot&amp;page=1" target="_blank">EggBot</a> or <a href="https://www.thingiverse.com/search?q=spherebot&amp;page=1" target="_blank">SphereBot</a> and you will find many projects.</p>

<h2 id="what-is-version-20">What is version 2.0?</h2>

<p>As for the version 2.0 designation, this code is a fork of the <a href="https://github.com/zaggo/SphereBot" target="_blank">zaggo/SphereBot</a> originally modified for the <a href="https://www.adafruit.com/product/1438" target="_blank">Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit</a> in the <a href="https://github.com/jinschoi/SphereBot" target="_blank">jinschoi/SphereBot</a> fork. This fork takes on a large number of changes including consistent naming conventions, an advanced G-Code parser, additional custom G-Code commands, a touchscreen control, SD card reader support and audio alerts.</p>

<p>So, please check out version 2.0 <a href="https://github.com/tgolla/SphereBot" target="_blank">tgolla/SphereBot</a>!</p>]]></content><author><name>Terence Golla</name></author><category term="Arduino" /><category term="Arduino" /><category term="ESP8266" /><category term="ESP32" /><category term="Eggbot" /><category term="Spherebot" /><summary type="html"><![CDATA[Meet the SphereBot, an Eggbot clone. This project started many years ago when my daughter decide she was going to show chickens at the county fair. For those of you who don’t live in the country, showing chickens is really a thing. While we never did get to the county fair with a chicken, and the daughter is now in college, the chickens are still around. And after building 4 chicken coops, dad has almost completed this project. Now when I say almost completed, I mean it’s only in pre-release because I have yet to complete the assembly documentation. I have been printing eggs for over 7 years and other than plans to write a simpler/user friendly version option for the non-hobbyist who has no desire to connect the SphereBot to a computer, the software is complete. What’s an EggBot? The EggBot is a compact, easy to use open-source art robot that can draw on spherical or egg-shaped objects. (ref: The Original EggBot - Simple, fun, &amp; open source CNC art robot (egg-bot.com)) What is a SphereBot? The SphereBot, an Eggbot clone. Since the conception of the EggBot hobbyist have been trying to build their own version of the EggBot. Just search Thingiverse for EggBot or SphereBot and you will find many projects. What is version 2.0? As for the version 2.0 designation, this code is a fork of the zaggo/SphereBot originally modified for the Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit in the jinschoi/SphereBot fork. This fork takes on a large number of changes including consistent naming conventions, an advanced G-Code parser, additional custom G-Code commands, a touchscreen control, SD card reader support and audio alerts. So, please check out version 2.0 tgolla/SphereBot!]]></summary></entry><entry><title type="html">Why are you NOT using Visual Studio Code!</title><link href="https://terencegolla.com/developer-tools/why-are-you-not-using-visual-studio-code/" rel="alternate" type="text/html" title="Why are you NOT using Visual Studio Code!" /><published>2023-10-05T06:16:00-05:00</published><updated>2023-10-05T06:16:00-05:00</updated><id>https://terencegolla.com/developer-tools/why-are-you-not-using-visual-studio-code</id><content type="html" xml:base="https://terencegolla.com/developer-tools/why-are-you-not-using-visual-studio-code/"><![CDATA[<p><a href="https://code.visualstudio.com/" title="VS Code" target="_blank" class="align-right"><img src="/assets/images/posts/Visual-Studio-Code.png" alt="Image" title="VS Code" /></a> Seriously, in the last 2-3 years VS Code has surpassed editors like <a href="https://www.vim.org/" title="Vim" target="_blank">Vim</a>, <a href="https://notepad-plus-plus.org/" title="Notepad++" target="_blank">Notepad++</a>, <a href="https://atom-editor.cc/" title="Atom">Atom</a>, <a href="https://www.sublimetext.com/" title="Siblime Text" target="_blank">Siblime Text</a>, etc.</p>

<p><a href="https://code.visualstudio.com/" title="VS Code" target="_blank">VS Code</a> is a top shelf open-source editor that runs on Windows, Linux, and Mac with the IntelliSense you have come to love in Visual Studio, the ability to debug code and Git build built in.</p>

<p>Want more? Then just look at the at multitude of extensions in the <a href="https://marketplace.visualstudio.com/VSCode" title="VS Code Marketplace" target="_blank">VS Code Marketplace</a> that make it a first-class editor. The following are just a few of the VS Code extensions you should check out.</p>

<ul>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.vscode-dotnet-runtime" target="_blank">.NET Runtime Install Tool - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=vincent-ledu.adr-tools" target="_blank">adr-tools - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=vsciot-vscode.vscode-arduino" target="_blank">Arduino - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.azure-account" target="_blank">Azure Account - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=AzureADB2CTools.aadb2c" target="_blank">Azure AD B2C - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions" target="_blank">Azure Functions - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-logicapps" target="_blank">Azure Logic Apps (Consumption) - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.azure-repos" target="_blank">Azure Repos - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azureresourcegroups" target="_blank">Azure Resources - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azureterraform" target="_blank">Azure Terraform - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=jeff-hykin.better-cpp-syntax" target="_blank">Better C++ Syntax - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools" target="_blank">C/C++ - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools-extension-pack" target="_blank">C/C++ Extension Pack - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools-themes" target="_blank">C/C++ Themes - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp" target="_blank">C# - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=twxs.cmake" target="_blank">CMake - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=josetr.cmake-language-support-vscode" target="_blank">CMake Language Support - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools" target="_blank">CMake Tools - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers" target="_blank">Dev Containers - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker" target="_blank">Docker - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=cschlosser.doxdocgen" target="_blank">Doxygen Documentation Generator - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=DurableFunctionsMonitor.durablefunctionsmonitor" target="_blank">Durable Functions Monitor - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" target="_blank">ESLint - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph" target="_blank">Git Graph - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=donjayamanne.githistory" target="_blank">Git History - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=howardzuo.vscode-git-tags" target="_blank">Git Tags - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot" target="_blank">GitHub Copilot - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat" target="_blank">GitHub Copilot Chat - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github" target="_blank">GitHub Pull Requests and Issues - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.remotehub" target="_blank">GitHub Repositories - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=codezombiech.gitignore" target="_blank">gitignore - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens" target="_blank">GitLens — Git supercharged - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=GoogleCloudTools.cloudcode" target="_blank">Google Cloud Code - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform" target="_blank">HashiCorp Terraform - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ecmel.vscode-html-css" target="_blank">HTML CSS Support - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=icsharpcode.ilspy-vscode" target="_blank">ilspy-vscode - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-python.isort" target="_blank">isort - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=Dedsec727.jekyll-run" target="_blank">Jekyll Run - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ZainChen.json" target="_blank">json - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter" target="_blank">Jupyter - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-toolsai.vscode-jupyter-cell-tags" target="_blank">Jupyter Cell Tags - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter-keymap" target="_blank">Jupyter Keymap - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter-renderers" target="_blank">Jupyter Notebook Renderers - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-toolsai.vscode-jupyter-slideshow" target="_blank">Jupyter Slide Show - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare" target="_blank">Live Share - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one" target="_blank">Markdown All in One - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=yzane.markdown-pdf" target="_blank">Markdown PDF - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-edgedevtools.vscode-edge-devtools" target="_blank">Microsoft Edge Tools for VS Code - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ML2.nc-gcode" target="_blank">nc-gcode - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide" target="_blank">PlatformIO IDE - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=Postman.postman-for-vscode" target="_blank">Postman - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell" target="_blank">PowerShell - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" target="_blank">Prettier - Code formatter - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=pdconsec.vscode-print" target="_blank">Print - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance" target="_blank">Pylance - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python" target="_blank">Python - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh" target="_blank">Remote - SSH - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.remote-explorer" target="_blank">Remote Explorer - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.remote-repositories" target="_blank">Remote Repositories - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-serial-monitor" target="_blank">Serial Monitor - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client" target="_blank">Thunder Client - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=tonybaloney.vscode-pets" target="_blank">vscode-pets - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl" target="_blank">WSL - Visual Studio Marketplace</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=DotJoshJohnson.xml" target="_blank">XML Tools - Visual Studio Marketplace</a></li>
</ul>

<p>Disclaimer: Remember VS Code is not an IDE like Visual Studio; it is an extremely powerful editor that at the minimum complements Visual Studio and with the multitude of extension comes very close to being a full fledged IDE.  That said, check out <a href="https://platformio.org/" title="PlatformIO IDE for VSCode" target="_blank">PlatformIO IDE for VSCode</a> which turns VS Code into a powerful embedded board development IDE for Arduino, ESP8266, ESP32, etc.</p>]]></content><author><name>Terence Golla</name></author><category term="Developer-Tools" /><category term="VS-Code" /><category term="VS-Code_Marketplace" /><category term="Editor" /><category term="Debug" /><category term="IntelliSense" /><category term="Git" /><category term="GitHub" /><category term="Vim" /><category term="Notepad++" /><category term="Atom" /><category term="Sublime-Text" /><category term="Visual-Studio" /><category term="IDE" /><category term="PlatformIO" /><category term="Arduino" /><category term="ESP8266" /><category term="ESP32" /><category term="Windows" /><category term="Linux" /><category term="Mac" /><summary type="html"><![CDATA[Seriously, in the last 2-3 years VS Code has surpassed editors like Vim, Notepad++, Atom, Siblime Text, etc. VS Code is a top shelf open-source editor that runs on Windows, Linux, and Mac with the IntelliSense you have come to love in Visual Studio, the ability to debug code and Git build built in. Want more? Then just look at the at multitude of extensions in the VS Code Marketplace that make it a first-class editor. The following are just a few of the VS Code extensions you should check out. .NET Runtime Install Tool - Visual Studio Marketplace adr-tools - Visual Studio Marketplace Arduino - Visual Studio Marketplace Azure Account - Visual Studio Marketplace Azure AD B2C - Visual Studio Marketplace Azure Functions - Visual Studio Marketplace Azure Logic Apps (Consumption) - Visual Studio Marketplace Azure Repos - Visual Studio Marketplace Azure Resources - Visual Studio Marketplace Azure Terraform - Visual Studio Marketplace Better C++ Syntax - Visual Studio Marketplace C/C++ - Visual Studio Marketplace C/C++ Extension Pack - Visual Studio Marketplace C/C++ Themes - Visual Studio Marketplace C# - Visual Studio Marketplace CMake - Visual Studio Marketplace CMake Language Support - Visual Studio Marketplace CMake Tools - Visual Studio Marketplace Dev Containers - Visual Studio Marketplace Docker - Visual Studio Marketplace Doxygen Documentation Generator - Visual Studio Marketplace Durable Functions Monitor - Visual Studio Marketplace ESLint - Visual Studio Marketplace Git Graph - Visual Studio Marketplace Git History - Visual Studio Marketplace Git Tags - Visual Studio Marketplace GitHub Copilot - Visual Studio Marketplace GitHub Copilot Chat - Visual Studio Marketplace GitHub Pull Requests and Issues - Visual Studio Marketplace GitHub Repositories - Visual Studio Marketplace gitignore - Visual Studio Marketplace GitLens — Git supercharged - Visual Studio Marketplace Google Cloud Code - Visual Studio Marketplace HashiCorp Terraform - Visual Studio Marketplace HTML CSS Support - Visual Studio Marketplace ilspy-vscode - Visual Studio Marketplace isort - Visual Studio Marketplace Jekyll Run - Visual Studio Marketplace json - Visual Studio Marketplace Jupyter - Visual Studio Marketplace Jupyter Cell Tags - Visual Studio Marketplace Jupyter Keymap - Visual Studio Marketplace Jupyter Notebook Renderers - Visual Studio Marketplace Jupyter Slide Show - Visual Studio Marketplace Live Share - Visual Studio Marketplace Markdown All in One - Visual Studio Marketplace Markdown PDF - Visual Studio Marketplace Microsoft Edge Tools for VS Code - Visual Studio Marketplace nc-gcode - Visual Studio Marketplace PlatformIO IDE - Visual Studio Marketplace Postman - Visual Studio Marketplace PowerShell - Visual Studio Marketplace Prettier - Code formatter - Visual Studio Marketplace Print - Visual Studio Marketplace Pylance - Visual Studio Marketplace Python - Visual Studio Marketplace Remote - SSH - Visual Studio Marketplace Remote Explorer - Visual Studio Marketplace Remote Repositories - Visual Studio Marketplace Serial Monitor - Visual Studio Marketplace Thunder Client - Visual Studio Marketplace vscode-pets - Visual Studio Marketplace WSL - Visual Studio Marketplace XML Tools - Visual Studio Marketplace Disclaimer: Remember VS Code is not an IDE like Visual Studio; it is an extremely powerful editor that at the minimum complements Visual Studio and with the multitude of extension comes very close to being a full fledged IDE. That said, check out PlatformIO IDE for VSCode which turns VS Code into a powerful embedded board development IDE for Arduino, ESP8266, ESP32, etc.]]></summary></entry><entry><title type="html">Adding Authentication/Authorization Information to Swagger API Documentation with Swashbuckle</title><link href="https://terencegolla.com/.net/adding-authentication-authorization-information-to-swagger-api-documentation-with-swashbuckle/" rel="alternate" type="text/html" title="Adding Authentication/Authorization Information to Swagger API Documentation with Swashbuckle" /><published>2023-04-20T23:41:00-05:00</published><updated>2023-04-20T23:41:00-05:00</updated><id>https://terencegolla.com/.net/adding-authentication-authorization-information-to-swagger-api-documentation-with-swashbuckle</id><content type="html" xml:base="https://terencegolla.com/.net/adding-authentication-authorization-information-to-swagger-api-documentation-with-swashbuckle/"><![CDATA[<p>I’ve always wished swagger documentation included authentication and more importantly authorization information for each API call. Fortunately, Swashbuckle can be configured with various methods and filters to generate your very own customized Swagger documentation. Unfortunately, while the Swashbuckle documentation is good, it is often hard to find good examples.</p>

<p>This example started with a search of the Internet, with the thought that surely someone else had thought of this exact thing. But to my dismay, the only thing I found that came remotely close to what I was looking for, I found in the GitHub repository <a href="https://github.com/mattfrear/Swashbuckle.AspNetCore.Filters">Swashbuckle.AspNetCore.Filters</a> published by Matt Frear. In the code repository is an <code class="language-plaintext highlighter-rouge">AppendAuthorizeToSummaryOperationFilter</code> which appends authorization information to the API summary. While this was close to what I was looking for I was concerned about space, some of the APIs I need to document can have 5-10 policies.  I also wanted a bit more verbose description of the roles and/or policies required with the understanding that roles are ORed, while policies are ANDed.  And I need it to handle the custom authentication attribute <code class="language-plaintext highlighter-rouge">AuthorizeOnAnyOnePolicyAttribute</code>. More about this attribute can be found in the GitHub repository TGolla.Swashbuckle.AspNetCore TGolla.AspNetCore.Mvc.Filters <a href="https://github.com/tgolla/TGolla.Swashbuckle.AspNetCore/blob/main/TGolla.AspNetCore.Mvc.Filters/Readme.md"><code class="language-plaintext highlighter-rouge">Readme.md</code></a> file.</p>

<p>With an example to guide me I’ve put together the following operation filter which appends detailed information about authentication/authorization to the end of the API description. The description is the first thing you see following the summary and is populated with the text found inside the triple-slash comments <code class="language-plaintext highlighter-rouge">&lt;remarks&gt;&lt;/remarks&gt;</code> tags for the API call.</p>

<p>If you would like to see the filter in action check out the <code class="language-plaintext highlighter-rouge">AppendAuthorizationToDescriptionExample</code> website in the GitHub repository  <a href="https://github.com/tgolla/TGolla.Swashbuckle.AspNetCore/tree/main/TGolla.Swashbuckle.AspNetCore">TGolla.Swashbuckle.AspNetCore</a> or start using the filter in your project by installing the NuGet package <a href="https://www.nuget.org/packages/TGolla.Swashbuckle.AspNetCore/">TGolla.Swashbuckle.AspNetCore</a>.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Authorization</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.OpenApi.Models</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Swashbuckle.AspNetCore.SwaggerGen</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">TGolla.AspNetCore.Mvc.Filters</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">TGolla.Swashbuckle.AspNetCore.SwaggerGen</span>
<span class="p">{</span>
    <span class="c1">/// &lt;summary&gt;</span>
    <span class="c1">/// Appends the API authentication/authorization information to the operation description.</span>
    <span class="c1">/// &lt;/summary&gt;</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">AppendAuthorizationToDescription</span> <span class="p">:</span> <span class="n">IOperationFilter</span>
    <span class="p">{</span>
        <span class="c1">// Boolean used to determine if the AllowAnonymous description should be added.</span>
        <span class="k">private</span> <span class="kt">bool</span> <span class="n">excludeAllowAnonymousDescription</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>

        <span class="c1">/// &lt;summary&gt;</span>
        <span class="c1">/// Initializes a new instance of the AppendAuthorizationToDescription class. </span>
        <span class="c1">/// &lt;/summary&gt;</span>
        <span class="c1">/// &lt;param name="excludeAllowAnonymousDescription"&gt;Boolean used to determine if the AllowAnonymous description should be added.&lt;/param&gt;</span>
        <span class="k">public</span> <span class="nf">AppendAuthorizationToDescription</span><span class="p">(</span><span class="kt">bool</span> <span class="n">excludeAllowAnonymousDescription</span> <span class="p">=</span> <span class="k">false</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="n">excludeAllowAnonymousDescription</span> <span class="p">=</span> <span class="n">excludeAllowAnonymousDescription</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">/// &lt;summary&gt;</span>
        <span class="c1">/// Applys the appended API authentication/authorization information to the operation description.</span>
        <span class="c1">/// &lt;/summary&gt;</span>
        <span class="c1">/// &lt;param name="operation"&gt;An open API operation.&lt;/param&gt;</span>
        <span class="c1">/// &lt;param name="context"&gt;The operation filter context.&lt;/param&gt;</span>
        <span class="c1">/// &lt;remarks&gt;Basic concepts pulled from https://github.com/mattfrear/Swashbuckle.AspNetCore.Filters.&lt;/remarks&gt;</span>
        <span class="k">public</span> <span class="k">void</span> <span class="nf">Apply</span><span class="p">(</span><span class="n">OpenApiOperation</span> <span class="n">operation</span><span class="p">,</span> <span class="n">OperationFilterContext</span> <span class="n">context</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">context</span><span class="p">.</span><span class="n">GetControllerAndActionAttributes</span><span class="p">&lt;</span><span class="n">AllowAnonymousAttribute</span><span class="p">&gt;().</span><span class="nf">Any</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="k">if</span> <span class="p">(!</span><span class="n">excludeAllowAnonymousDescription</span><span class="p">)</span>
                    <span class="n">operation</span><span class="p">.</span><span class="n">Description</span> <span class="p">+=</span> <span class="s">"\r\n\r\nAuthentication/authorization is not required."</span><span class="p">;</span>

                <span class="k">return</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="kt">var</span> <span class="n">authorizeAttributes</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">GetControllerAndActionAttributes</span><span class="p">&lt;</span><span class="n">AuthorizeAttribute</span><span class="p">&gt;();</span>
            <span class="kt">var</span> <span class="n">authorizeOnAnyOnePolicyAttributes</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">GetControllerAndActionAttributes</span><span class="p">&lt;</span><span class="n">AuthorizeOnAnyOnePolicyAttribute</span><span class="p">&gt;();</span>

            <span class="c1">// Get list of authorize policies.</span>
            <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">authorizeAttributePolicies</span> <span class="p">=</span> <span class="n">authorizeAttributes</span><span class="p">.</span><span class="nf">AuthorizeAttributePolicies</span><span class="p">();</span>

            <span class="c1">// Get a list of roles.</span>
            <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">authorizeAttributeRoles</span> <span class="p">=</span> <span class="n">authorizeAttributes</span><span class="p">.</span><span class="nf">AuthorizeAttributeRoles</span><span class="p">();</span>

            <span class="c1">// Get list of authorize on any one policy policies. </span>
            <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">authorizeOnAnyOnePolicyAttributePolicies</span> <span class="p">=</span> <span class="n">authorizeOnAnyOnePolicyAttributes</span><span class="p">.</span><span class="nf">AuthorizeOnAnyOnePolicyAttributePolicies</span><span class="p">();</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">authorizeAttributePolicies</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
                <span class="n">operation</span><span class="p">.</span><span class="n">Description</span> <span class="p">+=</span> <span class="s">$"\r\n\r\nAuthorization requires </span><span class="p">{((</span><span class="n">authorizeAttributePolicies</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">1</span><span class="p">)</span> <span class="p">?</span> <span class="s">"each of "</span> <span class="p">:</span> <span class="s">""</span><span class="p">)}</span><span class="s"> the following </span><span class="p">{((</span><span class="n">authorizeAttributePolicies</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">1</span><span class="p">)</span> <span class="p">?</span> <span class="s">"policies"</span> <span class="p">:</span> <span class="s">"policy"</span><span class="p">)}</span><span class="s">: &lt;b&gt;</span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">"&lt;/b&gt;, &lt;b&gt;"</span><span class="p">,</span> <span class="n">authorizeAttributePolicies</span><span class="p">)}</span><span class="s">&lt;/b&gt;"</span><span class="p">;</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">authorizeAttributeRoles</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
                <span class="n">operation</span><span class="p">.</span><span class="n">Description</span> <span class="p">+=</span> <span class="s">$"\r\n\r\nAuthorization requires </span><span class="p">{((</span><span class="n">authorizeAttributeRoles</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">1</span><span class="p">)</span> <span class="p">?</span> <span class="s">"any one of "</span> <span class="p">:</span> <span class="s">""</span><span class="p">)}</span><span class="s"> the following </span><span class="p">{((</span><span class="n">authorizeAttributeRoles</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">1</span><span class="p">)</span> <span class="p">?</span> <span class="s">"roles"</span> <span class="p">:</span> <span class="s">"role"</span><span class="p">)}</span><span class="s">: &lt;b&gt;</span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">"&lt;/b&gt;, &lt;b&gt;"</span><span class="p">,</span> <span class="n">authorizeAttributeRoles</span><span class="p">)}</span><span class="s">&lt;/b&gt;"</span><span class="p">;</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">authorizeOnAnyOnePolicyAttributePolicies</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
                <span class="n">operation</span><span class="p">.</span><span class="n">Description</span> <span class="p">+=</span> <span class="s">$"\r\n\r\nAuthorization requires </span><span class="p">{((</span><span class="n">authorizeOnAnyOnePolicyAttributePolicies</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">1</span><span class="p">)</span> <span class="p">?</span> <span class="s">"any one of "</span> <span class="p">:</span> <span class="s">""</span><span class="p">)}</span><span class="s"> the following </span><span class="p">{((</span><span class="n">authorizeOnAnyOnePolicyAttributePolicies</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">1</span><span class="p">)</span> <span class="p">?</span> <span class="s">"policies"</span> <span class="p">:</span> <span class="s">"policy"</span><span class="p">)}</span><span class="s">: &lt;b&gt;</span><span class="p">{</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">"&lt;/b&gt;, &lt;b&gt;"</span><span class="p">,</span> <span class="n">authorizeOnAnyOnePolicyAttributePolicies</span><span class="p">)}</span><span class="s">&lt;/b&gt;"</span><span class="p">;</span>

            <span class="k">if</span> <span class="p">(</span><span class="n">authorizeAttributes</span><span class="p">.</span><span class="nf">Any</span><span class="p">()</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">authorizeAttributePolicies</span><span class="p">.</span><span class="nf">Any</span><span class="p">()</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">authorizeAttributeRoles</span><span class="p">.</span><span class="nf">Any</span><span class="p">()</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">authorizeOnAnyOnePolicyAttributePolicies</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
                <span class="n">operation</span><span class="p">.</span><span class="n">Description</span> <span class="p">+=</span> <span class="s">"\r\n\r\nAuthentication, but no authorization is required."</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The code is relatively simple.  The <code class="language-plaintext highlighter-rouge">AppendAuthorizationToDescription</code> operation filter is created using the <code class="language-plaintext highlighter-rouge">IOperationFilter</code> interface. The interface requires that you define the <code class="language-plaintext highlighter-rouge">Apply()</code> method. This method is handed an <code class="language-plaintext highlighter-rouge">OpenApiOperation</code> which gives you access to things like the API description and an <code class="language-plaintext highlighter-rouge">OperationFilterContext</code> which gives you access to the API attributes.</p>

<p>The first thing the <code class="language-plaintext highlighter-rouge">Apply()</code> method does is to look to see if the API method is decorated with an <code class="language-plaintext highlighter-rouge">AllowAnonymousAttribute</code>.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">AllowAnonymous</span><span class="p">]</span>
</code></pre></div></div>
<p>This is done by calling the <code class="language-plaintext highlighter-rouge">OperationFilterContext</code> extension method  <code class="language-plaintext highlighter-rouge">GetControllerAndActionAttributes</code>. This was pulled directly from the GitHub repository Swashbuckle.AspNetCore.Filters published by Matt Frear.  The method takes an <code class="language-plaintext highlighter-rouge">Attribute</code> type and returns an <code class="language-plaintext highlighter-rouge">IEnumerable</code> list of any attributes of the type passed. If an <code class="language-plaintext highlighter-rouge">AllowAnonymousAttribute</code> is found and assuming the <code class="language-plaintext highlighter-rouge">excludeAllowAnonymousDescription</code> is false the message “Authentication/authorization is not required.” is appended to the operation description.</p>

<p>If an <code class="language-plaintext highlighter-rouge">AllowAnonymousAttribute</code> was not found the code continues on to again uses the <code class="language-plaintext highlighter-rouge">GetControllerAndActionAttributes</code> method, this time to collect a list of attributes type <code class="language-plaintext highlighter-rouge">AuthorizeAttribute</code> and a list of attributes type <code class="language-plaintext highlighter-rouge">AuthorizeOnAnyOnePolicyAttribute</code>.  These lists are then queried using Linq to build string lists of policies, roles and authorize on any one policies with the extension methods <code class="language-plaintext highlighter-rouge">AuthorizeAttributePolicies</code>, <code class="language-plaintext highlighter-rouge">AuthorizeAttributeRoles</code>, and <code class="language-plaintext highlighter-rouge">AuthorizeOnAnyOnePolicyAttributePolicies</code>.  The string lists are then used to generate a verbose message concerning the authorization required which is appended to the description and if there are no required policies, roles or authorize on any one policies the message “Authentication, but no authorization is required.” is returned.</p>

<p>To implement the filter requires that you call the <code class="language-plaintext highlighter-rouge">OperationFilter&lt;&gt;()</code> method with the <code class="language-plaintext highlighter-rouge">AppendAuthorizationToDescription</code> class inside <code class="language-plaintext highlighter-rouge">AddSwaggerGen()</code> in your <code class="language-plaintext highlighter-rouge">progrms.cs</code> file.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
<span class="p">{</span>
	<span class="p">...</span>
    <span class="n">c</span><span class="p">.</span><span class="n">OperationFilter</span><span class="p">&lt;</span><span class="n">AppendAuthorizationToDescription</span><span class="p">&gt;();</span>
	<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If you wish to exclude the <code class="language-plaintext highlighter-rouge">AllowAnonymousAttribute</code> message “Authentication/authorization is not required.” from an API description you will need to set the <code class="language-plaintext highlighter-rouge">excludeAllowAnonymousDescription</code> parameter argument to true.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">c</span><span class="p">.</span><span class="n">OperationFilter</span><span class="p">&lt;</span><span class="n">AppendAuthorizationToDescription</span><span class="p">&gt;(</span><span class="k">true</span><span class="p">);</span>
</code></pre></div></div>

<p>Also included in the TGolla.Swashbuckle.AspNetCore code repository is the <code class="language-plaintext highlighter-rouge">AddSecurityRequirement</code> operation filter which allows you to target operations that require a security schema.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
<span class="p">{</span>
	<span class="p">...</span>
	<span class="n">c</span><span class="p">.</span><span class="nf">AddSecurityDefinition</span><span class="p">(</span><span class="s">"Bearer"</span><span class="p">,</span> <span class="k">new</span> <span class="n">OpenApiSecurityScheme</span>
    <span class="p">{</span>
        <span class="n">Description</span> <span class="p">=</span> <span class="s">"JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"  A token can be acquired using any one of the /Tokens API calls."</span><span class="p">,</span>
        <span class="n">Name</span> <span class="p">=</span> <span class="s">"Authorization"</span><span class="p">,</span>
        <span class="n">In</span> <span class="p">=</span> <span class="n">ParameterLocation</span><span class="p">.</span><span class="n">Header</span><span class="p">,</span>
        <span class="n">Type</span> <span class="p">=</span> <span class="n">SecuritySchemeType</span><span class="p">.</span><span class="n">ApiKey</span><span class="p">,</span>
        <span class="n">Scheme</span> <span class="p">=</span> <span class="s">"Bearer"</span>
    <span class="p">});</span>
   	<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When you define a security schema by invoking the <code class="language-plaintext highlighter-rouge">AddSecurityDefinition</code> method you also need to indicate which operations that scheme is applicable to. You can apply schemes globally (i.e. to ALL operations) through the <code class="language-plaintext highlighter-rouge">AddSecurityRequirement</code> method. This is what adds the Authorize button and unlock/lock icons to the end of each API summary.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
<span class="p">{</span>
	<span class="p">...</span>
	<span class="n">c</span><span class="p">.</span><span class="nf">AddSecurityRequirement</span><span class="p">(</span><span class="k">new</span> <span class="nf">OpenApiSecurityRequirement</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="p">{</span>
            <span class="k">new</span> <span class="n">OpenApiSecurityScheme</span>
            <span class="p">{</span>
                <span class="n">Reference</span> <span class="p">=</span> <span class="k">new</span> <span class="n">OpenApiReference</span>
                <span class="p">{</span>
                    <span class="n">Id</span> <span class="p">=</span> <span class="s">"Bearer"</span><span class="p">,</span>
                    <span class="n">Type</span> <span class="p">=</span> <span class="n">ReferenceType</span><span class="p">.</span><span class="n">SecurityScheme</span>
                <span class="p">}</span>
            <span class="p">},</span>
            <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()</span>
        <span class="p">}</span>
    <span class="p">});</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Or you can be more specific by replacing the <code class="language-plaintext highlighter-rouge">AddSecurityRequirement</code> method with the <code class="language-plaintext highlighter-rouge">AddSecurityRequirement</code> operation filter provide in this example. The filter will only apply the security schema to API actions decorated with either the <code class="language-plaintext highlighter-rouge">AuthorizeAttribute</code> or <code class="language-plaintext highlighter-rouge">AuthorizeOnAnyOnePolicyAttribute</code> attributes. In this configuration it also makes sense to set the <code class="language-plaintext highlighter-rouge">excludeAllowAnonymousDescription</code> parameter argument to true.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddSwaggerGen</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span>
<span class="p">{</span>
	<span class="p">...</span>
    <span class="n">c</span><span class="p">.</span><span class="n">OperationFilter</span><span class="p">&lt;</span><span class="n">AppendAuthorizationToDescription</span><span class="p">&gt;(</span><span class="k">true</span><span class="p">);</span>
    <span class="p">...</span>
    <span class="n">c</span><span class="p">.</span><span class="n">OperationFilter</span><span class="p">&lt;</span><span class="n">AddSecurityRequirement</span><span class="p">&gt;(</span><span class="k">new</span> <span class="n">OpenApiSecurityScheme</span>
    <span class="p">{</span>
        <span class="n">Reference</span> <span class="p">=</span> <span class="k">new</span> <span class="n">OpenApiReference</span>
        <span class="p">{</span>
            <span class="n">Id</span> <span class="p">=</span> <span class="s">"Bearer"</span><span class="p">,</span>
            <span class="n">Type</span> <span class="p">=</span> <span class="n">ReferenceType</span><span class="p">.</span><span class="n">SecurityScheme</span>
        <span class="p">}</span>
    <span class="p">});</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Addition information on adding security definitions and requirements can be found the Swashbuckle documentation <a href="https://github.com/domaindrivendev/Swashbuckle.AspNetCore#add-security-definitions-and-requirements">domaindrivendev/Swashbuckle.AspNetCore: Swagger tools for documenting API’s built on ASP.NET Core (github.com)</a>.</p>]]></content><author><name>Terence Golla</name></author><category term=".NET" /><category term="Swashbuckle" /><category term="Swagger" /><category term="IOperationFilter" /><category term="Authorize" /><category term="Policy" /><summary type="html"><![CDATA[I’ve always wished swagger documentation included authentication and more importantly authorization information for each API call. Fortunately, Swashbuckle can be configured with various methods and filters to generate your very own customized Swagger documentation. Unfortunately, while the Swashbuckle documentation is good, it is often hard to find good examples. This example started with a search of the Internet, with the thought that surely someone else had thought of this exact thing. But to my dismay, the only thing I found that came remotely close to what I was looking for, I found in the GitHub repository Swashbuckle.AspNetCore.Filters published by Matt Frear. In the code repository is an AppendAuthorizeToSummaryOperationFilter which appends authorization information to the API summary. While this was close to what I was looking for I was concerned about space, some of the APIs I need to document can have 5-10 policies. I also wanted a bit more verbose description of the roles and/or policies required with the understanding that roles are ORed, while policies are ANDed. And I need it to handle the custom authentication attribute AuthorizeOnAnyOnePolicyAttribute. More about this attribute can be found in the GitHub repository TGolla.Swashbuckle.AspNetCore TGolla.AspNetCore.Mvc.Filters Readme.md file. With an example to guide me I’ve put together the following operation filter which appends detailed information about authentication/authorization to the end of the API description. The description is the first thing you see following the summary and is populated with the text found inside the triple-slash comments &lt;remarks&gt;&lt;/remarks&gt; tags for the API call. If you would like to see the filter in action check out the AppendAuthorizationToDescriptionExample website in the GitHub repository TGolla.Swashbuckle.AspNetCore or start using the filter in your project by installing the NuGet package TGolla.Swashbuckle.AspNetCore. using Microsoft.AspNetCore.Authorization; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using TGolla.AspNetCore.Mvc.Filters; namespace TGolla.Swashbuckle.AspNetCore.SwaggerGen { /// &lt;summary&gt; /// Appends the API authentication/authorization information to the operation description. /// &lt;/summary&gt; public class AppendAuthorizationToDescription : IOperationFilter { // Boolean used to determine if the AllowAnonymous description should be added. private bool excludeAllowAnonymousDescription = false; /// &lt;summary&gt; /// Initializes a new instance of the AppendAuthorizationToDescription class. /// &lt;/summary&gt; /// &lt;param name="excludeAllowAnonymousDescription"&gt;Boolean used to determine if the AllowAnonymous description should be added.&lt;/param&gt; public AppendAuthorizationToDescription(bool excludeAllowAnonymousDescription = false) { this.excludeAllowAnonymousDescription = excludeAllowAnonymousDescription; } /// &lt;summary&gt; /// Applys the appended API authentication/authorization information to the operation description. /// &lt;/summary&gt; /// &lt;param name="operation"&gt;An open API operation.&lt;/param&gt; /// &lt;param name="context"&gt;The operation filter context.&lt;/param&gt; /// &lt;remarks&gt;Basic concepts pulled from https://github.com/mattfrear/Swashbuckle.AspNetCore.Filters.&lt;/remarks&gt; public void Apply(OpenApiOperation operation, OperationFilterContext context) { if (context.GetControllerAndActionAttributes&lt;AllowAnonymousAttribute&gt;().Any()) { if (!excludeAllowAnonymousDescription) operation.Description += "\r\n\r\nAuthentication/authorization is not required."; return; } var authorizeAttributes = context.GetControllerAndActionAttributes&lt;AuthorizeAttribute&gt;(); var authorizeOnAnyOnePolicyAttributes = context.GetControllerAndActionAttributes&lt;AuthorizeOnAnyOnePolicyAttribute&gt;(); // Get list of authorize policies. List&lt;string&gt; authorizeAttributePolicies = authorizeAttributes.AuthorizeAttributePolicies(); // Get a list of roles. List&lt;string&gt; authorizeAttributeRoles = authorizeAttributes.AuthorizeAttributeRoles(); // Get list of authorize on any one policy policies. List&lt;string&gt; authorizeOnAnyOnePolicyAttributePolicies = authorizeOnAnyOnePolicyAttributes.AuthorizeOnAnyOnePolicyAttributePolicies(); if (authorizeAttributePolicies.Any()) operation.Description += $"\r\n\r\nAuthorization requires {((authorizeAttributePolicies.Count &gt; 1) ? "each of " : "")} the following {((authorizeAttributePolicies.Count &gt; 1) ? "policies" : "policy")}: &lt;b&gt;{string.Join("&lt;/b&gt;, &lt;b&gt;", authorizeAttributePolicies)}&lt;/b&gt;"; if (authorizeAttributeRoles.Any()) operation.Description += $"\r\n\r\nAuthorization requires {((authorizeAttributeRoles.Count &gt; 1) ? "any one of " : "")} the following {((authorizeAttributeRoles.Count &gt; 1) ? "roles" : "role")}: &lt;b&gt;{string.Join("&lt;/b&gt;, &lt;b&gt;", authorizeAttributeRoles)}&lt;/b&gt;"; if (authorizeOnAnyOnePolicyAttributePolicies.Any()) operation.Description += $"\r\n\r\nAuthorization requires {((authorizeOnAnyOnePolicyAttributePolicies.Count &gt; 1) ? "any one of " : "")} the following {((authorizeOnAnyOnePolicyAttributePolicies.Count &gt; 1) ? "policies" : "policy")}: &lt;b&gt;{string.Join("&lt;/b&gt;, &lt;b&gt;", authorizeOnAnyOnePolicyAttributePolicies)}&lt;/b&gt;"; if (authorizeAttributes.Any() &amp;&amp; !authorizeAttributePolicies.Any() &amp;&amp; !authorizeAttributeRoles.Any() &amp;&amp; !authorizeOnAnyOnePolicyAttributePolicies.Any()) operation.Description += "\r\n\r\nAuthentication, but no authorization is required."; } } } The code is relatively simple. The AppendAuthorizationToDescription operation filter is created using the IOperationFilter interface. The interface requires that you define the Apply() method. This method is handed an OpenApiOperation which gives you access to things like the API description and an OperationFilterContext which gives you access to the API attributes. The first thing the Apply() method does is to look to see if the API method is decorated with an AllowAnonymousAttribute. [AllowAnonymous] This is done by calling the OperationFilterContext extension method GetControllerAndActionAttributes. This was pulled directly from the GitHub repository Swashbuckle.AspNetCore.Filters published by Matt Frear. The method takes an Attribute type and returns an IEnumerable list of any attributes of the type passed. If an AllowAnonymousAttribute is found and assuming the excludeAllowAnonymousDescription is false the message “Authentication/authorization is not required.” is appended to the operation description. If an AllowAnonymousAttribute was not found the code continues on to again uses the GetControllerAndActionAttributes method, this time to collect a list of attributes type AuthorizeAttribute and a list of attributes type AuthorizeOnAnyOnePolicyAttribute. These lists are then queried using Linq to build string lists of policies, roles and authorize on any one policies with the extension methods AuthorizeAttributePolicies, AuthorizeAttributeRoles, and AuthorizeOnAnyOnePolicyAttributePolicies. The string lists are then used to generate a verbose message concerning the authorization required which is appended to the description and if there are no required policies, roles or authorize on any one policies the message “Authentication, but no authorization is required.” is returned. To implement the filter requires that you call the OperationFilter&lt;&gt;() method with the AppendAuthorizationToDescription class inside AddSwaggerGen() in your progrms.cs file. builder.Services.AddSwaggerGen(c =&gt; { ... c.OperationFilter&lt;AppendAuthorizationToDescription&gt;(); ... } If you wish to exclude the AllowAnonymousAttribute message “Authentication/authorization is not required.” from an API description you will need to set the excludeAllowAnonymousDescription parameter argument to true. c.OperationFilter&lt;AppendAuthorizationToDescription&gt;(true); Also included in the TGolla.Swashbuckle.AspNetCore code repository is the AddSecurityRequirement operation filter which allows you to target operations that require a security schema. builder.Services.AddSwaggerGen(c =&gt; { ... c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\" A token can be acquired using any one of the /Tokens API calls.", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, Scheme = "Bearer" }); ... } When you define a security schema by invoking the AddSecurityDefinition method you also need to indicate which operations that scheme is applicable to. You can apply schemes globally (i.e. to ALL operations) through the AddSecurityRequirement method. This is what adds the Authorize button and unlock/lock icons to the end of each API summary. builder.Services.AddSwaggerGen(c =&gt; { ... c.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }, new List&lt;string&gt;() } }); ... } Or you can be more specific by replacing the AddSecurityRequirement method with the AddSecurityRequirement operation filter provide in this example. The filter will only apply the security schema to API actions decorated with either the AuthorizeAttribute or AuthorizeOnAnyOnePolicyAttribute attributes. In this configuration it also makes sense to set the excludeAllowAnonymousDescription parameter argument to true. builder.Services.AddSwaggerGen(c =&gt; { ... c.OperationFilter&lt;AppendAuthorizationToDescription&gt;(true); ... c.OperationFilter&lt;AddSecurityRequirement&gt;(new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }); ... } Addition information on adding security definitions and requirements can be found the Swashbuckle documentation domaindrivendev/Swashbuckle.AspNetCore: Swagger tools for documenting API’s built on ASP.NET Core (github.com).]]></summary></entry><entry><title type="html">Expresso</title><link href="https://terencegolla.com/developer-tools/expresso/" rel="alternate" type="text/html" title="Expresso" /><published>2022-04-08T04:51:00-05:00</published><updated>2022-04-08T04:51:00-05:00</updated><id>https://terencegolla.com/developer-tools/expresso</id><content type="html" xml:base="https://terencegolla.com/developer-tools/expresso/"><![CDATA[<p><a href="https://ultrapico.com/expresso.htm" target="_blank"><img src="/assets/images/posts/Expresso.png" alt="Image" title="Expresso" class="align-right" /></a> Recently a colleage of mine introduced me to a network AI tool our company had begun to use.  The tool allows you to setup different types of pattern matching, one of which is through the use of regular expressions.  When I asked him what type of tools he was using to build and test the regular expressions he looked at me like I was crazy.  That was until I introduced him to Expresso, a tool I have been using for over 20 years.</p>

<p>Expresso is a full feature regular expression (RegEx) development tool that allows you to easily build and test your regular expressions.  The tool allows you to create a project to build and test your specific regular expression which can be saved and modified in the future.</p>

<p>In design mode you can manually edit the regular expression; use an expression builder tool to create expression syntax by selecting criteria from a selection of tabs, checkboxes, and radio buttons; and provides an analyzer window that provide a treeview with an English translation of your regular expression.</p>

<p>In test mode you can provide a block of text on which you can execute and review the results of a match, partial match, exclude match, replace, validate and split.  The tool also comes with a library of regular expression projects and will generate code in C#, Visual Basic, Managed C++ and C++/CLI.</p>

<p>Check it out at <a href="https://ultrapico.com/expresso.htm" title="Expresso" target="_blank">https://ultrapico.com/expresso.htm</a>.</p>]]></content><author><name>Terence Golla</name></author><category term="Developer-Tools" /><category term="Regular-Expression" /><category term="RegEx" /><summary type="html"><![CDATA[Recently a colleage of mine introduced me to a network AI tool our company had begun to use. The tool allows you to setup different types of pattern matching, one of which is through the use of regular expressions. When I asked him what type of tools he was using to build and test the regular expressions he looked at me like I was crazy. That was until I introduced him to Expresso, a tool I have been using for over 20 years. Expresso is a full feature regular expression (RegEx) development tool that allows you to easily build and test your regular expressions. The tool allows you to create a project to build and test your specific regular expression which can be saved and modified in the future. In design mode you can manually edit the regular expression; use an expression builder tool to create expression syntax by selecting criteria from a selection of tabs, checkboxes, and radio buttons; and provides an analyzer window that provide a treeview with an English translation of your regular expression. In test mode you can provide a block of text on which you can execute and review the results of a match, partial match, exclude match, replace, validate and split. The tool also comes with a library of regular expression projects and will generate code in C#, Visual Basic, Managed C++ and C++/CLI. Check it out at https://ultrapico.com/expresso.htm.]]></summary></entry><entry><title type="html">Swashbuckle Custom Ordering of Controllers</title><link href="https://terencegolla.com/.net/swashbucklecustom-ordering-of-controllers/" rel="alternate" type="text/html" title="Swashbuckle Custom Ordering of Controllers" /><published>2021-09-16T14:41:00-05:00</published><updated>2021-09-16T14:41:00-05:00</updated><id>https://terencegolla.com/.net/swashbucklecustom-ordering-of-controllers</id><content type="html" xml:base="https://terencegolla.com/.net/swashbucklecustom-ordering-of-controllers/"><![CDATA[<p>This article is an adaptation of an article written by Rob Janssen (<a href="https://blog.robiii.nl/" title="RobIII">RobIII</a>) in 2018 on customizing the order in which controllers are display in the <a href="https://github.com/domaindrivendev/Swashbuckle.AspNetCore" title="Swagger UI by Swashbuckle">Swagger UI by Swashbuckle</a>.  It addresses the depreciation of the method OrderActionGroupsBy which is no longer available when using <code class="language-plaintext highlighter-rouge">AddSwaggerGen</code>, <code class="language-plaintext highlighter-rouge">UseSwagger</code> and <code class="language-plaintext highlighter-rouge">UseSwaggerUI</code> in your ASP.NET API projects by using the <code class="language-plaintext highlighter-rouge">AddSwaggerGen</code> method <code class="language-plaintext highlighter-rouge">OrderActionsBy</code> in the configuration.</p>

<p>By default, when using Swashbuckle to generate a Swagger document, controllers are ordered alphabetically.  There are however situations where alphabetical ordering is not best for your documentation.  For example, consider an API project in which each controller represents a type of theater (Arena, Black Box, Proscenium, Thrust).  Instead of sorting these alphabetically it makes more sense to order them as they are normally sorted when teaching theater (Proscenium, Thrust, Arena, Black Box).</p>

<p>In this article we will look at using a custom attribute to affect the order controllers are displayed in the Swagger documentation.  Again, most of this code was adapted from Rob Janssen’s article <a href="https://blog.robiii.nl/2018/08/swashbuckle-custom-ordering-of.html" title="Swashbuckle Custom Ordering of Controllers">Swashbuckle Custom Ordering of Controllers</a>. A complete example can be found in the GitHub repository <a href="https://github.com/tgolla/TGolla.Swashbuckle.AspNetCore/tree/main/SwashbuckleCustomOrderingControllersExample">TGolla.Swashbuckle.AspNetCore</a> and you can start using the <code class="language-plaintext highlighter-rouge">SwaggerControllerOrder</code> attribute in your project by installing the NuGet package <a href="https://www.nuget.org/packages/TGolla.Swashbuckle.AspNetCore">TGolla.Swashbuckle.AspNetCore</a>.</p>

<h2 id="how-it-works">How It Works</h2>

<p>When adding Swagger to your project using the Swashbuckle tools you have the ability to alter the sort order of the Controllers and even the individual APIs in the configuration information of the <code class="language-plaintext highlighter-rouge">AddSwaggerGen</code> method using <code class="language-plaintext highlighter-rouge">OrderActionsBy</code>.  With <code class="language-plaintext highlighter-rouge">OrderActionsBy</code> you use a lambda expression to alter the sort key string used to order (group) API calls.</p>

<p>By default, the sort key is set to the first tag which by default, is set to the controller’s name, hence controllers are sorted alphabetically after which API calls are normally ordered by relative path.  The following code represents the equivalent to the predefined default as defined using  <code class="language-plaintext highlighter-rouge">OrderActionsBy</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OrderActionsBy((apiDesc) =&gt; $"{apiDesc.ActionDescriptor.RouteValues["controller"]}");
</code></pre></div></div>

<p>In our example we are going to annotate each controller class with an order attribute, create a class that uses reflections to collect a list of all the controllers along with the order attribute value and expose a method in the class that returns the order and controller name in a sort key string.  This method can then be used in the <code class="language-plaintext highlighter-rouge">OrderActionsBy</code> lambda expression.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OrderActionsBy((apiDesc) =&gt; $"{swaggerControllerOrder.SortKey(apiDesc.ActionDescriptor.RouteValues["controller"])}")
</code></pre></div></div>

<p>We will also look at going one step further by adding additional information like the HTTP method (GET, POST, PUT, DELETE,…) to affect the order of the individual API calls.</p>

<h2 id="the-code">The Code</h2>
<p>To begin we need to define an attribute class (<code class="language-plaintext highlighter-rouge">SwaggerControllerOrderAttribute.cs</code>).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>namespace TGolla.Swashbuckle.AspNetCore.SwaggerGen
{
    /// &lt;summary&gt;
    /// Annotates a controller with a Swagger sorting order that is used when generating 
    /// the Swagger documentation to order the controllers in a specific desired order.
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;
    /// Ref: http://blog.robiii.nl/2018/08/swashbuckle-custom-ordering-of.html modified for AddSwaggerGen() extension OrderActionsBy().
    /// https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md#change-operation-sort-order-eg-for-ui-sorting
    /// &lt;/remarks&gt;
    public class SwaggerControllerOrderAttribute : Attribute
    {
        /// &lt;summary&gt;
        /// Gets the sorting order of the controller.
        /// &lt;/summary&gt;
        public uint Order { get; private set; }

        /// &lt;summary&gt;
        /// Initializes a new instance of the &lt;see cref="SwaggerControllerOrderAttribute"/&gt; class.
        /// &lt;/summary&gt;
        /// &lt;param name="order"&gt;Sets the sorting order of the controller.&lt;/param&gt;
        public SwaggerControllerOrderAttribute(uint order)
        {
            Order = order;
        }
    }
}
</code></pre></div></div>

<p>This class allows us to annotate a controller with the attribute <code class="language-plaintext highlighter-rouge">[SwaggerControllerOrder(x)]</code> where <em>x</em> is an integer value indicating the order by ascending value.  By default, a controller that is not annotated with the attribute is assigned the order value of 4294967295.  Controllers with the same order assigned either by default or with the attribute are then sorted alphabetically, however this can be altered when setting the <code class="language-plaintext highlighter-rouge">OrderActionBy</code> method.
In our example we will want to annotate each of our controllers as follows.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    [ApiController]
    [Route("theater/[controller]")]
    [SwaggerControllerOrder(1)]
    public class ProsceniumController : ControllerBase
    {
        // ...
    }

    [ApiController]
    [Route("theater/[controller]")]
    [SwaggerControllerOrder(2)]
    public class ThrustController: ControllerBase
    {
        // ...
    }

    [ApiController]
    [Route("theater/[controller]")]
    [SwaggerControllerOrder(3)]
    public class ArenaController: ControllerBase
    {
        // ...
    }

    [ApiController]
    [Route("theater/[controller]")]
    [SwaggerControllerOrder(4)]
    public class BlackBoxController: ControllerBase
    {
        // ...
    }
</code></pre></div></div>

<p>Next we need to create a class that will collect a list of controllers with the <code class="language-plaintext highlighter-rouge">SwaggerControllerOrder</code> attribute value.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using System.Reflection;

namespace TGolla.Swashbuckle.AspNetCore.SwaggerGen
{
    /// &lt;summary&gt;
    /// Class for determining controller sort keys based on the SwaggerControllerOrder attribute.
    /// &lt;/summary&gt;
    /// &lt;typeparam name="T"&gt;The type controllers should implement.  By default this would normally be ControllerBase or Controller
    /// unless you have derived your own specific api controller class.&lt;/typeparam&gt;
    /// &lt;remarks&gt;
    /// Ref: http://blog.robiii.nl/2018/08/swashbuckle-custom-ordering-of.html modified for AddSwaggerGen() extension OrderActionsBy().
    /// https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md#change-operation-sort-order-eg-for-ui-sorting
    /// &lt;/remarks&gt;
    public class SwaggerControllerOrder&lt;T&gt;
    {
        private readonly Dictionary&lt;string, uint&gt; orders;   // Our lookup table which contains controllername -&gt; sortorder pairs.

        /// &lt;summary&gt;
        /// Initializes a new instance of the &lt;see cref="SwaggerControllerOrder&amp;lt;TargetException&amp;gt;"/&gt; class.
        /// &lt;/summary&gt;
        /// &lt;param name="assembly"&gt;The assembly to scan for for classes implementing &lt;typeparamref name="T"/&gt;.&lt;/param&gt;
        public SwaggerControllerOrder(Assembly assembly)
            : this(GetFromAssembly&lt;T&gt;(assembly)) { }

        /// &lt;summary&gt;
        /// Initializes a new instance of the &lt;see cref="SwaggerControllerOrder&amp;lt;TargetException&amp;gt;"/&gt; class.
        /// &lt;/summary&gt;
        /// &lt;param name="controllers"&gt;
        /// The controllers to scan for a &lt;see cref="SwaggerControllerOrderAttribute"/&gt; to determine the sortorder.
        /// &lt;/param&gt;
        public SwaggerControllerOrder(IEnumerable&lt;Type&gt; controllers)
        {
            // Initialize our dictionary; scan the given controllers for our custom attribute, read the Order property
            // from the attribute and store it as controllername -&gt; sorderorder pair in the (case-insensitive) dicationary.
            orders = new Dictionary&lt;string, uint&gt;(
                controllers.Where(c =&gt; c.GetCustomAttributes&lt;SwaggerControllerOrderAttribute&gt;().Any())
                .Select(c =&gt; new { Name = ResolveControllerName(c.Name), c.GetCustomAttribute&lt;SwaggerControllerOrderAttribute&gt;().Order })
                .ToDictionary(v =&gt; v.Name, v =&gt; v.Order), StringComparer.OrdinalIgnoreCase);
        }

        /// &lt;summary&gt;
        /// Returns all &lt;typeparamref name="TController"/&gt;'s from the given assembly.
        /// &lt;/summary&gt;
        /// &lt;typeparam name="TController"&gt;The type classes must implement to be regarded a controller.&lt;/typeparam&gt;
        /// &lt;param name="assembly"&gt;The assembly to scan for given &lt;typeparamref name="TController"/&gt;s.&lt;/param&gt;
        /// &lt;returns&gt;Returns all types implementing &lt;typeparamref name="TController"/&gt;.&lt;/returns&gt;
        public static IEnumerable&lt;Type&gt; GetFromAssembly&lt;TController&gt;(Assembly assembly)
        {
            return assembly.GetTypes().Where(c =&gt; typeof(TController).IsAssignableFrom(c));
        }

        /// &lt;summary&gt;
        /// Determines the 'friendly' name of the controller by stripping the (by convention) "Controller" suffix
        /// from the name. If there's a built-in way to do this in .Net then I'd love to hear about it!
        /// &lt;/summary&gt;
        /// &lt;param name="name"&gt;The name of the controller.&lt;/param&gt;
        /// &lt;returns&gt;The friendly name of the controller.&lt;/returns&gt;
        private static string ResolveControllerName(string name)
        {
            const string suffix = "Controller"; // We want to strip "Controller" from "FooController"

            // Ensure name ends with suffix (case-insensitive)
            if (name.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
                // Return name with suffix stripped
                return name.Substring(0, name.Length - suffix.Length);
            // Suffix not found, return name as-is
            return name;
        }

        /// &lt;summary&gt;
        /// Returns the unsigned integer sort order value.  
        /// &lt;/summary&gt;
        /// &lt;param name="controller"&gt;The controller name.&lt;/param&gt;
        /// &lt;returns&gt;The unsigned integer sort order value.&lt;/returns&gt;
        private uint Order(string controller)
        {
            // Try to get the sort order value from our lookup; if none is found, assume uint.MaxValue.
            if (!orders.TryGetValue(controller, out uint order))
                order = uint.MaxValue;

            return order;
        }

        /// &lt;summary&gt;
        /// Returns an order key based on a the SwaggerControllerOrderAttribute for use with OrderActionsBy.
        /// &lt;/summary&gt;
        /// &lt;param name="controller"&gt;The controller name.&lt;/param&gt;
        /// &lt;returns&gt;A zero padded 32-bit unsigned integer.&lt;/returns&gt;
        public string OrderKey(string controller)
        {
            return Order(controller).ToString("D10");
        }

        /// &lt;summary&gt;
        /// Returns a sort key based on a the SwaggerControllerOrderAttribute for use with OrderActionsBy.
        /// &lt;/summary&gt;
        /// &lt;param name="controller"&gt;The controller name.&lt;/param&gt;
        /// &lt;returns&gt;A zero padded 32-bit unsigned integer combined with the controller's name.&lt;/returns&gt;
        public string SortKey(string controller)
        {
            return $"{OrderKey(controller)}_{controller}";
        }
    }
}
</code></pre></div></div>

<p>To use the class, we define an instance in the <code class="language-plaintext highlighter-rouge">ConfigureServices</code> method in the <code class="language-plaintext highlighter-rouge">Program.cs</code> file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SwaggerControllerOrder&lt;ControllerBase&gt; swaggerControllerOrder = new SwaggerControllerOrder&lt;ControllerBase&gt;(Assembly.GetEntryAssembly());
</code></pre></div></div>

<p>When instantiated the <code class="language-plaintext highlighter-rouge">SwaggerControllerOrder</code> class searches the assembly for controllers of type <code class="language-plaintext highlighter-rouge">ControllerBase</code> and builds a dictionary list of controller names with their associated <code class="language-plaintext highlighter-rouge">SwaggerControllerOrder</code> attribute value.  Those without the <code class="language-plaintext highlighter-rouge">SwaggerControllerOrder</code> attribute are not place in the list and will be assigned the default max value 4294967295.</p>

<p>With the class instantiated we can now use it when adding the Swagger generation service by adding an <code class="language-plaintext highlighter-rouge">OrderActionsBy</code> method call to the <code class="language-plaintext highlighter-rouge">AddSwaggerGen</code> configuration in the <code class="language-plaintext highlighter-rouge">Program.cs</code> file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>builder.Services.AddSwaggerGen(c =&gt;
{
    c.OrderActionsBy((apiDesc) =&gt; $"{swaggerControllerOrder.SortKey(apiDesc.ActionDescriptor.RouteValues["controller"])}");
});
</code></pre></div></div>

<h2 id="taking-it-a-step-further">Taking It a Step Further</h2>

<p>As mentioned earlier you can also use <code class="language-plaintext highlighter-rouge">OrderActionsBy</code> to  affect the order of the individual API calls.</p>

<p>For example if you are adding group names (i.e. <code class="language-plaintext highlighter-rouge">[ApiExplorerSettings(GroupName = "Hidden")]</code>) to certain API calls to say hide those API calls from certain users you but don’t want then to be group (displayed) separately for your advanced users you can add the relative path to the sort key string.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>c.OrderActionsBy((apiDesc) =&gt; $"{swaggerControllerOrder.SortKey(apiDesc.ActionDescriptor.RouteValues["controller"])}_{apiDesc.RelativePath}");
</code></pre></div></div>

<p>If you would like to see your API calls ordered alphabetically by HTTP method, you could use the following lambda expression.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>c.OrderActionsBy((apiDesc) =&gt; $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.HttpMethod}");
</code></pre></div></div>

<p>If you would like to see API calls ordered by HTTP method, in a specific order you could add the following sort order array…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>string[] methodsOrder = new string[5] { "get", "post", "put", "patch", "delete", "options", "trace" };
</code></pre></div></div>

<p>… with the following lambda expression.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>c.OrderActionsBy(apiDesc =&gt; $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{Array.IndexOf(methodsOrder, apiDesc.HttpMethod.ToLower())}");
</code></pre></div></div>

<p>The possibilities are endless, and this example could even be taken one step further by adding an attribute to annotate <code class="language-plaintext highlighter-rouge">IActionResult</code> (API) calls.</p>

<p>Ref: <a href="https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting" title="Change Operation Sort Order (e.g. for UI Sorting)">https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting</a></p>]]></content><author><name>Terence Golla</name></author><category term=".NET" /><category term="Swashbuckle" /><category term="Swagger" /><category term="AddSwaggerGen" /><category term="OrderActionsBy" /><summary type="html"><![CDATA[This article is an adaptation of an article written by Rob Janssen (RobIII) in 2018 on customizing the order in which controllers are display in the Swagger UI by Swashbuckle. It addresses the depreciation of the method OrderActionGroupsBy which is no longer available when using AddSwaggerGen, UseSwagger and UseSwaggerUI in your ASP.NET API projects by using the AddSwaggerGen method OrderActionsBy in the configuration.]]></summary></entry><entry><title type="html">Outputting Build Numbers in your Azure DevOps Pipeline Artifacts</title><link href="https://terencegolla.com/.net/outputting-build-numbers-in-your-azure-devops-pipeline-artifacts/" rel="alternate" type="text/html" title="Outputting Build Numbers in your Azure DevOps Pipeline Artifacts" /><published>2021-08-16T19:02:00-05:00</published><updated>2021-08-16T19:02:00-05:00</updated><id>https://terencegolla.com/.net/outputting-build-numbers-in-your-azure-devops-pipeline-artifacts</id><content type="html" xml:base="https://terencegolla.com/.net/outputting-build-numbers-in-your-azure-devops-pipeline-artifacts/"><![CDATA[<p>In the process of deploying and then testing code to development and staging environments there is always that question in the back of your head… Is the code I’m testing really the code I deployed?</p>

<p>In the world of the Azure DevOps pipelines the easiest way to identify the code you deploy is by the pipeline build number.</p>

<p><img src="/assets/images/posts/Azure-CI-Build-Pipeline.png" alt="Image" title="Azure CI Build Pipeline" class="align-center" /></p>

<p>What I’m going to talk about in the article is how you can output and then reference this build number with your deployed code.</p>

<p>The process requires that you first add a task to your pipeline YAML file that outputs the build number to a file in your artifacts which you can then access through your code.  A complete example can be found on GitHub at <a href="https://github.com/tgolla/BuildNumbersInAzurePipelineArtifactsExample" target="_blank">https://github.com/tgolla/BuildNumbersInAzurePipelineArtifactsExample</a>.</p>

<p>In your Azure pipeline YAML file (<code class="language-plaintext highlighter-rouge">azure-pipelines.yml</code>) you will need to add the following PowerShell task before the build task (<code class="language-plaintext highlighter-rouge">DotNetCoreCLI@2... Command: ‘build’</code>).</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Replace AzureBuildNumber.txt content with actual build number.</span>
<span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">PowerShell@2</span>
  <span class="na">inputs</span><span class="pi">:</span>
    <span class="na">targetType</span><span class="pi">:</span> <span class="s1">'</span><span class="s">inline'</span>
    <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
      <span class="s">New-Item $(Build.SourcesDirectory)/BuildNumbersInAzurePipelineArtifactsExample/AzureBuildNumber.txt -Force</span>
      <span class="s">Set-Content $(Build.SourcesDirectory)/BuildNumbersInAzurePipelineArtifactsExample/AzureBuildNumber.txt '$(Build.BuildNumber)'</span>
</code></pre></div></div>

<p>The task executes a PowerShell script that first creates (New Item) the AzureBuildNumber.txt file in the projects root folder and then adds (Set-Content) the build number.  If the file already exists, the -Force parameter deletes the existing file and content before recreating the file.</p>

<p>In your code add the following method.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">static</span> <span class="kt">string</span> <span class="n">AzureBuildNumber</span>
<span class="p">{</span>
    <span class="k">get</span>
    <span class="p">{</span>
        <span class="kt">string</span> <span class="n">azureBuildNumber</span> <span class="p">=</span> <span class="s">"Unknown"</span><span class="p">;</span>

        <span class="k">try</span>
        <span class="p">{</span>
            <span class="n">System</span><span class="p">.</span><span class="n">IO</span><span class="p">.</span><span class="n">StreamReader</span> <span class="n">file</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">IO</span><span class="p">.</span><span class="nf">StreamReader</span><span class="p">(</span><span class="s">"AzureBuildNumber.txt"</span><span class="p">);</span>

            <span class="n">azureBuildNumber</span> <span class="p">=</span> <span class="n">file</span><span class="p">.</span><span class="nf">ReadLine</span><span class="p">()</span> <span class="p">??</span> <span class="s">"Unknown"</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">catch</span>
        <span class="p">{</span>
            <span class="c1">// Do nothing.</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">azureBuildNumber</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The method will read the AzureBuildNumber.txt file from the projects deployed root folder and return the build number or ‘Unknown’ should the file be missing or empty.  The complete example defines an AssemblyInfo class that also returns the assembly name and informational version.</p>

<p>Note: The assembly informational version is defined in the XML project file (<code class="language-plaintext highlighter-rouge">BuildNumbersInAzurePipelineArtifactsExample.csproj</code>) as part of the property group.  By default, this is not automatically added to the project file and will need to be added manually or through the project’s properties tab under Package -&gt; Package version: which must initially be changed to something other than 1.0.0.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;PropertyGroup&gt;</span>
  <span class="nt">&lt;TargetFramework&gt;</span>netcoreapp3.1<span class="nt">&lt;/TargetFramework&gt;</span>
  <span class="nt">&lt;Version&gt;</span>1.0.1<span class="nt">&lt;/Version&gt;</span>
<span class="nt">&lt;/PropertyGroup&gt;</span>
</code></pre></div></div>]]></content><author><name>Terence Golla</name></author><category term=".NET" /><category term="Azure-DevOps" /><category term="Azure-DevOps-Pipeline" /><category term="Azure-Pipeline" /><category term="Azure-Pipeline-CI" /><category term="CI" /><category term="Azure-Pipeline-CI/CD" /><category term="CI/CD" /><summary type="html"><![CDATA[In the process of deploying and then testing code to development and staging environments there is always that question in the back of your head… Is the code I’m testing really the code I deployed?]]></summary></entry><entry><title type="html">NuGet Package Explorer (NPE)</title><link href="https://terencegolla.com/developer-tools/nuget-package-explorer-npe/" rel="alternate" type="text/html" title="NuGet Package Explorer (NPE)" /><published>2019-12-18T14:57:00-06:00</published><updated>2019-12-18T14:57:00-06:00</updated><id>https://terencegolla.com/developer-tools/nuget-package-explorer-npe</id><content type="html" xml:base="https://terencegolla.com/developer-tools/nuget-package-explorer-npe/"><![CDATA[<p><a href="https://github.com/NuGetPackageExplorer/NuGetPackageExplorer" target="_blank"><img src="/assets/images/posts/NuGet-Package-Explorer-Logo.png" alt="Image" title="NuGet Package Explorer Logo" class="align-right" /></a>NuGet Package Explorer (NPE) is an application that makes it easy to create and explore NuGet packages. You can load a .nupkg or .snupkg file from disk or directly from a feed such as <a href="http://nuget.org" title="NuGet" target="_blank">nuget.org</a>.  Even with the added functionality in Visual Studio to generate NuGet packaged in a build the tool is extremely useful if you are working with NuGet packages.</p>

<p>You can find out more about the application at its GitHub repository (<a href="https://github.com/NuGetPackageExplorer/NuGetPackageExplorer" title="NuGet Package Explorer (NPE)" target="_blank">https://github.com/NuGetPackageExplorer/NuGetPackageExplorer</a>).  Scroll down to the README.md documentation for information on installing the application.</p>]]></content><author><name>Terence Golla</name></author><category term="Developer-Tools" /><category term="NuGet" /><summary type="html"><![CDATA[NuGet Package Explorer (NPE) is an application that makes it easy to create and explore NuGet packages. You can load a .nupkg or .snupkg file from disk or directly from a feed such as nuget.org. Even with the added functionality in Visual Studio to generate NuGet packaged in a build the tool is extremely useful if you are working with NuGet packages.]]></summary></entry></feed>