user77787: I have tried to follow along/reproduce the results from several sources for emulating the Nikon infrared remote trigger:

Adafruit Guide
DIY Nikon ML-L3 IR Wireless Remote Hack with Arduino UNO
Teardown and Project: Clone Nikon ML-L3 IR Remote and Emulation
Teach you how to DIY Nikon ML-L3 infrared remote control did not read yet
Arduino wireless remote for Nikon DSLR
Arduino Nikon Infrared command code
Arduino – IR remote/ intervalometer for Nikon D80 DSLR

I can read the signal from my remote trigger, but I cannot reproduce it with my Arduino setup. The signal I can receive (w/ TSOP38238 receiver diode) matches all of the signals given by the links above (there is some expected variance in pulse durations).
I don't think my hardware is at fault:

TSOP38238 receives @ 950 nm
my IR LED is 940-950 nm per datasheet
my IR LED works (I can see it emitting when I point it at my phone camera)


simulate this circuit – Schematic created using CircuitLab
My Code:
I have tried the code given in the links above, most don't use Port Manipulation and instead trial-and-error the delay durations to account for the overhead in Arduino functions. I have experimented with different values of the wait variable (8-13 microseconds) but have not gotten it working so far. I prefer to have my code versatile like this (no hard coded durations) so I can use it with other remotes in the future.
uint16_t Signal[] = [2050, 27800, 430, 1650, 430, 3650, 430, 65000]; // ON & OFF pulse durations (microseconds)
uint8_t wait = 13; // duration (microseconds) between pin state changes

// the setup function runs once when you press reset or power the board
void setup() {
    DDRB = B00000001; // set digital pin 8 to output
    PORTB = B00000000; // set digital pin 8 LOW
    Serial.begin(9600); // start serial monitor
}

// the loop function runs over and over again forever
void loop() {
    delay(1500); // wait a bit between sending signal
    sendSignal();
    sendSignal();
    // send signal twice, this is what the actual remote does
    Serial.println("Signal sent"); // message saying signal was sent
}

// function that sends the signal
void sendSignal(void) {
    for (int i = 0; i < sizeof(Signal)/sizeof(Signal[0]); i++) { // for each element in Signal array
        if (i%2 > 0) { // odd numbered index, is an OFF pulse
            PORTB = B00000000; // set digital pin 8 LOW
            delayMicroseconds(Signal[i]); // delay for OFF pulse duration
        } else { // even numbered index (including i=0), is an ON pulse, 38 kHz (26.3 usec period), 50% duty cycle
            unsigned long currentTime = micros(); // get current time
            while (micros() - currentTime < (unsigned long)Signal[i]) { // while still in ON pulse duration
                PORTB = B00000001; // set digital pin 8 HIGH
                delayMicroseconds(wait); // leave ON for time
                PORTB = B00000000; // set digital pin 8 LOW
                delayMicroseconds(wait); // leave OFF for time
            }
        }
    }
}

I hold my camera in direct line of sight of the LED (not blocking the receiver on the camera) from distances of ~2in to ~3ft away without success.
Has anyone tried this before? Is there a mistake in my approach or code?

user77787: Got it working. The problems were the delayMicroseconds input being too large (see comments) and the frequency mismatches.
I set up a script to time the code segments of interest and that allowed me to select the best wait duration. It looks like this:
while (i < 256) {
    /* your code to time here */
    i++;
}
i = 0;
unsigned long time = 10000000 + micros();
Serial.println(time);

Make sure to time the empty loop and subtract that from your readings w/ code. Take the difference between time read outs, subtract the printing statement overhead (which is dependent on how many digits it is printing, hence the 10000000 + micros() to keep # of digits constant) and divide by 256.
I measured 4 different combinations to find the closest frequency match:




Command Type:
Wait duration (micro seconds):
Frequency (Hz):
% difference from 38 kHz




Port Manipulation
14
36990
-2.7 %


Port Manipulation
13
39960
5.2 %


digitalWrite
11
35980
-5.3 %


digitalWrite
10
38790
2.1 %




The final code ended up looking a lot like the one from this link; counting off cycles of the on-off 38 kHz pulse.

77787: <p>I have tried to follow along/reproduce the results from several sources for emulating the Nikon infrared remote trigger:</p>
<ul>
<li><a href="https://learn.adafruit.com/ir-sensor/overview" rel="nofollow noreferrer">Adafruit Guide</a></li>
<li><a href="https://youtu.be/lk3DIUwNq2Q" rel="nofollow noreferrer">DIY Nikon ML-L3 IR Wireless Remote Hack with Arduino UNO</a></li>
<li><a href="https://goughlui.com/2013/12/06/teardown-and-project-clone-nikon-ml-l3-ir-remote-and-emulation/" rel="nofollow noreferrer">Teardown and Project: Clone Nikon ML-L3 IR Remote and Emulation</a></li>
<li><a href="https://www.programmersought.com/article/56665017649/" rel="nofollow noreferrer">Teach you how to DIY Nikon ML-L3 infrared remote control</a> <em>did not read yet</em></li>
<li><a href="https://youtu.be/198oRcCQdP8" rel="nofollow noreferrer">Arduino wireless remote for Nikon DSLR</a></li>
<li><a href="https://www.christidis.info/index.php/personal-projects/arduino-nikon-infrared-command-code" rel="nofollow noreferrer">Arduino Nikon Infrared command code</a></li>
<li><a href="https://duino4projects.com/arduino-ir-remote-intervalometer-nikon-d80-dslr/" rel="nofollow noreferrer">Arduino – IR remote/ intervalometer for Nikon D80 DSLR</a></li>
</ul>
<p>I can read the signal from my remote trigger, but I cannot reproduce it with my Arduino setup. The signal I can receive (w/ TSOP38238 receiver diode) matches all of the signals given by the links above (there is some expected variance in pulse durations).</p>
<p><strong>I don't think my hardware is at fault:</strong></p>
<ul>
<li>TSOP38238 receives @ 950 nm</li>
<li>my <a href="https://www.robotshop.com/media/files/pdf/datasheet-com-09349.pdf" rel="nofollow noreferrer">IR LED</a> is 940-950 nm per datasheet</li>
<li>my IR LED works (I can see it emitting when I point it at my phone camera)</li>
</ul>

<p><img src="https://i.stack.imgur.com/xF00k.png" alt="schematic" /></p>

<p><sup><a href="/plugins/schematics?image=http%3a%2f%2fi.stack.imgur.com%2fxF00k.png">simulate this circuit</a> &ndash; Schematic created using <a href="https://www.circuitlab.com/" rel="nofollow">CircuitLab</a></sup></p>
<p><strong>My Code:</strong></p>
<p>I have tried the code given in the links above, most don't use <a href="https://www.arduino.cc/en/Reference/PortManipulation" rel="nofollow noreferrer">Port Manipulation</a> and instead trial-and-error the delay durations to account for the overhead in Arduino functions. I have experimented with different values of the <code>wait</code> variable (8-13 microseconds) but have not gotten it working so far. I prefer to have my code versatile like this (no hard coded durations) so I can use it with other remotes in the future.</p>
<pre><code>uint16_t Signal[] = [2050, 27800, 430, 1650, 430, 3650, 430, 65000]; // ON &amp; OFF pulse durations (microseconds)
uint8_t wait = 13; // duration (microseconds) between pin state changes

// the setup function runs once when you press reset or power the board
void setup() {
    DDRB = B00000001; // set digital pin 8 to output
    PORTB = B00000000; // set digital pin 8 LOW
    Serial.begin(9600); // start serial monitor
}

// the loop function runs over and over again forever
void loop() {
    delay(1500); // wait a bit between sending signal
    sendSignal();
    sendSignal();
    // send signal twice, this is what the actual remote does
    Serial.println(&quot;Signal sent&quot;); // message saying signal was sent
}

// function that sends the signal
void sendSignal(void) {
    for (int i = 0; i &lt; sizeof(Signal)/sizeof(Signal[0]); i++) { // for each element in Signal array
        if (i%2 &gt; 0) { // odd numbered index, is an OFF pulse
            PORTB = B00000000; // set digital pin 8 LOW
            delayMicroseconds(Signal[i]); // delay for OFF pulse duration
        } else { // even numbered index (including i=0), is an ON pulse, 38 kHz (26.3 usec period), 50% duty cycle
            unsigned long currentTime = micros(); // get current time
            while (micros() - currentTime &lt; (unsigned long)Signal[i]) { // while still in ON pulse duration
                PORTB = B00000001; // set digital pin 8 HIGH
                delayMicroseconds(wait); // leave ON for time
                PORTB = B00000000; // set digital pin 8 LOW
                delayMicroseconds(wait); // leave OFF for time
            }
        }
    }
}
</code></pre>
<p>I hold my camera in direct line of sight of the LED (not blocking the receiver on the camera) from distances of ~2in to ~3ft away without success.</p>
<p>Has anyone tried this before? Is there a mistake in my approach or code?</p>
 77787: <p>Got it working. The problems were the <code>delayMicroseconds</code> input being too large (see comments) and the frequency mismatches.</p>
<p>I set up a script to time the code segments of interest and that allowed me to select the best wait duration. It looks like this:</p>
<pre><code>while (i &lt; 256) {
    /* your code to time here */
    i++;
}
i = 0;
unsigned long time = 10000000 + micros();
Serial.println(time);
</code></pre>
<p>Make sure to time the empty loop and subtract that from your readings w/ code. Take the difference between <code>time</code> read outs, subtract the printing statement overhead (which is dependent on how many digits it is printing, hence the <code>10000000 + micros()</code> to keep # of digits constant) and divide by 256.</p>
<p>I measured 4 different combinations to find the closest frequency match:</p>
<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th style="text-align: center;">Command Type:</th>
<th style="text-align: center;">Wait duration (micro seconds):</th>
<th style="text-align: center;">Frequency (Hz):</th>
<th style="text-align: center;">% difference from 38 kHz</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">Port Manipulation</td>
<td style="text-align: center;">14</td>
<td style="text-align: center;">36990</td>
<td style="text-align: center;">-2.7 %</td>
</tr>
<tr>
<td style="text-align: center;">Port Manipulation</td>
<td style="text-align: center;">13</td>
<td style="text-align: center;">39960</td>
<td style="text-align: center;">5.2 %</td>
</tr>
<tr>
<td style="text-align: center;">digitalWrite</td>
<td style="text-align: center;">11</td>
<td style="text-align: center;">35980</td>
<td style="text-align: center;">-5.3 %</td>
</tr>
<tr>
<td style="text-align: center;"><strong>digitalWrite</strong></td>
<td style="text-align: center;"><strong>10</strong></td>
<td style="text-align: center;"><strong>38790</strong></td>
<td style="text-align: center;"><strong>2.1 %</strong></td>
</tr>
</tbody>
</table>
</div>
<p>The final code ended up looking a lot like the one from <a href="https://www.christidis.info/index.php/personal-projects/arduino-nikon-infrared-command-code" rel="nofollow noreferrer">this link</a>; counting off cycles of the on-off 38 kHz pulse.</p>
