analogWrite slow?

Posted on
  • I'm trying to add a speaker to a project, following the Making Music page using an ESP8266 board (NodeMCU v3).

    The example is using analogWrite() to create sound on the speaker and it works.
    When the speaker is emmiting sound, I can enter new analogWrites with different frequencies to change the pitch and that works instantly.

    The problem is when the speaker is going from silent to emmiting sound. There is quite a long delay before it does anything (~1700ms). Is that normal behavior with the ESP8266 ?

    The final desired product chimes when a door is opened and 1700ms delay makes it weird.


    // starting from silent
    let BUZZER = NodeMCU.D1
    // action : activate buzzer at 1000 freq
    analogWrite(BUZZER, 0.5, {freq:1000});
    // result : ~ 1700ms delay... then sound
    // action : change freq to 1500
    analogWrite(BUZZER, 0.5, {freq:1500});
    // result : instant pitch change
    // action : stop buzzer
    analogWrite(BUZZER, 0, {freq:1500});
    // result : instant stop
    // action : activate buzzer again
    analogWrite(BUZZER, 0.5, {freq:1000});
    // result : ~1700ms delay then sound

    So, copy-pasting the whole example code results in no sounds as there are no notes lasting over 1700ms.

    Example 2:
    I can also observe the same effect visually with the following snippet (breathing LED) :

    setInterval(()=>{ analogWrite(D2, (Math.sin(getTime()) / 2.01) + 0.5) }, 10) 

    And then at any point sending analogWrite(D2,0) or digitalWrite(D2,0) (or 1) will pause the effect for roughly 1700ms again. Actually, sending analogWrite between 0 and 1 (exclusively) to any pin that is not already been "activated" by another analogWrite will pause the effect on the unrelated D2 pin!

    What I have tried :

    1. Calling analogWrite twice
    2. Using digitalWrite for 0 and 1 values
    3. Using analogWrite 0 and 1 with and without frequency
    4. Using analogWrite in a setTimeout
    5. Using values very close to 0 and 1 as "off" state (that kinda works but big cheat as it is still audible from closer)
    6. Using a different ESP8266 dev board

    Bonus info:

    In the breathing LED effect snippet, I am dividing the sin of time by 2.01 instead of 2 which would allow the numbers to include 0 and 1. If I don't do so, the LED becomes unpredictable when going low, it can flash at full brightness for a very small amount of time, Im assuming when it reaches an actual 0 or 1, but the flash does not last 1700ms.

  • Fri 2020.02.21

    While I normally don't respond to non-officially supported boards posts @Drumline18, this one intrigued me as I recently played with similar code snippets, but with non ESP8266 boards.

    In my case, using a logic analyzer, the duration to produce a 1KHz signal using similar sample snippets as in post #1, would occur in one cycle or in this case ~1msec.

    'There is quite a long delay before it does anything'

    But in L9 it is stated 'instant' - so which is it?

    As all the code is not present, it is difficult to determine, but the one and a half second delay could be in another part of the code block. Post all the code.

    Without the datasheet for the specified pins, and their ability to drive the speaker, I'd have to guess here. Again, post the link to the specific device datasheet.

    How is the speaker connected? Is there a decoupling capacitor used? Does that have polarity? If so, it's possible the delay is caused as that cap is charging. Again, post a picture or better describe.

    re L3 '// action : activate buzzer at 1000 freq'

    reference to 'speaker' in text and 'buzzer' in code

    Is it an actual 4 ohm or 8 ohm speaker, or is it a passive/active buzzer as the keyword defined implies?

    I suppose there may be an internal issue. Clicking on the Right facing arrow adjacent to the analogWrite() function heading might lead to some clues.­obal_analogWrite

    Click on Right Facing arrow takes one to it's source:­b/master/src/jswrap_io.c#L167

    Divisor for frequency perhaps? Not likely as 1000 base10 is not easily divided by a nice value such as 1024 or 2^10 base2

    What are the results of attempting a dozen other frequency values and another group of multiples of ten? a test to see if division by ten is causal

    Is memory being cleared between each new frequency test? That example 2 nesting of an analogWrite() within an interval might be problematic. The previous write to that pin might not be cleared before the intended frequency is desired. I found out something similar when the logic analyzer displayed more pulses (higher frequency) than what I thought I was attempting to output.

  • English is not my native language, sorry if my explanations are not clear enough.

    I don't want to bother Gordon with the title-thingie but I assure you I'm supporting the project on Patreon. Thank you for your answer.

    There is no complete code. I was playing with it in the same way shown in the video at the top of Making Music, using the WebIDE. But here is the complete code from the example, in one block:

    var BUZZER=A9;
    function freq(f) { 
      if (f===0) digitalWrite(BUZZER,0);
      else analogWrite(BUZZER, 0.5, { freq: f } );
    var pitches = {
    function step() {
      var ch = tune[pos];
      if (ch !== undefined) pos++;
      if (ch in pitches) freq(pitches[ch]);
      else freq(0); // off
    var tune = "c    c    c   d    e   e  d   e    f   g   C  C C   g  g g   e  e e   c  c c  g    f  e   d c";
    var pos = 0;
    setInterval(step, 100);

    You can see in that code where the BUZZER terminology comes from. But I have both a passive buzzer and a speaker salvaged from a screen (no markings). Both exhibit the same behavior, and the LED too, so that's why I'm targetting the MCU/code now. No capacitors, no resistors. The buzzer has polarity. It's connected GND to GND and + to a pin, directly, just like in the tutorial.

    The confusion over L9 stating I can change frequencies instantly stems from the fact the problem depends on the state of the pin I'm trying to change.

    If a pin is already using PWM (i.e. by a previous analogWrite 0.x), the changes are instant.
    But, if the current state of the pin is either exactly 0 or 1, there is a delay before changes occur.
    But also, transitioning from 0 or 1 to an intermediary value on another pin will hang other pins.

    1 -> 0.x = delay
    0 -> 0.x = delay
    0.x -> 0.y = instant
    0.x -> 0 = instant
    0.x -> 1 = instant
    0.x -> freq change => instant

    Its like... sticking to the 1 and 0. I don't know how to better explain it. It may require a video (tomorrow once everyone is awake).

    Here is the code I am using to get that 1700ms figure :

    analogWrite(PIN, 0.5, {freq:1000});
    setTimeout(()=>{analogWrite(PIN,0)}, 1700)

    By playing with the timeout delay I get it to where I can hear it start, resulting in a extremely short beep. At 1700 I can still hear it but at 1690 I can't.

    I get the exact same behavior, down to the 1700ms figure, with 2 different boards of different manufacturers. I can't find the proper datasheets (I don't know which ones are the proper ones) but I highly doubt it makes a difference. One is ESP-12F and the other is ESP8266MOD. I tried all pins with the buzzer and used onboard LEDs for the visual tests.

    Following your link to source code I am reading the implementation for ESP8266.
    The code executes differently if Hardware PWM is defined, notably defaulting the freq at 50 in software mode and 1000 in hardware mode.­b/624d6f2c700bfbf08e4a7eb7d282bd57648470­56/targets/esp8266/jshardware.c#L609

    Whenever I test without adding a frequency I get the same sound as if I try with freq : 50, leading me to believe it is defaulting to software right away. I don't understand much further in the source though.

  • Hi @Drumline18

    thanks for sharing this finding.

    Here is the code I am using to get that 1700ms figure :

    analogWrite(PIN, 0.5, {freq:1000});
    setTimeout(()=>{analogWrite(PIN,0)}, 1700)

    I will run some test and let you know if there is a way to fix this.

  • I use function() instead of ()=>, because it doesn't work in my browser.
    And to stop analogWrite, use digatalRead.

    // sinus.js
    //setInterval( ()=> {analogWrite(D2, (Math.sin(getTime()) / 2.01) + 0.5 );},10);
    //setInterval( function() {analogWrite(D2, (Math.sin(getTime()) / 2.001) + 0.5 );},10);
    analogWrite(D2, 0.5, {freq:1000});
    //setTimeout(function() {analogWrite(D2, 0);},1700);
    setTimeout(function() {digitalRead(D2);},10);
  • Moved my comment to a more appropriate place hopefully (sorry for the confusion):­355/

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

analogWrite slow?

Posted by Avatar for Drumline18 @Drumline18