Need join-pulses node (eventually)


#1

The problem: What if we need the result of BOTH of 2 asynchronous operations (e.g. xod-dev/esp8266-mcu/connect and xod/common-hardware/dht2x-thermometer), so we can perform a 3rd operation (LED, Servo, radio-write, etc).

While I2C (and other */read-byte nodes) are not currently asynchronous, a few others are, including delay.

As far as I can tell, there is no join-pulses (wait-for-all-pulses, etc.) node.

Here is a contrived example of 2 asynchronous operations, with a 3rd that wants to use BOTH results.

I put an any node where a join should be, to show what we would need.

Also, here’s the project need-join.xodball (9.1 KB)
because it might be easier to see if you open it in XOD.

The join would fire a pulse (and reset) only when ALL input pulses have (eventually) fired. It only counts the first pulse on a particular input, until reset. I imagine using bits to hold each signal, and when all are set, fire the output pulse and clear the bits. Of course that might limit the number of inputs to 32 or 64.


#2

Yes, such nodes would be very handy. Could you think about exact variations necessary? Beside the join-pulses which works like:

IN1 ---x-------x----------x---
IN2 -------x---x---x---x------
OUT -------x---x----------x---

This one might be useful for cases when “I want this stuff to happen only after ESP is initialized”:

SET -----------x----------x---
 IN -------x---x---x---x------
OUT -----------x---x---x------

Both nodes should provide an RST pin as well to reset to the initial state.


#3

first-pulse is a complement: pulse on the first input in the set, wait for reset to do it again. i.e. “first through gate”. For the examples below, which are complex, probably simpler versions without all enable or once, etc. Otherwise, I have not thought much! Anyone else?

Sketch 1: wait-all

“kitchen sink” version. All the controls. ENABLE seemed like an idea? Or is that a bad mixing of pure/impure?

Sketch 2: wait-after

Same, but only waits once after a SET: i.e. start waiting on SET.
Screenshot%20from%202019-01-14%2016-18-12

Tricks

If you send continuously to RST, then all pulses must arrive in one transaction! If you use a delay you can require all pulses to arrive within a time-window (looks like we need a first-pulse node!)

I don’t know how to use variadic input-pins with .cpp. Will have to look for an example. I don’t know when I’ll work on this.


#4

The fundamental nodes are :

  • parallel-to-serial (p-to-s as shorthand) : each pulse on an input sets a corresponding bit. Emits the bit-set as a number (23 bits maximum!) when a new bit is set. Only fires once, set to start/keep accumulating. rst to reset to 0 (no emit?). Also emits a mask of all bits set so you can compare all vs so-far.
    • I think you could build this out of a set-bit primitive? Not sure about the rst & rst behaviour.
  • time-of-pulses “class”: keeps track of when each input was seen. This is the memory-expensive version of the above. The output is a custom-type which is that table, and the bit-mask. Like the above, must be set each time, and has a rst which clears the table. Its methods/helpers:
    • x-clear-entry-i : clears just one entry in the table. Has a done pulse.
    • x-to-serial : like p-to-s, outputs the bits which are set as a number.
    • x-entry-expired : takes i and msec, emits a older pulse and an older-i value if any entry in the time-of-pulses is more than msec old. Emits younger otherwise.

A couple of “helper” nodes:

  • pulse-or-timeout : When started and give a msec, passes one subsequent pulse. If the msec expires with no pulse, emits a timeout pulse. Must be rst after either emit. Thus, many of the following can be …or-timeout.
  • all-in-the-last-msec : passes a pulse, clears table entries if they are old, reset timer on each input, fires timeout if msec expires w/no input. For combining.
  • count-bits : counts the number of 1 bits in a number.

Derived nodes:

These have set and rst, like p-to-s. So you have to set each time these recieve an input pulse, and rst to reset.
Each of these should have a convenience version: they continue to handle inputs without have to set each time. And, possibly self-rst so they “repeat”. Thus you could use the convenience continously & repeatedly, which is common.

  • wait-all (analogous to and): fires when all inputs have fired since last rst. Built from p-to-s, if bitwise-and(serial, mask) == mask (i.e. all bits set), fire.
    • convenience version does self-set and self-rst
    • combines nicely with or-timeout
    • combines nicely with in-the-last-msec
  • count-inputs (analogous to ?): emits count on each update from p-to-s, how many of the inputs have seen a pulse? Built from p-to-s and count-bits.
    • combines with or-timeout
    • could combine with in-the-last-msec (timeout if not at least n counts)
    • convenience version does self-set, but not self-rst. counts up to “full”. rst to count up again.
  • at-least-n : fire when n inputs seen. Built from count-inputs.
    • convenience version does self-set, but not self-rst.
    • maybe a every-n which is this plus self-set, and self-rst?
    • combines nicely with or-timeout
    • combines nicely with in-the-last-msec
  • at-least-one or only-on-first (analogous to or) : somewhat like any but with rst and rst, and never fires on 2nd. same as at-least-n for n=1
    • convenience version has self-set, but not self-rst.
    • the current any is this node with self-set, and self-rst.
    • combines nicely with or-timeout
    • could build a in-the-last-msec version w/using the time-of-pulses object:any-within-msec
  • any-so-far : output yes or no pulse on ask and the bits
    yes: pulse-on-true if p-to-s.number != 0
    no: pulse-on-false if p-to-s.number == 0
  • pulse-gate (“pass next pulse”): inputs of allow and pulse. allows one pulse after each allow. built from wait-all, allow->set & rst, pulse to p1.
  • block-pulses : the above + input block: pass pulses until a block. built from wait-all : block is rst. self-set. have to set after a block to turn back on.

I’m thinking that set should be called step, because it means “do one”.


#5

The above helped me think through the possible primitives, then I realized I could implement set-bits (called parallel-to-serial above), and thus wait-all in pure xod.

pulse-to-bits.xodball (50.0 KB)

I didn’t turn wait-all into a reusable node, I just made an example patch so far.

Does efficiency concerns warrant turning this into .cpp?