Digital signal repeater

Digital I/O pulse repeater

This node would read the duration of a short pulse on a digital input pin. The pulse is then copied to a digital output pin after a specified delay. The delay is based on the duration of the input pulse multiplied by a number.

Inputs

  1. port…board port to read input signal from
  2. input-number…“IN” multiplier to calculate delay before outputting signal
  3. input-boolean…“H/L” High (true) or Low (false) Pulse Measure
  4. input-pulse… “UPD” Triggers new read

Outputs

  1. port…board port to write output signal to
  2. output-number…“Time” duration of input pulse
  3. output-pulse…Fires on writing complete

    Extra info: rationale, edge-cases, details

This node is intended to intercept a trigger signal from teeth on a rotating wheel and to then output the same signal after a specified delay. (In an combustion engine crank signal pulses control ignition timing. By delaying the output the spark timing can be modified.)

In the case of zero delay then the output pulse could start immediately after the inlet pulse finishes.

block

This is my guess at what the layout would look like. The pulse-in node below by robertspark/components has the main elements for the pulse measurement. Suggestions on how to scaling the delay and writing the output signal would be much appreciated.

#pragma XOD error_raise enable

struct State {
};

{{ GENERATED_CODE }}

void evaluate(Context ctx) {
    if (!isInputDirty<input_UPD>(ctx))
        return;

    const uint8_t port = getValue<input_PORT>(ctx);
    if (!isValidDigitalPort(port)) {
        raiseError(ctx);
        return;
    }

    bool highLow = getValue<input_HU002FL>(ctx);
    uint8_t value = 0;
    if (highLow == 1)
    {
        value = 1;
    }
    else {
        value = 0;
    }

    bool mSecs = getValue<input_mU002Fu>(ctx);
    auto multi = 0.0;
    if (mSecs == 1)
    {
        multi = 0.001;
    }
    else {
        multi = 1.0;
    }

    emitValue<output_TIME>(ctx, pulseIn(port, value) * multi);
    emitValue<output_DONE>(ctx, 1);
}

This arduino sketch approximates what I would like to do

byte PWM_PIN = 3;
byte PWM_POUT = 12;
int pwm_value;
int offset = 2; //this delays the output

void setup() {
pinMode(PWM_PIN, INPUT);
pinMode (PWM_POUT, OUTPUT);
}

void loop() {
pwm_value = pulseIn(PWM_PIN, LOW);
delayMicroseconds (pwm_value * offset);
digitalWrite (PWM_POUT, LOW);
delayMicroseconds (pwm_value);
digitalWrite (PWM_POUT, HIGH);
}

It seems to work ok for a typical crank sensors. The trace below is for 5% on @ 1 kHz frequency with a 2 x pulse duration offset for the repeated signal.

I had a go at making a xod node based on the arduino sketch.
image

image
and included the following for the not implemented… ( I know nothing about c++ )

#pragma XOD error_raise enable

struct State {
};

{{ GENERATED_CODE }}

void evaluate(Context ctx) {
    if (!isInputDirty<input_UPD>(ctx))
        return;

    const uint8_t port = getValue<input_crank>(ctx);
    if (!isValidDigitalPort(port)) {
        raiseError(ctx);
        return;
    }
emitValue<output_DONE>(ctx, 0);
    //const uint8_t port = (13);
    bool highLow = getValue<input_HU002FL>(ctx);
    uint8_t value = 0;
    uint8_t toothtime = 0;
    uint8_t retard = 0;
    uint8_t PSI = 0;
    if (highLow == 1)
    {
        value = 1;
    }
    else {
        value = 0;
    }
    //noInterrupts();
    toothtime = (ctx, pulseIn(port, value));
    emitValue<output_TIME>(ctx, (toothtime));
    
    PSI = getValue<input_PSI>(ctx);
    emitValue<output_offset>(ctx, (PSI * toothtime));
    delayMicroseconds(PSI * toothtime);
    pinMode(7, OUTPUT);
::digitalWrite(7, false);
  delayMicroseconds (toothtime);
  ::digitalWrite(7, true);
emitValue<output_DONE>(ctx, 1);
    //Interrupts();
    }

The input pulse and offset are calculated ok but for some reason the output pulse is registered for only 1 in ~5 of the input pulses.

Is there a way to fix this?
Thanks

I think part of the problem is the UPD pin. It tells the node to ignore all other input unless there had been a pulse on the UPD pin. You want node to fire every time the physical input pin changes. You may need to create a separate read node and pass the value in to this node since nodes only “fire” when their input changes.

Thanks for the help gweimer, I will experiment some more.

The thing is that e the node reads measures the input pulses correctly but the corresponding output pulses are not registered. I guess it is something got to do with how many times the digitalwrite function can be called per second as when using lower input signal frequencies all output pulses are recorded. (as seen below)

There may be a way to write to the port diectly without using digitalwrite?

I’m guessing the problem is seeing the input pulses, not sending the output pulse.

Thanks gweimer, your explanation is more likely.
You mentioned making a seperate read node to capture all inputs.

Any advice on how this might look like?

Digital-read linked to your node. Your node will need to use a Boolean pin instead of Port number.

Hi I tried adding digital-read to the input of the node.

image

It did not update probably as due to the digital read also having an UPD input. Is this the general idea of what was suggested?

It looks like the pulseIn function records ok without having to toggle UPD as long as the frequency is less than 200 Hz ( ~5 mS). Above 200 Hz it starts to miss pulses.
It seems that in XOD pulseIn can run 200 times a second where as in Arduino it can run thousands of times a second.
Would attaching interrupts be a better way to record high frequency pulses in XOD?

.

If crank is boolean like you have here, you can drop the UPD pin on your node. Node will only “fire” when one of the input pins change (like value for crank).

By turning off the watch windows in debug the speed at which the input port can be sampled increases from 200 Hz to 1000 Hz before pulses are missed.

I will try to use interrupts instead of pulse in to see if the port read frequency can be increased further.