How do I digitalWrite binary data at a certain time frame?

I want to be able to output a 16 bit binary with a delay of like 3000 microseconds each bit. How do I do that?

That's slow enough that you should be able to bit bang it.

Use of the function delayMicroseconds() comes to mind. The maximum is 16383 us.

Keep in mind that digitalWrite() takes around 4 us on an Arduino Uno R3; much faster with modern MCUs.

corrected (reset val after processing)

const byte PinOut = LED_BUILTIN;

unsigned long msecBit = 250;        // 3;  for testing

// -----------------------------------------------------------------------------
void out (
    unsigned val )
{
    digitalWrite (PinOut, LOW);         // start marker
    delay (msecBit);

    for (int n = 0; n < 16; n++)  {
        digitalWrite (PinOut, val & 1);
        delay (msecBit);
        val >>= 1;
    }
    digitalWrite (PinOut, HIGH);        // restore output to non-start cond
}


// -----------------------------------------------------------------------------
unsigned val;
void loop ()
{
    if (Serial.available ())  {
        char c = Serial.read ();

        switch (c)  {
        case '0'...'9':
            val = 10*val + c-'0';
            return;

        case 'o':
            Serial.println (val);
            out (val);
            val = 0;
            break;
        }
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (115200);

    pinMode      (PinOut, OUTPUT);
    digitalWrite (PinOut, HIGH);        // restore output to non-start cond
}

for pulse sequences of specified time periods have a look at the ESP32 Remote Control Transceiver (RMT)

give more details of what you are attempting to implement?

perhaps it's not obvious that without a known idle condition (when data is not being transmitted) and the actual data preceeded by the opposite condition, there is no way to determine when the data is transmitted and what the 1st value is.

using the ESP32S3 RMT the 16bit sequence 1010000000111111 (each bit 3mSec) would look like (blue trace is 16bit data yellow is 1mSec oscilloscope trigger pulse)

the complete sequence is 48mSec

looking at start of sequence bits are 3mSec

program

// ESP32S3 RMT 16 bit sequence 3mSec  sync test

// https://docs.espressif.com/projects/esp-idf/en/v5.3.1/esp32/api-reference/peripherals/rmt.html

// on a ESP32 I get error
//  rmt_new_sync_manager(345): sync manager not supported

#include "Arduino.h"
#include "driver/rmt_tx.h"

// setup Tx channels
rmt_channel_handle_t tx_channels[2] = { NULL };
gpio_num_t tx_gpio_number[2] = { GPIO_NUM_14, GPIO_NUM_15 };  // pin numbers

rmt_encoder_handle_t copyEncoder{ NULL };
rmt_copy_encoder_config_t copyEncoderConfig = {};

rmt_transmit_config_t transmitConfig = {
  .loop_count = 1
};

// 16 bits 3mSec each - 16bit data 1010000000111111
const rmt_symbol_word_t pulsePattern1[] = {     
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 1 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 1 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 1 },
};
const rmt_symbol_word_t pulsePattern2[] = {     // 1mSec pulse to trigger oscilloscope
  { .duration0 = 1000, .level0 = 1, .duration1 = 1000, .level1 = 0 },
};

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("ESP32S3 RMT 16 bit sequence sync test");
  delay(2000);
  for (int i = 0; i < 2; i++) {
    rmt_tx_channel_config_t tx_chan_config = {
      .gpio_num = tx_gpio_number[i],
      .clk_src = RMT_CLK_SRC_DEFAULT,
      .resolution_hz = 1 * 1000 * 1000,  // 1MHz clock
      .mem_block_symbols = 64,
      .trans_queue_depth = 1,
    };
    ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_channels[i]));
  }
  assert(rmt_new_copy_encoder(&copyEncoderConfig, &copyEncoder) == ESP_OK && "Failed to Create Copy Encoder");
  ESP_ERROR_CHECK(rmt_enable(tx_channels[1]));
  ESP_ERROR_CHECK(rmt_enable(tx_channels[0]));

  // setup sync manage for the two pulses
  rmt_sync_manager_handle_t synchro = NULL;
  rmt_sync_manager_config_t synchro_config = {
    .tx_channel_array = tx_channels,
    .array_size = sizeof(tx_channels) / sizeof(tx_channels[0]),
  };
  ESP_ERROR_CHECK(rmt_new_sync_manager(&synchro_config, &synchro));

  Serial.println("Starting Transmitters");
  // setup pulse patterns - clock is 10MHz
  assert(rmt_transmit(tx_channels[0], copyEncoder, &pulsePattern1, sizeof(pulsePattern1), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
  assert(rmt_transmit(tx_channels[1], copyEncoder, &pulsePattern2, sizeof(pulsePattern2), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
  Serial.println("Transmitters Running");
}
void loop() {
  if (Serial.available()) {
    while (Serial.available()) Serial.read();
    assert(rmt_transmit(tx_channels[0], copyEncoder, &pulsePattern1, sizeof(pulsePattern1), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
    assert(rmt_transmit(tx_channels[1], copyEncoder, &pulsePattern2, sizeof(pulsePattern2), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
  }
}

the 1mSec trigger pulse for the oscilloscope is required because as @gcjr states there is no way to determine when the data is transmitted and what the 1st value is

e.g. 0010000000111111 would look like

what if the 1st bit or several bits were a zeros ???

the final oscilloscope display in post 10 starts with 00, e.g. 0010000000111111

this example starts with six 0's, e.g. 0000001010101010

const rmt_symbol_word_t pulsePattern1[] = {     
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
};

I know where the 16bit sequence starts from the trigger pulse which is RMT synchronised with the 16bit sequence
in practice the start of the 16bit sequence has to be known, e.g. using start bits like RS232 or if transmitting frames with a start pattern such as the X25 synchronous start flag 01111110

and stop condition which is opposite of the start

If you have a board in mind, you might want to name it.

3 ms per bit is close to 300 baud serial.

[added thot] would you be okay with sending 2 bytes as Serial data with start and stop bits that get stripped off before Serial.read() gets the bits?

How close to 3000 micros per bit? I hope not close at all.

A function that takes an unsigned int and turns it into a data-driven 3-ms-intervals blinker should do the meat of what you want.

So here's the big question: is that all you want it to do?

Why? Because if you are okay with do one thing at a time, you can use delay() and other blocking code.

If you want to blink a status led to show that the program is working on time, that is more than one thing at a time. If you want to load input while outputting, there's another one. With non-blocking code you can add afterthoughts as functions, they all run concurrent. An Uno can do a few timed things more than 50 times a milli on average, even 20x / ms is fast, 50 micro loops. So who learns non-blocking has a key to smooth automating.

The usart also has a synchronous mode that does send start and stop bits. (Reading would be more difficult, since the sync mode needs a clock.) 3ms per bit might be slower than it can go, though.

For example: 16-bit data
int y = 0b1010101010101010;

Do you want to send them on Serial Monitor?

for(int i = 0; i < 16; i++)
{
       int n = bitRead(y, i);
       if(n == 0)
       {
            Serial.println('0');
       }
       else
       {
           Serial.println(n);
       }
       delayMicroseconds(3000);
}

synchronous, the S in USART means with a clock see RS-232 Timing signals.

a common UART, as well as USART generates start and stop bits to recognize the start of transmission

doesn't the Arduino Serial interface only support 8 bits.

one reason is because of the allowed 2% transmission rate error, more than that many bits (plus start and stop) would be difficult to recognize if the TX is +2 and the RX is -2%

it's not clear what the OPs goal is. could use some help from him/her. Could be to just send data optically, IR transmitter and receiver. But the OP seems to have neglected accounting for how to recognize the start of transmission.

could also be just to shift data into a 7-segment display and the OP is unaware of the shiftout()

as @westfw states synchronous mode needs a clock

1010000000111101 16 bit sequence 3mSec synchronous mode with 1.5mSec clock - read on falling edge - may look like (blue data yellow clock)

looking at first few bits

const rmt_symbol_word_t pulsePattern1[] = {
  // 1010000000111101 16 bit data 3mSec timing
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 0 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 1 },
  { .duration0 = 3000, .level0 = 1, .duration1 = 3000, .level1 = 1 },
  { .duration0 = 3000, .level0 = 0, .duration1 = 3000, .level1 = 1 },
};
const rmt_symbol_word_t pulsePattern2[] = {
  // 10101010101010101010101010101010 sync pulses 1.5mSec - read on falling edge
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
  { .duration0 = 1500, .level0 = 1, .duration1 = 1500, .level1 = 0 },
};

think that more information is required on the transmitter and receiver protocols etc

From OP's post #1, I have understood that he wants to output (on Serial Monitor or on a LED) 1-bit at a time with 3000 us delay in-between.

Given:
int y = 0b1010101010101010;
Serial.print(bitRead(y, 0)) sends bit-0 (which is 0) as a 10-bit frame (StratBit 00000110 StopBit) to Serial Monitor using UART Port.

where does he say serial monitor or LED?

the Serial interface can only send 8 data bits + start and stop. Start is alway low and stop high

When used in Synchronous mode, the AVR USART can be the "master" and generates the clock, so you don't need an external clock (you might lose the pin dedicated to clock.)
(Actually, I might have mis-spoken. The AVR USART has an "SPI Mode" that is documented separately from it's "Synchrnonous mode", and it is this "SPI Mode" that I'm thinking might be useful.)

Two 8bit frames transmitted in SPI mode without start or stop bits, is identical to transmitting a 16bit frame. The USART module has a byte of buffering, so this works even better than the dedicated SPI interface (which will skip a bit time between bytes when operated at high speeds.)
People have implemented video using the USART's SPI mode!

(Minimum speed seems to be about 2kbps, though; 512us per bit...)

If I remember correctly, the serial port sends start, stop, partity at transmit and strips them off at the receive.

It is correct only for "asynchronous" TX/RX mode.