I’ve been working with variadic patch nodes and the custom output types to try to implement a parallel to serial converter/byte serializer patch. The idea is that you connect up any number of sources of bytes to a parallel set of inputs, and then there is a serial output which allows you to send them one at a time over say the UART or i2c. this is the general idea:
There is a variadic parallel to serial input that takes in bytes and emits a custom “self” type which is the internal “List” type. The nodes store an input byte in the state, converts it to a single element array in the state which holds a pointer to that byte, turns that into a single element list of a pointer to byte and emits that. The next stage then concatenates that single element with the list of X size coming in on the bus from the left.
Code for byte to list conversion is like:
using Type = List<uint8_t*>;
struct State {
uint8_t byte;
uint8_t* byte_arr[1];
PlainListView<uint8_t*>* byte_view;
};
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
State* state = getState(ctx);
if (isSettingUp())
{
state->byte_view = new PlainListView<uint8_t*>(state->byte_arr, 1);
}
state->byte = getValue<input_IN>(ctx);
state->byte_arr[0] = &state->byte;
Type out = Type::List(state->byte_view);
emitValue<output_OUT>(ctx, out);
}
Code for the concatenation is like:
struct State {
ConcatListView<uint8_t*> view;
};
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
State* state = getState(ctx);
auto list1 = getValue<input_IN1>(ctx);
auto list2 = getValue<input_IN2>(ctx);
state->view = ConcatListView<uint8_t*>(list1, list2);
emitValue<output_OUT>(ctx, List<uint8_t*>::List(&state->view));
}
And then code for the serialization is like:
struct State {
Iterator<uint8_t*>* it;
};
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
if (!isInputDirty<input_UPD>(ctx))
return;
State* state = getState(ctx);
if (isSettingUp())
{
state->it = new Iterator<uint8_t*>(Iterator<uint8_t*>::nil());
auto byte_list = getValue<input_P2S>(ctx);
*state->it = byte_list.iterate();
}
if (!(*state->it))
{
auto byte_list = getValue<input_P2S>(ctx);
*state->it = byte_list.iterate();
} else {
//dereference pointer-to-iterator stored in the State, dereference
//iterator to get byte pointer and post-increment the iterator, dereference
//byte pointer and emit a byte. Yuck!
emitValue<output_BYTE>(ctx, *(*(*state->it)++));
emitValue<output_DONE>(ctx, true);
}
}
Edit: The problem is in the code above, an Iterator type doesn’t have a postfix increment operator overload, only a prefix increment overload. Surprised it even kindof worked at all! Instead of that pointer-mess I think I’ll rewrite to do like auto& it = *(state->foo); to take a reference to the iterator (Iterators are non-copy-assignable) to make the code easier to reason about
Which generates an iterator for the “bus” list input and pushes bytes one at a time, then loops around. So ideally if I have a 4 input parallel-to-serial node up top with input values for the bytes in hex 0x41, 0x42, 0x43, 0x44 the UART at the bottom will spit out “ABCDABCDABCD…” etc. over and over.
So this kind of works but there are also some problems/improvements that could be made. There’s a bug such that the UART seems to be printing an extra character e.g. “ABCDDABCDDABCDD…” etc. instead of what’s expected, not sure what the problem is exactly. The method I’m using doesn’t seem to be particularly memory-efficient, the example patch in the image with a 4 byte “bus” and a UART takes over 5kb of program memory and nearly 400 bytes of SRAM on an Arduino Nano, the memory usage grows rapidly with increasing variadic byte inputs.
There is probably a better way to use the various list functions available like the one for list folding to accomplish this sort of thing and the fact that nodes already store their inputs in memory, or are constexpr, so I don’t think I should also have to store the input byte in the state, but I don’t really understand how to use that function! Also would be nice to make the inputs generic rather than just bytes. Additionally feeding parallel-to-serial-inputs from the single byte output of another parallel-to-serial-output doesn’t really work at all. You can connect up these “buses” and nodes in interesting ways “on paper” at least but anything other than just an input block into an output block you’re most likely to just get a processor reset.
Any suggestions on how to address primarily the off-by-one error or other improvements/issue would be appreciated.
ParallelToSerial.xodball (11.4 KB)