<?xml version="1.0" encoding="UTF-8"?>        <rss version="2.0"
             xmlns:atom="http://www.w3.org/2005/Atom"
             xmlns:dc="http://purl.org/dc/elements/1.1/"
             xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
             xmlns:admin="http://webns.net/mvcb/"
             xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
             xmlns:content="http://purl.org/rss/1.0/modules/content/">
        <channel>
            <title>
									DroneBot Workshop Forums - Recent Topics				            </title>
            <link>https://forum.dronebotworkshop.com/</link>
            <description>Discussion board for Robotics, Arduino, Raspberry Pi and other DIY electronics and modules. Join us today!</description>
            <language>en-US</language>
            <lastBuildDate>Sat, 05 Sep 2026 11:44:46 +0000</lastBuildDate>
            <generator>wpForo</generator>
            <ttl>60</ttl>
							                    <item>
                        <title>Hi there I am Michel (or Mike) from the Netherlands!</title>
                        <link>https://forum.dronebotworkshop.com/introductions/hi-there-i-am-michel-or-mike-from-the-netherlands/</link>
                        <pubDate>Thu, 03 Sep 2026 18:22:15 +0000</pubDate>
                        <description><![CDATA[Pleased to meet you all ;-)! 
I am a retired Navy Officer with a university degree in electronics. Didn’t do much engineering with my knowledge, I was basically the kind off general manager...]]></description>
                        <content:encoded><![CDATA[<p>Pleased to meet you all ;-)! </p>
<p><br />I am a retired Navy Officer with a university degree in electronics. Didn’t do much engineering with my knowledge, I was basically the kind off general manager type. However….I recently got  myself an oscilloscoop, a signal generator, bench power station and I am studying electronics once more. I like to keep my brain working. I am a licensed HF radio operator, but not very active.</p>
<p>Presently I am really into the microcontroller stuf, got a lot of Arduino Nano’s and due to stupidities I have short circuited a lot of them. Learning by doing, studying and youtube offcourse, that’s how I got here! Just finished a small weather station and a real time clock with 8 led matrices. Gosh…..so many matrices destroyed due to wrong wiring ;-). But it is finally working.</p>
<p>The next project will be a GPS with TFT module. As a navy person and mountain guide I am very interested in navigation! I want geographical coordinates, UTM coordinates and local coordinates on one screen. Its not really practical, but I like the math that’s involved ;-).</p>
<p>And finally I would like to be able to make my own PCB’s. </p>
<p>I am absolutely not a pro, just a beginner in this microcontroller domain. I am looking forward to benefit from all the wisdom in this community. And sincerely hope I can contribute too.</p>
<p>Kind regards</p>
<p>Michel</p>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>Michel</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/introductions/hi-there-i-am-michel-or-mike-from-the-netherlands/</guid>
                    </item>
				                    <item>
                        <title>Ardruino clock help 2</title>
                        <link>https://forum.dronebotworkshop.com/arduino/ardruino-clock-help-2/</link>
                        <pubDate>Sun, 30 Aug 2026 19:12:49 +0000</pubDate>
                        <description><![CDATA[#include &lt;I2C_RTC.h&gt;

/*
Lee&#039;s Clock: Hopefully the final revision. 
In order to use the buttons:
    Hold &#039;set&#039; to adjust the &#039;minutes&#039; and &#039;brightness&#039;.
    Press &#039;set&#039; to chan...]]></description>
                        <content:encoded><![CDATA[<pre contenteditable="false">#include &lt;I2C_RTC.h&gt;

/*
Lee's Clock: Hopefully the final revision. 
In order to use the buttons:
    Hold 'set' to adjust the 'minutes' and 'brightness'.
    Press 'set' to change between twelve and twenty four hour time.
    As of right now the 'hours' button doesn't work so i forced the minutes to cycle to the next hour.
    If you hold any of the first three buttons; it will begin to add faster.
    Set the brightness first and then minutes because holding 'set' pauses the clock.
    Make the minutes one higher than the current time and let go of the 'set' button when the time on the standard you are using hits zero seconds.

I advise against putting any battery on the RTC module as it will consistently overcharge
*/

#include &lt;Wire.h&gt;
#include &lt;RTClib.h&gt; 

DS3231 rtc;

// Shift Register Hardware Pin Definitions
const int reg_clock = 13; // SRCLK
const int reg_latch = 10; // RCLK
const int reg_data  = 11; // SER (Data In)
const int reg_OE    = 9;  // OE (Output Enable - Hardware PWM Control)

// Exact physical pinout matching your custom PCB panel traces
const int btn_bright = 2; // J6 Pin 5 -&gt; Arduino Pin 2
const int btn_min    = 3; // J6 Pin 4 -&gt; Arduino Pin 3 (Minutes Button with Hour Rollover)
const int btn_set    = 5; // J6 Pin 2 -&gt; Arduino Pin 5 (Set Button)

// Common Anode segment patterns (1 = Segment ON / Sink to GND)
const byte digitPatterns[] = {
  0b01111110, // 0
  0b00110000, // 1
  0b01101101, // 2
  0b01111001, // 3
  0b00110011, // 4
  0b01011011, // 5
  0b01011111, // 6
  0b01110000, // 7
  0b01111111, // 8
  0b01111011  // 9
};

// Global Time variables
int hours = 12, minutes = 14, seconds = 0; // Pre-set to 12:14:00 for easy testing
bool is24Hour = false;

// Chime Flags
bool chimeDoneThisMinute = false; 
int lastCheckedMinute = -1;

// Asynchronous Chime Engine State Variables (Zero Delays)
bool introMelodyActive = false;
unsigned long chimeStateTimer = 0;
int strikeCount = 0;
int totalStrikesNeeded = 0;

// Your exact custom active-low brightness PWM levels
const int brightnessPWM[] = {255, 192, 128, 64, 0}; 
int currentBrightStage = 2;

// Non-blocking button and chime tracking state counters
int setHoldCounter = 0;  
int minHoldCounter = 0;  
int rtcRefreshTimer = 0; 
bool setModeActive = false;

// Tracking variables to prevent display lag and electrical noise
int lastDispSec = -1, lastDispMin = -1, lastDispHr = -1;

void updateDisplayRouting() {
  int displayHours = hours;
  bool isPM = (displayHours &gt;= 12);

  if (!is24Hour) {
    displayHours = displayHours % 12;
    if (displayHours == 0) displayHours = 12;
  }
  
  int onesSec = seconds % 10;
  int tensSec = seconds / 10;
  int onesMin = minutes % 10;
  int tensMin = minutes / 10;
  int onesHr  = displayHours % 10;
  int tensHr  = displayHours / 10;
  
  byte seg0 = digitPatterns;
  byte seg1 = digitPatterns;
  byte seg2 = (tensHr == 0) ? 0b00000000 : digitPatterns;
  byte seg3 = digitPatterns;
  byte seg4 = digitPatterns;
  byte seg5 = digitPatterns;
  
  if (setModeActive) {
    seg2 |= 0b10000000; 
    seg3 |= 0b10000000; 
  }
  
  if (!is24Hour &amp;&amp; isPM) {
    seg5 |= 0b10000000; 
  }

  digitalWrite(reg_latch, LOW);
  shiftOut(reg_data, reg_clock, MSBFIRST, seg5); 
  shiftOut(reg_data, reg_clock, MSBFIRST, seg4); 
  shiftOut(reg_data, reg_clock, MSBFIRST, seg3); 
  shiftOut(reg_data, reg_clock, MSBFIRST, seg2); 
  shiftOut(reg_data, reg_clock, MSBFIRST, seg1); 
  shiftOut(reg_data, reg_clock, MSBFIRST, seg0); 
  digitalWrite(reg_latch, HIGH);
}

void readRTC() {
  
  hours = now.hour();
  minutes = now.minute();
  seconds = now.second();
}

// RESTORED: Uses Native Hardware Serial for your pins 0 and 1 connection
void triggerTrack(byte trackNumber) {
  Serial.write((uint8_t)'t'); 
  delayMicroseconds(50); 
  Serial.write((uint8_t)trackNumber); 
}

void checkChimesAsynchronous() {
  if (minutes != lastCheckedMinute) { 
    chimeDoneThisMinute = false; 
    lastCheckedMinute = minutes; 
  }
  
  if (!chimeDoneThisMinute) {
    if (minutes == 15) { 
      triggerTrack(1); 
      chimeDoneThisMinute = true; 
    }
    else if (minutes == 30) { 
      triggerTrack(2); 
      chimeDoneThisMinute = true; 
    }
    else if (minutes == 45) {
      triggerTrack(3);
      chimeDoneThisMinute = true;
    }
    else if (minutes == 0) { 
      triggerTrack(4); // Start hourly intro melody immediately
      introMelodyActive = true;
      chimeStateTimer = millis(); 
      
      totalStrikesNeeded = hours % 12;
      if (totalStrikesNeeded == 0) totalStrikesNeeded = 12;
      strikeCount = 0;
      chimeDoneThisMinute = true; 
    }
  }

  if (introMelodyActive) {
    unsigned long elapsed = millis() - chimeStateTimer;
    
    // Step 1: Wait out the 26-second intro song
    if (strikeCount == 0 &amp;&amp; elapsed &gt;= 26000) {
      triggerTrack(5); // First hour strike
      strikeCount = 1;
      chimeStateTimer = millis(); 
    }
    // Step 2: Cycle through individual hourly strikes every 3 seconds
    else if (strikeCount &gt; 0 &amp;&amp; strikeCount &lt; totalStrikesNeeded &amp;&amp; elapsed &gt;= 3000) {
      triggerTrack(5); // Consecutive hour strike
      strikeCount++;
      chimeStateTimer = millis();
    }
    // Step 3: Turn off tracking engine when total count completes
    else if (strikeCount &gt;= totalStrikesNeeded) {
      introMelodyActive = false;
    }
  }
}

void handleInputsNoMillis() {
  bool setPressed = (digitalRead(btn_set) == LOW);
  bool minPressed = (digitalRead(btn_min) == LOW);
  bool brightPressed = (digitalRead(btn_bright) == LOW);

  // --- SET BUTTON INITIAL PRESS AND MODE TRANSITION ---
  if (setPressed) {
    if (!setModeActive) {
      setHoldCounter++;
      if (setHoldCounter &gt; 30) { // ~600ms hold requirement reached
        setModeActive = true;
      }
    }
    
    if (setModeActive) {
      // 1. Brightness Toggle
      if (brightPressed) {
        currentBrightStage = (currentBrightStage + 1) % 5;
        analogWrite(reg_OE, brightnessPWM);
        delay(250); // Small mechanical debounce
      }

      // 2. Minutes Step with Automatic Hour Rollover
      if (minPressed) {
        minHoldCounter++;
        
        // Advance instantly on first press. If held past 25 frames (~500ms), scroll every 6 frames (~120ms)
        if (minHoldCounter == 1 || (minHoldCounter &gt; 25 &amp;&amp; minHoldCounter % 6 == 0)) {
          seconds = 0; 
          minutes++;
          
          // Rollover minutes into hours smoothly
          if (minutes &gt;= 60) {
            minutes = 0;
            hours++;
            if (hours &gt;= 24) hours = 0;
          }
          updateDisplayRouting();
        }
      } else {
        minHoldCounter = 0; // Reset scroll acceleration instantly on button release
      }
    }
  } 
  else {
    // --- SET BUTTON RELEASE DETECTED ---
    if (setHoldCounter &gt; 0) {
      if (setModeActive) {
        setModeActive = false;
        rtc.adjust(DateTime(2026, 8, 13, hours, minutes, 0)); // Save strictly on release
      } 
      else if (setHoldCounter &gt; 1 &amp;&amp; setHoldCounter &lt;= 30) {
        is24Hour = !is24Hour; // Short tap changes 12/24 mode formatting
      }
      setHoldCounter = 0; 
      minHoldCounter = 0;
      updateDisplayRouting();
    }
  }
}

void setup() {
  pinMode(reg_clock, OUTPUT); pinMode(reg_latch, OUTPUT); pinMode(reg_data, OUTPUT); pinMode(reg_OE, OUTPUT);
  analogWrite(reg_OE, brightnessPWM);
  
  pinMode(btn_bright, INPUT_PULLUP); 
  pinMode(btn_min,    INPUT_PULLUP); 
  pinMode(btn_set,    INPUT_PULLUP);
  
  Wire.begin(); 
  Serial.begin(38400); // Start native Hardware Serial on pins 0/1 at 38400 baud

  if (!rtc.begin()) {
    while (1); 
  }
  
  // TESTING FEATURE: Forces the RTC memory directly to 12:14:00 on every boot/reset 
  // so you can instantly verify the 12:15:00 chime sound!
  rtc.adjust(DateTime(2026, 8, 13, 12, 14, 0));

  readRTC(); 
  updateDisplayRouting();
}

void loop() {
  handleInputsNoMillis();
  
  if (!setModeActive) { 
    checkChimesAsynchronous();
    
    // Query the RTC hardware via I2C once every 10 loops (~200ms)
    rtcRefreshTimer++;
    if (rtcRefreshTimer &gt;= 10) {
      readRTC();
      rtcRefreshTimer = 0;
    }
  }
  
  // Conditional refresh gate blocks high-frequency trace noise from locking up buttons
  if (seconds != lastDispSec || minutes != lastDispMin || hours != lastDispHr || setModeActive) {
    updateDisplayRouting();
    lastDispSec = seconds;
    lastDispMin = minutes;
    lastDispHr  = hours;
  }
  
  delay(20); 
}</pre>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>Lmitchel</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/arduino/ardruino-clock-help-2/</guid>
                    </item>
				                    <item>
                        <title>ESP32 Simulations, where theory and practice meet</title>
                        <link>https://forum.dronebotworkshop.com/electronic-components/esp32-simulations-where-theory-and-practice-meet/</link>
                        <pubDate>Sun, 30 Aug 2026 02:08:59 +0000</pubDate>
                        <description><![CDATA[This post is a side stage of my debut project. The goal of this intermediate step is to get used on how to prepare programs for ESP32 systems or similar. This &quot;apprenticeship&quot; should be poss...]]></description>
                        <content:encoded><![CDATA[<p>You might do a topic on that... I'd certainly be interested.</p>
<p>This post is a side stage of my <a title="I plan to build a &quot;Voyager clone&quot; into an Altoids tin" href="https://forum.dronebotworkshop.com/postid/53103/" target="_blank" rel="noopener">debut project</a>. The goal of this intermediate step is to get used on how to prepare programs for ESP32 systems or similar. This "apprenticeship" should be possible using simulators like those suggested in <a title="Top 3 Free ESP32 Simulators for 2026" href="https://youtu.be/_HsZzkSYao0" target="_blank" rel="noopener">this clip</a>. T<span class="ytAttributedStringLinkInheritColor">hese are:</span></p>
<ul>
<li><span class="ytAttributedStringLinkInheritColor">Wokwi: </span><span class="ytAttributedStringLinkInheritColor"><a class="ytAttributedStringLink ytAttributedStringLinkCallToActionColor" href="https://www.youtube.com/redirect?event=video_description&amp;redir_token=QUM4Zm9rUkI5XzZ3WG5uMFJCZzJsRUhrT2hDWXxBR3JiS2FrMkQzZkdzeFVRUlpxaFFXVnFWYTc5QXM4dFlKTUxFbkp5U1FEYi10dDdfcjU0amZQeG1uRUd4Mm5DNHYtVi1XVTI0ZHVNV2F6VnJmeDFtSFNBRlg1UkJKM21udi1Q&amp;q=https%3A%2F%2Fwokwi.com%2F&amp;v=_HsZzkSYao0" target="_blank" rel="nofollow noopener">https://wokwi.com/</a><br />Its pricing plan for 0,- monthly is, to generate a personal 30-day license for Wokwi VS Code. So I should change my identity every month to continue testing?<br /><br /></span><span class="ytAttributedStringLinkInheritColor"></span></li>
<li><span class="ytAttributedStringLinkInheritColor">Cirkit Designer: </span><span class="ytAttributedStringLinkInheritColor"><a class="ytAttributedStringLink ytAttributedStringLinkCallToActionColor" href="https://www.youtube.com/redirect?event=video_description&amp;redir_token=QUM4Zm9rVDRMVjE4elM1ZlE0NTZLMC0xTjdRSHxBR3JiS2FtMjhweVh1N3BISEMwQjhPS2JuY05iN2dBRnpDSjgtQlBMVHJNeFluS1p6VjdmckNGNXRBVEI1YV90cUFxZ1lmVUJUc2hNWkh5RkdGTnExSUl5MW1HMXdmQjUxZ2VL&amp;q=https%3A%2F%2Fwww.cirkitstudio.com%2F&amp;v=_HsZzkSYao0" target="_blank" rel="nofollow noopener">https://www.cirkitstudio.com/</a><br />"Cirkit AI designs circuits, writes firmware, debugs projects, and researches solutions" (end of quote). So 'Cirkit' is primarily an AI front end? That could work due to the restricted focus, nevertheless I'm reluctant regarding AI, take me for too prudent -- so what.<br /><br /></span></li>
<li><span class="ytAttributedStringLinkInheritColor">Velxio: </span><span class="ytAttributedStringLinkInheritColor"><a class="ytAttributedStringLink ytAttributedStringLinkCallToActionColor" href="https://www.youtube.com/redirect?event=video_description&amp;redir_token=QUM4Zm9rVFVVWHQ2bjZrcjFIMnVyZ1Z1OHhpVnxBR3JiS2FucmNiWEpobkdpSndHUkNXMkk5MXNvQjN5UExIYWE2dzkxOEVKUDdwLVZBSmo4d0FGQ1F6RzJsLXdtdUJaYW16UGFQNXY4R0Y2NkRZejJNQlpIaXk4YVpJUXBwOENf&amp;q=https%3A%2F%2Fvelxio.dev%2F&amp;v=_HsZzkSYao0" target="_blank" rel="nofollow noopener">https://velxio.dev/</a><br />Near the bottom of its 'Pricing' section some <em>common questions</em> are answered. One of them is "Can I self-host Velxio for free? -- Yes." Plus a link to <a title="Velxio: Arduino &amp; Embedded Board Emulator" href="https://github.com/davidmonterocrespo24/velxio" target="_blank" rel="noopener">GitHub</a>. In contrast to the '.msi Download for Windows' with the hint "30-day free trial" I didn't find (yet) a similar restriction on GitHub.</span></li>
</ul>
<p>Since it's not a 'no installation, runs in your browser'-thing a. m. clip does not mention the <a title="QEMU Emulator" href="https://documentation.espressif.com/projects/esp-idf/en/v5.4.2/esp32/api-guides/tools/qemu.html#qemu-emulator" target="_blank" rel="noopener">simulation offered by Espressif</a>. Alas, it's a bit puzzling, because one description lists <a href="https://github.com/espressif/esp-toolchain-docs/blob/main/qemu/README.md#choose-your-target" target="_blank" rel="noopener">considerable restrictions</a>, while <a href="https://github.com/espressif/qemu#qemu-readme" target="_blank" rel="noopener">another reference</a> does not show similar limitations (or I've overlooked it).</p>
<p>I'll give a try at Espressif's QEMU and Velxio. As first experiment I take <a title="HP41C/CV/CX Emulator" href="https://hp.giesselink.com/v41.htm" target="_blank" rel="noopener">HP41Kernel.cpp of V41</a> (what emulates an HP-41 but the Voyager's CPU is functionally the same) and interpret with it the f/w of an HP11C (see attached ROM copy). A trace log (if possible) should be the same as with another "emulator", at least it should end quite qick with the simulated CPU clocks stopped and virtual display on. (I will run this test w/o display, I'll check only the display's on/off bit.)</p>
<p>Don't expect me to report success tomorrow, it will take several weeks I guess. Until then, don't hesitate to post expedient comments.</p>
10940]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>Amphitryonoff</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/electronic-components/esp32-simulations-where-theory-and-practice-meet/</guid>
                    </item>
				                    <item>
                        <title>Ardruino Clock code help</title>
                        <link>https://forum.dronebotworkshop.com/arduino/ardruino-clock-code-help/</link>
                        <pubDate>Sun, 30 Aug 2026 01:22:26 +0000</pubDate>
                        <description><![CDATA[First, I’m a 74 year old senior doing my first Ardruino project. I did the hardware for my wife’s clock, but not the code. A college student offered to help me. In exchange I’m helping him b...]]></description>
                        <content:encoded><![CDATA[<p>First, I’m a 74 year old senior doing my first Ardruino project. I did the hardware for my wife’s clock, but not the code. A college student offered to help me. In exchange I’m helping him build a Citation II pcb upgrade I have designed and built. So, the clock design is running with the code he provided. The clock is a normal LED 7 segment clock , with Westminster chime running off an mp3 player.</p>
<p>The only problem is the hour set switch doesn’t function. All other function work fine. I would also like to add if possible an TSOP 1838 IR sensor remote to set the clock beside switches, beside the sitwches.</p>
<p>if anyone can help me I would be greatly appreciated. Oh, James is back at college, junior year and can’t help me.</p>
<p>&nbsp;</p>
<p>Here is my code for reference</p>
<p>&nbsp;</p>
<p>#include &lt;I2C_RTC.h&gt;<br /><br />/*<br />Lee's Clock: Hopefully the final revision. <br />In order to use the buttons:<br />Hold 'set' to adjust the 'minutes' and 'brightness'.<br />Press 'set' to change between twelve and twenty four hour time.<br />As of right now the 'hours' button doesn't work so i forced the minutes to cycle to the next hour.<br />If you hold any of the first three buttons; it will begin to add faster.<br />Set the brightness first and then minutes because holding 'set' pauses the clock.<br />Make the minutes one higher than the current time and let go of the 'set' button when the time on the standard you are using hits zero seconds.<br /><br />I advise against putting any battery on the RTC module as it will consistently overcharge<br />*/<br /><br />#include &lt;Wire.h&gt;<br />#include &lt;RTClib.h&gt; <br /><br />DS3231 rtc;<br /><br />// Shift Register Hardware Pin Definitions<br />const int reg_clock = 13; // SRCLK<br />const int reg_latch = 10; // RCLK<br />const int reg_data = 11; // SER (Data In)<br />const int reg_OE = 9; // OE (Output Enable - Hardware PWM Control)<br /><br />// Exact physical pinout matching your custom PCB panel traces<br />const int btn_bright = 2; // J6 Pin 5 -&gt; Arduino Pin 2<br />const int btn_min = 3; // J6 Pin 4 -&gt; Arduino Pin 3 (Minutes Button with Hour Rollover)<br />const int btn_set = 5; // J6 Pin 2 -&gt; Arduino Pin 5 (Set Button)<br /><br />// Common Anode segment patterns (1 = Segment ON / Sink to GND)<br />const byte digitPatterns[] = {<br />0b01111110, // 0<br />0b00110000, // 1<br />0b01101101, // 2<br />0b01111001, // 3<br />0b00110011, // 4<br />0b01011011, // 5<br />0b01011111, // 6<br />0b01110000, // 7<br />0b01111111, // 8<br />0b01111011 // 9<br />};<br /><br />// Global Time variables<br />int hours = 12, minutes = 14, seconds = 0; // Pre-set to 12:14:00 for easy testing<br />bool is24Hour = false;<br /><br />// Chime Flags<br />bool chimeDoneThisMinute = false; <br />int lastCheckedMinute = -1;<br /><br />// Asynchronous Chime Engine State Variables (Zero Delays)<br />bool introMelodyActive = false;<br />unsigned long chimeStateTimer = 0;<br />int strikeCount = 0;<br />int totalStrikesNeeded = 0;<br /><br />// Your exact custom active-low brightness PWM levels<br />const int brightnessPWM[] = {255, 192, 128, 64, 0}; <br />int currentBrightStage = 2;<br /><br />// Non-blocking button and chime tracking state counters<br />int setHoldCounter = 0; <br />int minHoldCounter = 0; <br />int rtcRefreshTimer = 0; <br />bool setModeActive = false;<br /><br />// Tracking variables to prevent display lag and electrical noise<br />int lastDispSec = -1, lastDispMin = -1, lastDispHr = -1;<br /><br />void updateDisplayRouting() {<br />int displayHours = hours;<br />bool isPM = (displayHours &gt;= 12);<br /><br />if (!is24Hour) {<br />displayHours = displayHours % 12;<br />if (displayHours == 0) displayHours = 12;<br />}<br /><br />int onesSec = seconds % 10;<br />int tensSec = seconds / 10;<br />int onesMin = minutes % 10;<br />int tensMin = minutes / 10;<br />int onesHr = displayHours % 10;<br />int tensHr = displayHours / 10;<br /><br />byte seg0 = digitPatterns;<br />byte seg1 = digitPatterns;<br />byte seg2 = (tensHr == 0) ? 0b00000000 : digitPatterns;<br />byte seg3 = digitPatterns;<br />byte seg4 = digitPatterns;<br />byte seg5 = digitPatterns;<br /><br />if (setModeActive) {<br />seg2 |= 0b10000000; <br />seg3 |= 0b10000000; <br />}<br /><br />if (!is24Hour &amp;&amp; isPM) {<br />seg5 |= 0b10000000; <br />}<br /><br />digitalWrite(reg_latch, LOW);<br />shiftOut(reg_data, reg_clock, MSBFIRST, seg5); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg4); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg3); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg2); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg1); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg0); <br />digitalWrite(reg_latch, HIGH);<br />}<br /><br />void readRTC() {<br /><br />hours = now.hour();<br />minutes = now.minute();<br />seconds = now.second();<br />}<br /><br />// RESTORED: Uses Native Hardware Serial for your pins 0 and 1 connection<br />void triggerTrack(byte trackNumber) {<br />Serial.write((uint8_t)'t'); <br />delayMicroseconds(50); <br />Serial.write((uint8_t)trackNumber); <br />}<br /><br />void checkChimesAsynchronous() {<br />if (minutes != lastCheckedMinute) { <br />chimeDoneThisMinute = false; <br />lastCheckedMinute = minutes; <br />}<br /><br />if (!chimeDoneThisMinute) {<br />if (minutes == 15) { <br />triggerTrack(1); <br />chimeDoneThisMinute = true; <br />}<br />else if (minutes == 30) { <br />triggerTrack(2); <br />chimeDoneThisMinute = true; <br />}<br />else if (minutes == 45) {<br />triggerTrack(3);<br />chimeDoneThisMinute = true;<br />}<br />else if (minutes == 0) { <br />triggerTrack(4); // Start hourly intro melody immediately<br />introMelodyActive = true;<br />chimeStateTimer = millis(); <br /><br />totalStrikesNeeded = hours % 12;<br />if (totalStrikesNeeded == 0) totalStrikesNeeded = 12;<br />strikeCount = 0;<br />chimeDoneThisMinute = true; <br />}<br />}<br /><br />if (introMelodyActive) {<br />unsigned long elapsed = millis() - chimeStateTimer;<br /><br />// Step 1: Wait out the 26-second intro song<br />if (strikeCount == 0 &amp;&amp; elapsed &gt;= 26000) {<br />triggerTrack(5); // First hour strike<br />strikeCount = 1;<br />chimeStateTimer = millis(); <br />}<br />// Step 2: Cycle through individual hourly strikes every 3 seconds<br />else if (strikeCount &gt; 0 &amp;&amp; strikeCount &lt; totalStrikesNeeded &amp;&amp; elapsed &gt;= 3000) {<br />triggerTrack(5); // Consecutive hour strike<br />strikeCount++;<br />chimeStateTimer = millis();<br />}<br />// Step 3: Turn off tracking engine when total count completes<br />else if (strikeCount &gt;= totalStrikesNeeded) {<br />introMelodyActive = false;<br />}<br />}<br />}<br /><br />void handleInputsNoMillis() {<br />bool setPressed = (digitalRead(btn_set) == LOW);<br />bool minPressed = (digitalRead(btn_min) == LOW);<br />bool brightPressed = (digitalRead(btn_bright) == LOW);<br /><br />// --- SET BUTTON INITIAL PRESS AND MODE TRANSITION ---<br />if (setPressed) {<br />if (!setModeActive) {<br />setHoldCounter++;<br />if (setHoldCounter &gt; 30) { // ~600ms hold requirement reached<br />setModeActive = true;<br />}<br />}<br /><br />if (setModeActive) {<br />// 1. Brightness Toggle<br />if (brightPressed) {<br />currentBrightStage = (currentBrightStage + 1) % 5;<br />analogWrite(reg_OE, brightnessPWM);<br />delay(250); // Small mechanical debounce<br />}<br /><br />// 2. Minutes Step with Automatic Hour Rollover<br />if (minPressed) {<br />minHoldCounter++;<br /><br />// Advance instantly on first press. If held past 25 frames (~500ms), scroll every 6 frames (~120ms)<br />if (minHoldCounter == 1 || (minHoldCounter &gt; 25 &amp;&amp; minHoldCounter % 6 == 0)) {<br />seconds = 0; <br />minutes++;<br /><br />// Rollover minutes into hours smoothly<br />if (minutes &gt;= 60) {<br />minutes = 0;<br />hours++;<br />if (hours &gt;= 24) hours = 0;<br />}<br />updateDisplayRouting();<br />}<br />} else {<br />minHoldCounter = 0; // Reset scroll acceleration instantly on button release<br />}<br />}<br />} <br />else {<br />// --- SET BUTTON RELEASE DETECTED ---<br />if (setHoldCounter &gt; 0) {<br />if (setModeActive) {<br />setModeActive = false;<br />rtc.adjust(DateTime(2026, 8, 13, hours, minutes, 0)); // Save strictly on release<br />} <br />else if (setHoldCounter &gt; 1 &amp;&amp; setHoldCounter &lt;= 30) {<br />is24Hour = !is24Hour; // Short tap changes 12/24 mode formatting<br />}<br />setHoldCounter = 0; <br />minHoldCounter = 0;<br />updateDisplayRouting();<br />}<br />}<br />}<br /><br />void setup() {<br />pinMode(reg_clock, OUTPUT); pinMode(reg_latch, OUTPUT); pinMode(reg_data, OUTPUT); pinMode(reg_OE, OUTPUT);<br />analogWrite(reg_OE, brightnessPWM);<br /><br />pinMode(btn_bright, INPUT_PULLUP); <br />pinMode(btn_min, INPUT_PULLUP); <br />pinMode(btn_set, INPUT_PULLUP);<br /><br />Wire.begin(); <br />Serial.begin(38400); // Start native Hardware Serial on pins 0/1 at 38400 baud<br /><br />if (!rtc.begin()) {<br />while (1); <br />}<br /><br />// TESTING FEATURE: Forces the RTC memory directly to 12:14:00 on every boot/reset <br />// so you can instantly verify the 12:15:00 chime sound!<br />rtc.adjust(DateTime(2026, 8, 13, 12, 14, 0));<br /><br />readRTC(); <br />updateDisplayRouting();<br />}<br /><br />void loop() {<br />handleInputsNoMillis();<br /><br />if (!setModeActive) { <br />checkChimesAsynchronous();<br /><br />// Query the RTC hardware via I2C once every 10 loops (~200ms)<br />rtcRefreshTimer++;<br />if (rtcRefreshTimer &gt;= 10) {<br />readRTC();<br />rtcRefreshTimer = 0;<br />}<br />}<br /><br />// Conditional refresh gate blocks high-frequency trace noise from locking up buttons<br />if (seconds != lastDispSec || minutes != lastDispMin || hours != lastDispHr || setModeActive) {<br />updateDisplayRouting();<br />lastDispSec = seconds;<br />lastDispMin = minutes;<br />lastDispHr = hours;<br />}<br /><br />delay(20); <br />}#include &lt;I2C_RTC.h&gt;<br /><br />/*<br />Lee's Clock: Hopefully the final revision. <br />In order to use the buttons:<br />Hold 'set' to adjust the 'minutes' and 'brightness'.<br />Press 'set' to change between twelve and twenty four hour time.<br />As of right now the 'hours' button doesn't work so i forced the minutes to cycle to the next hour.<br />If you hold any of the first three buttons; it will begin to add faster.<br />Set the brightness first and then minutes because holding 'set' pauses the clock.<br />Make the minutes one higher than the current time and let go of the 'set' button when the time on the standard you are using hits zero seconds.<br /><br />I advise against putting any battery on the RTC module as it will consistently overcharge<br />*/<br /><br />#include &lt;Wire.h&gt;<br />#include &lt;RTClib.h&gt; <br /><br />DS3231 rtc;<br /><br />// Shift Register Hardware Pin Definitions<br />const int reg_clock = 13; // SRCLK<br />const int reg_latch = 10; // RCLK<br />const int reg_data = 11; // SER (Data In)<br />const int reg_OE = 9; // OE (Output Enable - Hardware PWM Control)<br /><br />// Exact physical pinout matching your custom PCB panel traces<br />const int btn_bright = 2; // J6 Pin 5 -&gt; Arduino Pin 2<br />const int btn_min = 3; // J6 Pin 4 -&gt; Arduino Pin 3 (Minutes Button with Hour Rollover)<br />const int btn_set = 5; // J6 Pin 2 -&gt; Arduino Pin 5 (Set Button)<br /><br />// Common Anode segment patterns (1 = Segment ON / Sink to GND)<br />const byte digitPatterns[] = {<br />0b01111110, // 0<br />0b00110000, // 1<br />0b01101101, // 2<br />0b01111001, // 3<br />0b00110011, // 4<br />0b01011011, // 5<br />0b01011111, // 6<br />0b01110000, // 7<br />0b01111111, // 8<br />0b01111011 // 9<br />};<br /><br />// Global Time variables<br />int hours = 12, minutes = 14, seconds = 0; // Pre-set to 12:14:00 for easy testing<br />bool is24Hour = false;<br /><br />// Chime Flags<br />bool chimeDoneThisMinute = false; <br />int lastCheckedMinute = -1;<br /><br />// Asynchronous Chime Engine State Variables (Zero Delays)<br />bool introMelodyActive = false;<br />unsigned long chimeStateTimer = 0;<br />int strikeCount = 0;<br />int totalStrikesNeeded = 0;<br /><br />// Your exact custom active-low brightness PWM levels<br />const int brightnessPWM[] = {255, 192, 128, 64, 0}; <br />int currentBrightStage = 2;<br /><br />// Non-blocking button and chime tracking state counters<br />int setHoldCounter = 0; <br />int minHoldCounter = 0; <br />int rtcRefreshTimer = 0; <br />bool setModeActive = false;<br /><br />// Tracking variables to prevent display lag and electrical noise<br />int lastDispSec = -1, lastDispMin = -1, lastDispHr = -1;<br /><br />void updateDisplayRouting() {<br />int displayHours = hours;<br />bool isPM = (displayHours &gt;= 12);<br /><br />if (!is24Hour) {<br />displayHours = displayHours % 12;<br />if (displayHours == 0) displayHours = 12;<br />}<br /><br />int onesSec = seconds % 10;<br />int tensSec = seconds / 10;<br />int onesMin = minutes % 10;<br />int tensMin = minutes / 10;<br />int onesHr = displayHours % 10;<br />int tensHr = displayHours / 10;<br /><br />byte seg0 = digitPatterns;<br />byte seg1 = digitPatterns;<br />byte seg2 = (tensHr == 0) ? 0b00000000 : digitPatterns;<br />byte seg3 = digitPatterns;<br />byte seg4 = digitPatterns;<br />byte seg5 = digitPatterns;<br /><br />if (setModeActive) {<br />seg2 |= 0b10000000; <br />seg3 |= 0b10000000; <br />}<br /><br />if (!is24Hour &amp;&amp; isPM) {<br />seg5 |= 0b10000000; <br />}<br /><br />digitalWrite(reg_latch, LOW);<br />shiftOut(reg_data, reg_clock, MSBFIRST, seg5); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg4); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg3); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg2); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg1); <br />shiftOut(reg_data, reg_clock, MSBFIRST, seg0); <br />digitalWrite(reg_latch, HIGH);<br />}<br /><br />void readRTC() {<br /><br />hours = now.hour();<br />minutes = now.minute();<br />seconds = now.second();<br />}<br /><br />// RESTORED: Uses Native Hardware Serial for your pins 0 and 1 connection<br />void triggerTrack(byte trackNumber) {<br />Serial.write((uint8_t)'t'); <br />delayMicroseconds(50); <br />Serial.write((uint8_t)trackNumber); <br />}<br /><br />void checkChimesAsynchronous() {<br />if (minutes != lastCheckedMinute) { <br />chimeDoneThisMinute = false; <br />lastCheckedMinute = minutes; <br />}<br /><br />if (!chimeDoneThisMinute) {<br />if (minutes == 15) { <br />triggerTrack(1); <br />chimeDoneThisMinute = true; <br />}<br />else if (minutes == 30) { <br />triggerTrack(2); <br />chimeDoneThisMinute = true; <br />}<br />else if (minutes == 45) {<br />triggerTrack(3);<br />chimeDoneThisMinute = true;<br />}<br />else if (minutes == 0) { <br />triggerTrack(4); // Start hourly intro melody immediately<br />introMelodyActive = true;<br />chimeStateTimer = millis(); <br /><br />totalStrikesNeeded = hours % 12;<br />if (totalStrikesNeeded == 0) totalStrikesNeeded = 12;<br />strikeCount = 0;<br />chimeDoneThisMinute = true; <br />}<br />}<br /><br />if (introMelodyActive) {<br />unsigned long elapsed = millis() - chimeStateTimer;<br /><br />// Step 1: Wait out the 26-second intro song<br />if (strikeCount == 0 &amp;&amp; elapsed &gt;= 26000) {<br />triggerTrack(5); // First hour strike<br />strikeCount = 1;<br />chimeStateTimer = millis(); <br />}<br />// Step 2: Cycle through individual hourly strikes every 3 seconds<br />else if (strikeCount &gt; 0 &amp;&amp; strikeCount &lt; totalStrikesNeeded &amp;&amp; elapsed &gt;= 3000) {<br />triggerTrack(5); // Consecutive hour strike<br />strikeCount++;<br />chimeStateTimer = millis();<br />}<br />// Step 3: Turn off tracking engine when total count completes<br />else if (strikeCount &gt;= totalStrikesNeeded) {<br />introMelodyActive = false;<br />}<br />}<br />}<br /><br />void handleInputsNoMillis() {<br />bool setPressed = (digitalRead(btn_set) == LOW);<br />bool minPressed = (digitalRead(btn_min) == LOW);<br />bool brightPressed = (digitalRead(btn_bright) == LOW);<br /><br />// --- SET BUTTON INITIAL PRESS AND MODE TRANSITION ---<br />if (setPressed) {<br />if (!setModeActive) {<br />setHoldCounter++;<br />if (setHoldCounter &gt; 30) { // ~600ms hold requirement reached<br />setModeActive = true;<br />}<br />}<br /><br />if (setModeActive) {<br />// 1. Brightness Toggle<br />if (brightPressed) {<br />currentBrightStage = (currentBrightStage + 1) % 5;<br />analogWrite(reg_OE, brightnessPWM);<br />delay(250); // Small mechanical debounce<br />}<br /><br />// 2. Minutes Step with Automatic Hour Rollover<br />if (minPressed) {<br />minHoldCounter++;<br /><br />// Advance instantly on first press. If held past 25 frames (~500ms), scroll every 6 frames (~120ms)<br />if (minHoldCounter == 1 || (minHoldCounter &gt; 25 &amp;&amp; minHoldCounter % 6 == 0)) {<br />seconds = 0; <br />minutes++;<br /><br />// Rollover minutes into hours smoothly<br />if (minutes &gt;= 60) {<br />minutes = 0;<br />hours++;<br />if (hours &gt;= 24) hours = 0;<br />}<br />updateDisplayRouting();<br />}<br />} else {<br />minHoldCounter = 0; // Reset scroll acceleration instantly on button release<br />}<br />}<br />} <br />else {<br />// --- SET BUTTON RELEASE DETECTED ---<br />if (setHoldCounter &gt; 0) {<br />if (setModeActive) {<br />setModeActive = false;<br />rtc.adjust(DateTime(2026, 8, 13, hours, minutes, 0)); // Save strictly on release<br />} <br />else if (setHoldCounter &gt; 1 &amp;&amp; setHoldCounter &lt;= 30) {<br />is24Hour = !is24Hour; // Short tap changes 12/24 mode formatting<br />}<br />setHoldCounter = 0; <br />minHoldCounter = 0;<br />updateDisplayRouting();<br />}<br />}<br />}<br /><br />void setup() {<br />pinMode(reg_clock, OUTPUT); pinMode(reg_latch, OUTPUT); pinMode(reg_data, OUTPUT); pinMode(reg_OE, OUTPUT);<br />analogWrite(reg_OE, brightnessPWM);<br /><br />pinMode(btn_bright, INPUT_PULLUP); <br />pinMode(btn_min, INPUT_PULLUP); <br />pinMode(btn_set, INPUT_PULLUP);<br /><br />Wire.begin(); <br />Serial.begin(38400); // Start native Hardware Serial on pins 0/1 at 38400 baud<br /><br />if (!rtc.begin()) {<br />while (1); <br />}<br /><br />// TESTING FEATURE: Forces the RTC memory directly to 12:14:00 on every boot/reset <br />// so you can instantly verify the 12:15:00 chime sound!<br />rtc.adjust(DateTime(2026, 8, 13, 12, 14, 0));<br /><br />readRTC(); <br />updateDisplayRouting();<br />}<br /><br />void loop() {<br />handleInputsNoMillis();<br /><br />if (!setModeActive) { <br />checkChimesAsynchronous();<br /><br />// Query the RTC hardware via I2C once every 10 loops (~200ms)<br />rtcRefreshTimer++;<br />if (rtcRefreshTimer &gt;= 10) {<br />readRTC();<br />rtcRefreshTimer = 0;<br />}<br />}<br /><br />// Conditional refresh gate blocks high-frequency trace noise from locking up buttons<br />if (seconds != lastDispSec || minutes != lastDispMin || hours != lastDispHr || setModeActive) {<br />updateDisplayRouting();<br />lastDispSec = seconds;<br />lastDispMin = minutes;<br />lastDispHr = hours;<br />}<br /><br />delay(20); <br />}</p>
<p>&nbsp;</p>
<p>Thanks for looking at my code and helping me.</p>
<p>&nbsp;</p>
<p>lee Mitchell</p>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>Lmitchel</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/arduino/ardruino-clock-code-help/</guid>
                    </item>
				                    <item>
                        <title>HVAC System Monitor</title>
                        <link>https://forum.dronebotworkshop.com/show-tell/hvac-system-monitor/</link>
                        <pubDate>Thu, 27 Aug 2026 06:47:06 +0000</pubDate>
                        <description><![CDATA[HVAC System Monitor — Now on GitHubJust published the repo for my three-node HVAC/environmental monitoring system:HVAC System MonitorThe system tracks furnace blower cycles and outdoor condi...]]></description>
                        <content:encoded><![CDATA[<p><span>HVAC System Monitor — Now on GitHub</span><br /><span>Just published the repo for my three-node HVAC/environmental monitoring system:</span><br /><br /><a class="postlink" href="https://github.com/Tech500/HVAC-System-Monitor">HVAC System Monitor</a><br /><br /><span>The system tracks furnace blower cycles and outdoor conditions using a mix of LoRa (Wake-on-Radio) and ESP-NOW:</span><br /><br /><span>Outside node – EoRa-S3-900TB (ESP32-S3 + SX1262) in a Stevenson screen, running a register-level SX1262 WOR implementation for ultra-low-power deep sleep</span><br /><br /><span>Blower node – ESP32-S3 Super Mini + MPU-6050 mounted on the furnace blower housing, detecting run cycles via vibration</span><br /><br /><span>Hub – EoRa-S3-900TB, mains-powered, aggregating data and posting to Google Sheets</span><br /><br /><span>Power profiling with a Nordic PPK2 shows a RxDutyCycle average of ~94 µA on the outside node, supporting long battery life on a single 3000 mAh LiPo.</span><br /><br /><span>Higher current due to RxDutyCycle Mode of the SX1262.</span><br /><br /><span>Best Regards,</span><br /><span>William</span></p>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>AB9NQ-William</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/show-tell/hvac-system-monitor/</guid>
                    </item>
				                    <item>
                        <title>Hello All</title>
                        <link>https://forum.dronebotworkshop.com/introductions/hello-all-10/</link>
                        <pubDate>Tue, 25 Aug 2026 01:38:56 +0000</pubDate>
                        <description><![CDATA[Hi I&#039;m Sel from the UK, I&#039;ve been  intrested in coding and electronics for a long time but I&#039;m just not very good at it. I&#039;ll have ago at trying to fix stuff even if I have no clue.
Right n...]]></description>
                        <content:encoded><![CDATA[<p>Hi I'm Sel from the UK, I've been  intrested in coding and electronics for a long time but I'm just not very good at it. I'll have ago at trying to fix stuff even if I have no clue.</p>
<p>Right now I'm trying to find a way to program an arduino to control at TV by faking the button presses behind the TV as the remote receiver circuit seems to have died.</p>
<p>Can I ask where would be the best place to ask questions about this?</p>
<p>&nbsp;</p>
<p>Thanks Sel.</p>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>F2Ksel</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/introductions/hello-all-10/</guid>
                    </item>
				                    <item>
                        <title>Comparing LSM6DSV vs MPU6050 Gyroscope/Accelerometers</title>
                        <link>https://forum.dronebotworkshop.com/electronic-components/comparing-lsm6dsv-vs-mpu6050-gyroscope-accelerometers/</link>
                        <pubDate>Sun, 23 Aug 2026 14:07:44 +0000</pubDate>
                        <description><![CDATA[During my brainstorming with AI about my self-balancing robotand my desire to reduce or eliminate the wobbly behavior most of these type bots seem to have.  AI was adamant that the MPU6050 w...]]></description>
                        <content:encoded><![CDATA[<div data-olk-copy-source="MailCompose">During my brainstorming with AI about my self-balancing robot <a id="OWAaf95d247-e3aa-35f7-3e1a-658285296311" class="OWAAutoLink" href="https://forum.dronebotworkshop.com/user-robot-projects/inqster-the-re-awakening" target="_blank" rel="noopener">https://forum.dronebotworkshop.com/user-robot-projects/inqster-the-re-awakening</a>, and my desire to reduce or eliminate the wobbly behavior most of these type bots seem to have.  AI was adamant that the MPU6050 was... well... deficient.  After looking over the ones it suggested, I finally picked up the LSM6DSV.  It's more expensive, but still in the range that I was willing to drop.  To see if AI was giving me the strait scoop, and see if it was worth the price, I've decided to do some benchmarks.  Here are my results.</div>
<div> </div>
<div><b>Test Rig</b></div>
<ul data-editing-info="{&quot;applyListStyleFromLevel&quot;:true}">
<li>
<div role="presentation">ESP32-S3.  All tests are run entirely in Core 1 either in the loop() method or interrupts also in Core 1. </div>
</li>
<li>
<div role="presentation">MPU6050 is using I2C running at 400kHz.  This is the fastest speed recommended for I2C on it.  </div>
</li>
<li>
<div role="presentation">LSM6DSV can be used with either I2C or SPI.  To get its max rate, SPI was used at 1MHz.  This is well below the maximum rate of SPI.</div>
</li>
<li>
<div role="presentation">Both sensors are on the same breadboard but are configured to be used either independently or together depending on the test.</div>
</li>
<li>
<div role="presentation">I wrote both software drivers to use minimalist (fastest) access to retrieve raw data and to know exactly what is in the code versus using Arduino hosted libraries.</div>
</li>
</ul>
<div>
10931
</div>
<div>        </div>
<div><b>Results</b></div>
<div>I'll include the sketch below which includes the raw data of the three tests I performed.  That way if you wish to dive deeper into the madness, you're welcome to.  Here, I'll just present my summary and interpretation of the results.</div>
<div> </div>
<div><b>TEST1 - Raw Speed</b></div>
<div>This tests the maximum rates that we can use the sensors.  The sensors are tested separately on different uploads of the ESP32-S3.  <b>In all tests, the LSM6DSV showed the approximate 4x speed expected simply because of using SPI over I2C of the MPU6050.</b></div>
<div> </div>
<ol start="1" data-editing-info="{&quot;applyListStyleFromLevel&quot;:false,&quot;orderedStyleType&quot;:11}">
<li>
<div role="presentation">In this first sub test, the ESP32-S3 was not asked to do anything with the data.    The MPU6050 had the higher rate of sampling <b>IN</b> the IMU at just over 8 kHz (124 ns/sample).  The LSM6DSV was not that far behind at just over 7.5 kHz (132 ns/sample).  Although higher, the ability to retrieve this data is significantly hampered in the MPU6050 by its use of I2C.  </div>
</li>
</ol>
<div> </div>
<div style="text-align: center"><b>LSM6DSV 7.5 kHz  vs  MPU6050 2.1 kHz</b></div>
<div> </div>
<ol start="2" data-editing-info="{&quot;applyListStyleFromLevel&quot;:false,&quot;orderedStyleType&quot;:11}">
<li>
<div role="presentation">In the second test, the ESP32-S3 also processed the data as if it will be run in an <a id="OWA07c01108-c32e-ce47-4558-81481fc715ab" class="OWAAutoLink" title="https://forum.dronebotworkshop.com/postid/53092/" href="https://forum.dronebotworkshop.com/postid/53092/">Inference</a> for the robot.  I tested it at two different sizes.  The first I expect to be somewhat larger than I'll need for the self-balancing robot, and the second one is 25x larger than I expect to ever need.  With the smaller use case, the LSM6DSV still had extra available CPU time to process the Inference.  The MPU6050 lost about 1.5%.  With the larger use case, they lost 29% and 24% repectively.</div>
</li>
</ol>
<div> </div>
<div style="text-align: center"><b>Small Inference:  LSM6DSV 7.5 kHz  vs  MPU6050 2.1 kHz</b></div>
<div style="text-align: center"><b>Large Inference:  LSM6DSV 5.33 kHz  vs  MPU6050 1.58 kHz</b></div>
<div> </div>
<div><b>TEST2 - Noise</b></div>
<div>This tests each sensor independently.  They are tested at their maximum rate (7.5 kHz, 2.1 kHz respectively) and stores the output in an array of 150,000 samples.  This summary is for just the data coming out for the acceleration in the Z direction (gravity) of it at rest on the desk.  The 2g scale is used.  Once accumulated, it then does an averaging and standard deviation on the data.  The LSM6DSV has less than 1% of the noise of the MPU6050.</div>
<div>
10932
</div>
<div>    </div>
<div> </div>
<div><b>TEST3 - Drift</b></div>
<div>In the use case of a self-balancing robot, the gyroscope data has to be summed over time to estimate whether the robot is tilting over.  Supposedly (marketing) it changes with temperature to a greater degree with the MPU6050.  The test takes a sample once a second from both sensors and outputs to the Serial monitor.  I ran the test over night starting with the test rig having sat turned off to settle to room temperature before powering up and running the test.  I do not see the drift or temperature dependency that was touted.  The trends over time seem to be the same.  Which indicates a simple offset could be used to normalize the data for drift.  The drastic noise variation is noticeable that was in test2.</div>
<div> </div>
<div>
10933
</div>
<p>Only you can judge for your use case which sensor is good enough for your project.  Although a lot faster, and a lot less noisy, it still indeterminant whether the LSM6DSV will result in noticeably better performance in the self-balancing robot project.</p>
<p>&nbsp;</p>
<p><em>Inq</em></p>
<p>&nbsp;</p>
<p><strong>Program</strong> is pretty fugly coding due it being just a test app with multiple independently coded tests, but it also contain comments of the raw result for those as anal retentive as Inq.</p>
<pre contenteditable="false">// To run tests set test to 1.  Only do ONE AT A TIME!

#define TEST1 0	// RAW SPEED
// To get maximum rates that we can use the sensors.
// * USE_LSM6DSV (or) USE_MPU6050 set to 1.  You can
//   do both, but they slow things down for both.
// * Use WITH_INFERENCE 1 to have some processing
// 	 of the data (simulates ANN Inference) for 
//   self-balancing robot, etc.

#define TEST2 0 // NOISE
// * USE_LSM6DSV (or) USE_MPU6050 set to 1.  
//   Don't do both, data is mis-match.
// * Use WITH_INFERENCE 0 to get highest rate.
// * Set VAL to component to evaluate 
//	 (aX, aY, aZ, gX, gY, gZ)

#define TEST3 1 // DRIFT
// As the chips warm up they will supposedly drift
// * USE_LSM6DSV (and) USE_MPU6050 set to 1.  
// * Use WITH_INFERENCE 0 to get highest rate.

#define USE_LSM6DSV	1
#define USE_MPU6050 1

#define WITH_INFERENCE 0

#define INFERENCE_SIZE 50
#define VAL aZ

#include &lt;Arduino.h&gt;
#include &lt;InqLSM6DSV.h&gt;
#include &lt;InqMPU6050.h&gt;
#include &lt;InqMatrix.h&gt;

/* RESULTS

TEST1 RAW SPEED ---------------------------------------------------------------

(A) Using LSM6DSV w/o Inference
(A) Sensor rate 7536.4 Hz, Use rate 7536.0
(A) g(14, -96, -68), a(-103, 1538, 16592)
(A) Sensor rate 7535.8 Hz, Use rate 7536.0
(A) g(87, -71, -19), a(-96, 1553, 16515)
(A) Sensor rate 7535.8 Hz, Use rate 7535.8
(A) g(63, -150, -53), a(-74, 1663, 16596)

(B) Using MPU-6050 w/o Inference
(B) Sensor rate 8044.3 Hz, Use rate 2098.0
(B) g(439, 781, -108), a(-208, 1252, 16084)
(B) Sensor rate 8044.4 Hz, Use rate 2098.5
(B) g(440, 756, -96), a(-76, 1032, 15992)
(B) Sensor rate 8044.1 Hz, Use rate 2098.4
(B) g(413, 771, -103), a(-140, 1132, 16160)

(A) Using LSM6DSV w/ INFERENCE_SIZE = 10
(A) Sensor rate 7536.2 Hz, Use rate 7535.8
(A) g(29, -7, -32), a(-129, 1592, 16530)
(A) Sensor rate 7535.8 Hz, Use rate 7536.0
(A) g(21, -37, -87), a(-87, 1621, 16473)
(A) Sensor rate 7535.6 Hz, Use rate 7535.8
(A) g(14, -44, -19), a(-140, 1546, 16533)

(B) Using MPU-6050 w/ INFERENCE_SIZE = 10
(B) Sensor rate 8042.8 Hz, Use rate 2062.3
(B) g(404, 758, -99), a(-152, 1076, 15976)
(B) Sensor rate 8042.6 Hz, Use rate 2062.5
(B) g(425, 784, -88), a(-180, 1124, 16096)
(B) Sensor rate 8042.3 Hz, Use rate 2062.6
(B) g(407, 757, -91), a(-200, 1144, 15984)

(A) Using LSM6DSV w/ INFERENCE_SIZE = 50
(A) Sensor rate 7538.1 Hz, Use rate 5333.3
(A) g(60, -70, -35), a(-134, 1578, 16487)
(A) Sensor rate 7535.8 Hz, Use rate 5333.3
(A) g(34, -38, -65), a(-89, 1687, 16466)
(A) Sensor rate 7535.7 Hz, Use rate 5333.3
(A) g(30, -86, -47), a(-129, 1621, 16497)

(B) Using MPU-6050 w/ INFERENCE_SIZE = 50
(B) Sensor rate 8045.9 Hz, Use rate 1576.7
(B) g(422, 778, -106), a(-236, 1088, 16072)
(B) Sensor rate 8044.8 Hz, Use rate 1576.9
(B) g(403, 773, -105), a(-248, 1260, 16060)
(B) Sensor rate 8044.8 Hz, Use rate 1576.0
(B) g(423, 794, -101), a(-136, 1180, 16048)

TEST2 NOISE -------------------------------------------------------------------
GYROSCOPE

Using MPU-6050 gX
(B) 8058.2 Hz
t=59  Avg=773.01  Delta=16.99  MaxDelta=1031.01 over200=604  over1000=247  Stdev=64.97
(B) 8057.8 Hz
t=59  Avg=770.93  Delta=19.07  MaxDelta=1285.93 over200=813  over1000=369  Stdev=75.76
(B) 8058.7 Hz
t=59  Avg=771.43  Delta=18.31  MaxDelta=1029.43 over200=748  over1000=313  Stdev=72.03

Using LSM6DSV gX
(A) 7535.6 Hz
t=15  Avg=58.03  Delta=18.72  MaxDelta=1978.97 over200=86  over1000=6  Stdev=27.58
(A) 7542.1 Hz
t=15  Avg=58.12  Delta=17.32  MaxDelta=95.12 over200=0  over1000=0  Stdev=21.74
(A) 7542.1 Hz
t=15  Avg=58.52  Delta=17.22  MaxDelta=88.52 over200=0  over1000=0  Stdev=21.60

Using MPU-6050 gY
(B) 8055.1 Hz
t=59  Avg=417.41  Delta=20.47  MaxDelta=675.41 over200=838  over1000=0  Stdev=49.44
(B) 8057.3 Hz
t=59  Avg=417.60  Delta=20.74  MaxDelta=675.60 over200=929  over1000=0  Stdev=50.58
(B) 8057.6 Hz
t=59  Avg=417.78  Delta=20.54  MaxDelta=932.78 over200=905  over1000=0  Stdev=49.73

Using LSM6DSV gY
(A) 7542.1 Hz
t=15  Avg=-85.65  Delta=18.29  MaxDelta=107.35 over200=0  over1000=0  Stdev=22.89
(A) 7542.1 Hz
t=15  Avg=-85.25  Delta=18.02  MaxDelta=100.25 over200=0  over1000=0  Stdev=22.60
(A) 7542.1 Hz
t=15  Avg=-85.99  Delta=18.23  MaxDelta=100.99 over200=0  over1000=0  Stdev=22.80

Using MPU-6050 gZ
(B) 8053.8 Hz
t=59  Avg=-100.01  Delta=11.24  MaxDelta=414.99 over200=1  over1000=0  Stdev=17.47
(B) 8056.8 Hz
t=59  Avg=-99.96  Delta=11.26  MaxDelta=415.04 over200=1  over1000=0  Stdev=17.59
(B) 8056.7 Hz
t=59  Avg=-99.97  Delta=11.21  MaxDelta=415.03 over200=1  over1000=0  Stdev=17.39

Using LSM6DSV gZ
(A) 7535.6 Hz
t=15  Avg=-44.47  Delta=11.30  MaxDelta=1655.47 over200=102  over1000=5  Stdev=21.94
(A) 7542.1 Hz
t=15  Avg=-44.19  Delta=10.94  MaxDelta=60.81 over200=0  over1000=0  Stdev=13.71
(A) 7542.1 Hz
t=15  Avg=-44.49  Delta=10.91  MaxDelta=62.51 over200=0  over1000=0  Stdev=13.65

ACCELEROMETER

6DoF Sensor Test LSM6DSV vs IMU6050
Using MPU-6050 aX
(B) 8056.7 Hz
t=59  Avg=-209.19  Delta=41.23  MaxDelta=304.81 over200=130  over1000=0  Stdev=52.03
(B) 8057.2 Hz
t=59  Avg=-209.22  Delta=42.18  MaxDelta=366.78 over200=202  over1000=0  Stdev=53.53
(B) 8058.6 Hz
t=59  Avg=-206.20  Delta=43.94  MaxDelta=569.80 over200=679  over1000=0  Stdev=58.00

6DoF Sensor Test LSM6DSV vs IMU6050
Using LSM6DSV aX
(A) 7535.6 Hz
t=15  Avg=-87.15  Delta=33.90  MaxDelta=199.85 over200=0  over1000=0  Stdev=42.41
(A) 7542.1 Hz
t=15  Avg=-87.18  Delta=33.84  MaxDelta=198.82 over200=0  over1000=0  Stdev=42.41
(A) 7542.1 Hz
t=15  Avg=-86.28  Delta=33.87  MaxDelta=190.72 over200=0  over1000=0  Stdev=42.39
(A) 7542.1 Hz

6DoF Sensor Test LSM6DSV vs IMU6050
Using MPU-6050 aX
(B) 8055.2 Hz
t=59  Avg=1095.15  Delta=40.80  MaxDelta=228.85 over200=10  over1000=0  Stdev=51.07
(B) 8057.3 Hz
t=59  Avg=1089.29  Delta=40.59  MaxDelta=225.29 over200=12  over1000=0  Stdev=50.87
(B) 8057.8 Hz
t=59  Avg=1093.67  Delta=41.14  MaxDelta=221.67 over200=14  over1000=0  Stdev=51.55

6DoF Sensor Test LSM6DSV vs IMU6050
Using LSM6DSV aX
(A) 7535.6 Hz
t=15  Avg=1578.79  Delta=35.65  MaxDelta=850.79 over200=218  over1000=0  Stdev=46.89
(A) 7542.1 Hz
t=15  Avg=1572.33  Delta=32.36  MaxDelta=179.33 over200=0  over1000=0  Stdev=40.59
(A) 7542.1 Hz
t=15  Avg=1572.76  Delta=33.90  MaxDelta=223.24 over200=4  over1000=0  Stdev=42.81

6DoF Sensor Test LSM6DSV vs IMU6050
Using MPU-6050 aZ
(B) 8056.0 Hz
t=59  Avg=16041.72  Delta=80.37  MaxDelta=16724.28 over200=631  over1000=186  Stdev=639.30
(B) 8058.5 Hz
t=59  Avg=16032.75  Delta=82.52  MaxDelta=16733.25 over200=1476  over1000=178  Stdev=625.25
(B) 8057.8 Hz
t=59  Avg=16035.85  Delta=79.20  MaxDelta=16730.15 over200=909  over1000=171  Stdev=612.43

6DoF Sensor Test LSM6DSV vs IMU6050
Using LSM6DSV aZ
(A) 7535.6 Hz
t=15  Avg=16491.22  Delta=36.21  MaxDelta=6378.22 over200=5  over1000=1  Stdev=49.17
(A) 7541.6 Hz
t=15  Avg=16494.97  Delta=36.40  MaxDelta=212.97 over200=2  over1000=0  Stdev=45.69
(A) 7541.6 Hz
t=15  Avg=16493.84  Delta=36.61  MaxDelta=327.16 over200=35  over1000=0  Stdev=46.19

This one is running I2C for 6050 at 1MHz
6DoF Sensor Test LSM6DSV vs IMU6050
Using MPU-6050
(B) 8044.9 Hz
t=39  Avg=16000.12  Delta=85.05  MaxDelta=16258.12 over200=943  over1000=193  Stdev=651.62
(B) 8047.5 Hz
t=39  Avg=16001.19  Delta=82.11  MaxDelta=16259.19 over200=978  over1000=176  Stdev=622.28
(B) 8047.4 Hz
t=39  Avg=16002.78  Delta=82.28  MaxDelta=16260.78 over200=1484  over1000=166  Stdev=605.35

TEST3 CREEP -------------------------------------------------------------------
This is sample output.  Run as long as you want.  Topic data shown was overnight.
It plots the chosen "VAL" once every second for both sensors.  We should seekdir
a trend as the chips warm up.
(A) Using LSM6DSV
(B) Using MPU-6050
   -46    -94
   -57   -102
   -50    -97
   -37   -107
   -71    -96
   -46    -83
   -26    -99
   -47   -105
   -46    -84
   -47    -97
   -45    -86
   -48   -102
   -60   -103
   -15   -105
   -37    -89
   -69   -104
   -57    -97
   -28   -107
   -55    -86
   -28    -94
   -39    -88
   -33    -87
*/

#if USE_LSM6DSV
	#define PIN_MISO 15
	#define PIN_CS   16
	#define PIN_MOSI 17
	#define PIN_SCLK 18
	#define PIN_INT1 3

	InqLSM6DSV imuLSM6(PIN_MISO, PIN_CS, PIN_MOSI, PIN_SCLK);
#endif

#if USE_MPU6050
	#define I2C_SDA 8
	#define I2C_SCL 9
	#define PIN_INT 10 
	
	InqMPU6050 imu6050(I2C_SDA, I2C_SCL);
#endif

// Interrupt

volatile bool aReady = false;
volatile bool bReady = false;
volatile uint32_t aCntSample = 1;
volatile uint32_t bCntSample = 1;

void IRAM_ATTR isrLSM6() { aReady=true; aCntSample++; }
void IRAM_ATTR isr6050() { bReady=true; bCntSample++; }

InqMatrix&lt;float&gt; m1, v1, v2;

// ============================================================
// Setup
// ============================================================

void setup()
{
    Serial.begin(115200);
    delay(1000);
    Serial.println("\n6DoF Sensor Test LSM6DSV vs IMU6050");

#if USE_LSM6DSV
	Serial.println("(A) Using LSM6DSV");
	if (!imuLSM6.begin())
	{
		Serial.println("Sensor Failure\n");
		while(true) delay(1000);
	}		
	pinMode(PIN_INT1, INPUT_PULLUP);
	attachInterrupt(digitalPinToInterrupt(PIN_INT1), 
		isrLSM6, RISING);
#endif

#if USE_MPU6050
	Serial.println("(B) Using MPU-6050");
	imu6050.begin();
	
	pinMode(PIN_INT, INPUT_PULLUP);
	attachInterrupt(digitalPinToInterrupt(PIN_INT), 
		isr6050, RISING);
#endif

	m1.size(INFERENCE_SIZE, INFERENCE_SIZE);
	m1.randomize();
	v1.size(INFERENCE_SIZE);  
	v2.size(INFERENCE_SIZE);
}

// ============================================================
// Main loop
// ============================================================
#define ONE_MEG 1000000.0f
#define MAX 120000
int16_t store;
uint32_t cntStore = 0;

Raw6050 bRawData;
uint32_t bCntUse = 0;

RawLSM6DSV aRawData;
uint32_t aCntUse = 0;

void loop()
{
	static uint32_t ts = micros();
	
	#if TEST1
	uint32_t t = micros() - ts;
	if (t &gt; 5000000)
	{
		#if USE_LSM6DSV
		Serial.printf("(A) Sensor rate %0.1f Hz, Use rate %0.1f\n",
			aCntSample * ONE_MEG / t, aCntUse * ONE_MEG / t);
		aCntSample = 0;
		aCntUse = 0;
		Serial.printf("(A) g(%d, %d, %d), a(%d, %d, %d)\n", 
			aRawData.gX, aRawData.gY, aRawData.gZ, aRawData.aX, aRawData.aY, aRawData.aZ);
		#endif

		#if USE_MPU6050
		Serial.printf("(B) Sensor rate %0.1f Hz, Use rate %0.1f\n",
			bCntSample * ONE_MEG / t, bCntUse * ONE_MEG / t);
		bCntSample = 0;
		bCntUse = 0;
		// Reoriented to match LSM6DSV coordinate frame.
		Serial.printf("(B) g(%d, %d, %d), a(%d, %d, %d)\n", 
			bRawData.gY, bRawData.gX, bRawData.gZ, bRawData.aY, bRawData.aX, bRawData.aZ);
		#endif
		Serial.println("");

		ts = micros();
	}
	
	#endif // TEST1

	#if USE_LSM6DSV
	if (aReady)
	{
		aReady = false;
		aCntUse++;
		aRawData = imuLSM6.readLSM6DSV();
		store = aRawData.VAL;
		cntStore++;
		#if WITH_INFERENCE		
		v1(0) = aRawData.gX;	
		v1(1) = aRawData.gY;	
		v1(2) = aRawData.gZ;	
		v1(3) = aRawData.aX;	
		v1(4) = aRawData.aY;	
		v1(5) = aRawData.aZ;	
		fakeInference(m1, v1, v2);
		#endif
	}
	#endif // USE_LSM6DSV
	
	#if USE_MPU6050
	if (bReady)
	{
		bReady = false;
		bCntUse++;
		bRawData = imu6050.read6050();
		store = bRawData.VAL;
		cntStore++;
		#if WITH_INFERENCE		
		v1(0) = aRawData.gX;	
		v1(1) = aRawData.gY;	
		v1(2) = aRawData.gZ;	
		v1(3) = aRawData.aX;	
		v1(4) = aRawData.aY;	
		v1(5) = aRawData.aZ;	
		fakeInference(m1, v1, v2);
		#endif
	}
	#endif // USE_MPU6050

	#if TEST2
	
	if (cntStore == MAX)
	{
		uint32_t t = micros() - ts;
		Serial.print("");
		#if USE_LSM6DSV
		Serial.printf("(A) %0.1f Hz", aCntSample * 1000000.0f / t);
		aCntSample = 0;
		aCntUse = 0;
		#endif
		#if USE_MPU6050
		Serial.printf("(B) %0.1f Hz", bCntSample * 1000000.0f / t);
		bCntSample = 0;
		bCntUse = 0;
		#endif
		Serial.println("");
		
		float total = 0;
		for (uint32_t i=0; i&lt;MAX; i++)
			total += store;
		float avg = total / MAX;
		float stddv = 0.0f;
		float delta = 0.0f;
		float maxdelta = 0;
		uint32_t over200 = 0;
		uint32_t over1000 = 0;
		for (uint32_t i=0; i&lt;MAX; i++)
		{
			float pt = (float)store - avg;
			stddv += pt * pt;
			
			float d = abs(pt);
			delta += d;
			if (d &gt; maxdelta) maxdelta = d;
			if (d &gt; 200.0f) over200++;
			if (d &gt; 1000.0f) over1000++;
		}
		stddv /= (float)(MAX - 1);
		stddv = sqrt(stddv);
		delta /= (float)MAX;
		Serial.printf("t=%u  Avg=%0.2f  Delta=%0.2f  MaxDelta=%0.2f over200=%u  over1000=%u  Stdev=%0.2f\n",
			t / 1000000, avg, delta, maxdelta, over200, over1000, stddv);

		ts = micros();
		cntStore = 0;
	}
	#endif // TEST2
	
	#if TEST3
	// Creep Test	
	uint32_t t = micros();
	if (t - ts &gt; 1000000)
	{
	    Serial.printf("%6d %6d\n", aRawData.gZ, bRawData.gZ);
		ts = t;
	}
	#endif // TEST3
}

void fakeInference(InqMatrix&lt;float&gt;&amp; m1, InqMatrix&lt;float&gt;&amp; v1, InqMatrix&lt;float&gt;&amp; v2)
{
	v2.productOf(m1, v1);
}	

</pre>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>Inq</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/electronic-components/comparing-lsm6dsv-vs-mpu6050-gyroscope-accelerometers/</guid>
                    </item>
				                    <item>
                        <title>Theory of terra incognita</title>
                        <link>https://forum.dronebotworkshop.com/electronic-components/theory-of-terra-incognita/</link>
                        <pubDate>Sat, 22 Aug 2026 05:25:50 +0000</pubDate>
                        <description><![CDATA[I have no idea about the theory of all that terra incognita I hope it would accomplish my plan. The plan is to build a &quot;Voyager clone&quot; into an Altoids tin. There exist already clones of anci...]]></description>
                        <content:encoded><![CDATA[<p>I have no idea about the theory of all that <em>terra incognita</em> I hope it would accomplish my plan. The plan is to build a "Voyager clone" into an Altoids tin. There exist already clones of ancient HP calculators, e.g. <a title="DM12C Financial Calculator" href="https://www.swissmicros.com/en/products/dm12c" target="_blank" rel="noopener">DM12C</a> or <a title="HP Classic Calculators Emulator" href="https://paxerlabs.com/voyager/" target="_blank" rel="noopener">paxerlabs.com/voyager</a>, but none with Wifi nor clamshell casing.</p>
<p>I've found the <a title="ESP32 Selection Guide – 2026" href="https://dronebotworkshop.com/esp32-2026/" target="_blank" rel="noopener">ESP32 Selection Guide</a> and the <a title="ESP Product Selector" href="https://products.espressif.com/#/product-selector" target="_blank" rel="noopener">Product Selector</a>, alas, there are criteria I don't grasp (yet): Flash, SRAM, ROM, PSRAM, ... well, I found SRAM explained in Wikipedia, but my question is -- how much of it will my project need? I surmise my software and its data must fit completely in the available SRAM. How may I estimate the size of the future s/w? What I plan to run exists already as a PC program, <a title="NutEm/PC — A Nut Firmware Interpreter Built On ooREXX/Win" href="http://www.hp41.org/html/NutEmPC.htm" target="_blank" rel="noopener">NutEm/PC</a> written in ooREXX, I must migrate its core to the SoC, no idea yet how to do that and how "resource demanding" the result will be.</p>
<p>Next theoretical question is, do I need a <span>general-purpose experimenter board? I don't like the trial-and-error way, I favour know-why rather know-how only. I'm used to work with virtual systems (once VM/ESA, today with virtual calculators) and hope I could push my project to a mature state <a title="Top 3 Free ESP32 Simulators for 2026" href="https://youtu.be/_HsZzkSYao0" target="_blank" rel="noopener">using simulations</a> only. Is that realistic?</span></p>
<p>But the major question is -- would an ESP32 be the best choice to achieve my goal?</p>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>Amphitryonoff</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/electronic-components/theory-of-terra-incognita/</guid>
                    </item>
				                    <item>
                        <title>simple Neural Net example</title>
                        <link>https://forum.dronebotworkshop.com/neural-networks/simple-neural-net-example/</link>
                        <pubDate>Fri, 21 Aug 2026 23:04:23 +0000</pubDate>
                        <description><![CDATA[@inq is giving a tutorial on building a neural network from ground up.
As a supplement I have found this link helpful as a simple starter.
I wrote a FreeBASIC version at the time (2018) an...]]></description>
                        <content:encoded><![CDATA[<p>@inq is giving a tutorial on building a neural network from ground up.</p>
<p>As a supplement I have found this link helpful as a simple starter.</p>
<p>https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/</p>
<p>I wrote a FreeBASIC version at the time (2018) and this is a GPTChat Python translation:</p>
<p>&nbsp;</p>
<pre contenteditable="false">import math


def sigmoid(x):
    return 1 / (1 + math.exp(-x))


# ============================================================
# INITIAL VALUES
# ============================================================

# Inputs
i1 = 0.05
i2 = 0.10

# Weights
w1 = 0.15
w2 = 0.20
w3 = 0.25
w4 = 0.30
w5 = 0.40
w6 = 0.45
w7 = 0.50
w8 = 0.55

# Biases
b1 = 0.35
b2 = 0.60

# Targets
target_o1 = 0.01
target_o2 = 0.99

# Learning rate
learning_rate = 0.5


# ============================================================
# FORWARD PASS
# ============================================================

# Hidden neuron h1
net_h1 = w1 * i1 + w2 * i2 + b1
out_h1 = sigmoid(net_h1)

# Hidden neuron h2
net_h2 = w3 * i1 + w4 * i2 + b1
out_h2 = sigmoid(net_h2)

# Output neuron o1
net_o1 = w5 * out_h1 + w6 * out_h2 + b2
out_o1 = sigmoid(net_o1)

# Output neuron o2
net_o2 = w7 * out_h1 + w8 * out_h2 + b2
out_o2 = sigmoid(net_o2)


# ============================================================
# ERROR
# ============================================================

E_o1 = 0.5 * (target_o1 - out_o1) ** 2
E_o2 = 0.5 * (target_o2 - out_o2) ** 2

E_total = E_o1 + E_o2


# ============================================================
# BACKWARD PASS
#
# First calculate all new weights using the ORIGINAL weights.
# The actual update happens after all gradients have been
# calculated.
# ============================================================


# ============================================================
# OUTPUT LAYER
# ============================================================

# ---- w5 ----

dE_do1 = -(target_o1 - out_o1)
do1_dneto1 = out_o1 * (1 - out_o1)
dneto1_dw5 = out_h1

gradient_w5 = dE_do1 * do1_dneto1 * dneto1_dw5

new_w5 = w5 - learning_rate * gradient_w5


# ---- w6 ----

dE_do1 = -(target_o1 - out_o1)
do1_dneto1 = out_o1 * (1 - out_o1)
dneto1_dw6 = out_h2

gradient_w6 = dE_do1 * do1_dneto1 * dneto1_dw6

new_w6 = w6 - learning_rate * gradient_w6


# ---- w7 ----

dE_do2 = -(target_o2 - out_o2)
do2_dneto2 = out_o2 * (1 - out_o2)
dneto2_dw7 = out_h1

gradient_w7 = dE_do2 * do2_dneto2 * dneto2_dw7

new_w7 = w7 - learning_rate * gradient_w7


# ---- w8 ----

dE_do2 = -(target_o2 - out_o2)
do2_dneto2 = out_o2 * (1 - out_o2)
dneto2_dw8 = out_h2

gradient_w8 = dE_do2 * do2_dneto2 * dneto2_dw8

new_w8 = w8 - learning_rate * gradient_w8


# ============================================================
# HIDDEN LAYER
# ============================================================

# ------------------------------------------------------------
# w1
# ------------------------------------------------------------

# Error flowing from output neuron o1
dE_do1 = -(target_o1 - out_o1)
do1_dneto1 = out_o1 * (1 - out_o1)
dneto1_douth1 = w5

dEo1_douth1 = dE_do1 * do1_dneto1 * dneto1_douth1


# Error flowing from output neuron o2
dE_do2 = -(target_o2 - out_o2)
do2_dneto2 = out_o2 * (1 - out_o2)
dneto2_douth1 = w7

dEo2_douth1 = dE_do2 * do2_dneto2 * dneto2_douth1


# Total error with respect to h1 output
dE_douth1 = dEo1_douth1 + dEo2_douth1

# h1 sigmoid derivative
douth1_dneth1 = out_h1 * (1 - out_h1)

# h1 net input with respect to w1
dneth1_dw1 = i1

gradient_w1 = (
    dE_douth1
    * douth1_dneth1
    * dneth1_dw1
)

new_w1 = w1 - learning_rate * gradient_w1


# ------------------------------------------------------------
# w2
# ------------------------------------------------------------

dneth1_dw2 = i2

gradient_w2 = (
    dE_douth1
    * douth1_dneth1
    * dneth1_dw2
)

new_w2 = w2 - learning_rate * gradient_w2


# ------------------------------------------------------------
# w3
# ------------------------------------------------------------

# Error flowing through h2
dE_do1 = -(target_o1 - out_o1)
do1_dneto1 = out_o1 * (1 - out_o1)
dneto1_douth2 = w6

dEo1_douth2 = dE_do1 * do1_dneto1 * dneto1_douth2


dE_do2 = -(target_o2 - out_o2)
do2_dneto2 = out_o2 * (1 - out_o2)
dneto2_douth2 = w8

dEo2_douth2 = dE_do2 * do2_dneto2 * dneto2_douth2


# Total error with respect to h2 output
dE_douth2 = dEo1_douth2 + dEo2_douth2

# h2 sigmoid derivative
douth2_dneth2 = out_h2 * (1 - out_h2)

# h2 net input with respect to w3
dneth2_dw3 = i1

gradient_w3 = (
    dE_douth2
    * douth2_dneth2
    * dneth2_dw3
)

new_w3 = w3 - learning_rate * gradient_w3


# ------------------------------------------------------------
# w4
# ------------------------------------------------------------

dneth2_dw4 = i2

gradient_w4 = (
    dE_douth2
    * douth2_dneth2
    * dneth2_dw4
)

new_w4 = w4 - learning_rate * gradient_w4


# ============================================================
# DISPLAY RESULTS
# ============================================================

print("Forward pass:")
print(f"net_h1  = {net_h1:.9f}")
print(f"out_h1  = {out_h1:.9f}")
print(f"net_h2  = {net_h2:.9f}")
print(f"out_h2  = {out_h2:.9f}")
print(f"net_o1  = {net_o1:.9f}")
print(f"out_o1  = {out_o1:.9f}")
print(f"net_o2  = {net_o2:.9f}")
print(f"out_o2  = {out_o2:.9f}")
print()

print("Error:")
print(f"E_o1    = {E_o1:.9f}")
print(f"E_o2    = {E_o2:.9f}")
print(f"E_total = {E_total:.9f}")
print()

print("New weights:")
print(f"new w1 = {new_w1:.9f}")
print(f"new w2 = {new_w2:.9f}")
print(f"new w3 = {new_w3:.9f}")
print(f"new w4 = {new_w4:.9f}")
print(f"new w5 = {new_w5:.9f}")
print(f"new w6 = {new_w6:.9f}")
print(f"new w7 = {new_w7:.9f}")
print(f"new w8 = {new_w8:.9f}")
</pre>
<p><br />A utube someone recommended to me at the time:<br /><br />https://www.youtube.com/watch?v=BR9h47Jtqyw<br /><br /></p>
<p>Evolution is learning system. Slightly modify the DNA on a set of animals and see which one survives long enough to reproduce. The survivors repeat the process thus complexity increases and the sticks.</p>
<p>&nbsp;</p>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>robotBuilder</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/neural-networks/simple-neural-net-example/</guid>
                    </item>
				                    <item>
                        <title>SMS Text messages with ESP32 and SIM7xxx GSM cell phone modules</title>
                        <link>https://forum.dronebotworkshop.com/suggest-content/sms-text-messages-with-esp32-and-sim7xxx-gsm-cell-phone-modules/</link>
                        <pubDate>Fri, 21 Aug 2026 20:42:00 +0000</pubDate>
                        <description><![CDATA[I&#039;m currently working with a project to design a system that sends a &#039;very occasional&#039; SMS Text message to another phone.  Woah, woah, woah - a fair number of &#039;this is so easy&#039; articles but ...]]></description>
                        <content:encoded><![CDATA[<p>I'm currently working with a project to design a system that sends a 'very occasional' SMS Text message to another phone.  Woah, woah, woah - a fair number of 'this is so easy' articles but very little actual working stuff.  Data communication to a server as an IOT device or phone seems doable,  but as soon as you venture into sending an SMS text - everything seems to go out the window.  Lots of the SIM card providers offer sim cards, but then it turns out they don't to SMS.  It also seems you can buy a full boat phone plan from a provider, and some are inexpensive (like $15 or $25 a month), but that seems a bit extreme to send maybe 1 text message a month.  For vendors I have tried, their sim cards get denied service.  Vendors don't seem to know why.<br />Anyway - this looks to me like a pretty interesting project area, and I'd love to see an actual factual how to from Bill.</p>]]></content:encoded>
						                            <category domain="https://forum.dronebotworkshop.com/"></category>                        <dc:creator>JamesW</dc:creator>
                        <guid isPermaLink="true">https://forum.dronebotworkshop.com/suggest-content/sms-text-messages-with-esp32-and-sim7xxx-gsm-cell-phone-modules/</guid>
                    </item>
							        </channel>
        </rss>
		