Hello,
I make haptic devices that requires very low latency (<0.3ms or 3kHz+), but also very small packages of data, often < 64 bytes.
Normal APIs, especially in Windows, usually don't get the settings right to reach the low latency needed. Teensy 4 of course is
fundamentally awesome and with the latency_test.zip shared in this thread and earlier: https://forum.pjrc.com/index.php?threads/usb-to-digital-i-o-delay.7826/
we can manage to reach this goal, even faster.
So what I have done is to use that test code as basis for my own implementation, with #ifdefs for windows, mac and linux. And it works. Fast too.
But my code is far from being clean, and it has some sections for handling corner cases that I am not proud of. It includes blocking and non-blocking, fallback if a message was not read completely etc. There is also the topic of identifying the COM port (recently discussed in another thread) and the topic of try send/receive as part of initializing the device in an end-user application.
The key I think is that I fundamentally don't want to read/write serial, I want to send/receive messages (frames of data). I cannot use HID or similar because of <1ms requirement. Hence this approach of serializing my small packages. I also use raw ascii as encoding which is suboptimal (but human readable).
Now the question: is there any super-fast cross-platform serial API and corresponding Teensy lib for low-latency package communication, or any work in that direction? If not, is there any interest for such?
For reference I attach part of my code (can add more if interested).
My "packages":
Teensy side:
I make haptic devices that requires very low latency (<0.3ms or 3kHz+), but also very small packages of data, often < 64 bytes.
Normal APIs, especially in Windows, usually don't get the settings right to reach the low latency needed. Teensy 4 of course is
fundamentally awesome and with the latency_test.zip shared in this thread and earlier: https://forum.pjrc.com/index.php?threads/usb-to-digital-i-o-delay.7826/
we can manage to reach this goal, even faster.
So what I have done is to use that test code as basis for my own implementation, with #ifdefs for windows, mac and linux. And it works. Fast too.
But my code is far from being clean, and it has some sections for handling corner cases that I am not proud of. It includes blocking and non-blocking, fallback if a message was not read completely etc. There is also the topic of identifying the COM port (recently discussed in another thread) and the topic of try send/receive as part of initializing the device in an end-user application.
The key I think is that I fundamentally don't want to read/write serial, I want to send/receive messages (frames of data). I cannot use HID or similar because of <1ms requirement. Hence this approach of serializing my small packages. I also use raw ascii as encoding which is suboptimal (but human readable).
Now the question: is there any super-fast cross-platform serial API and corresponding Teensy lib for low-latency package communication, or any work in that direction? If not, is there any interest for such?
For reference I attach part of my code (can add more if interested).
My "packages":
C++:
constexpr int model_a = 1;
constexpr int model_b = 2;
struct device_to_pc_message {
int model{1};
int enc[6] = {0,0,0,0,0,0};
int error_code{0};
// Returns number of characters, also writes a trailing \0
int toChars(char *c) const {
return sprintf(c, "[%d,%d,%d,%d,%d,%d,%d,%d]\n",
model, enc[0], enc[1], enc[2], enc[3], enc[4], enc[5], error_code);
}
// Returns 0 if success, 1 if fail
int fromChars(const char *c){
return 8 != sscanf(c, "[%d,%d,%d,%d,%d,%d,%d,%d]",
&model, &enc[0], &enc[1], &enc[2], &enc[3], &enc[4], &enc[5], &error_code);
}
};
struct pc_to_device_message {
int ma[3] = {0,0,0}; // milliamps per motor
// Returns number of characters, also writes a trailing \0
int toChars(char *c) const {
return sprintf(c, "[%d,%d,%d]\n", ma[0], ma[1], ma[2]);
}
// Returns 1 if success, 0 if fail
int fromChars(const char *c){
return 3 == sscanf(c, "[%d,%d,%d]", &ma[0], &ma[1], &ma[2]);
}
};
Teensy side:
C++:
// returns 0 if success, error code else
// start_pos is where [ is located
int await_message(size_t& start_pos){
bool start_found = false;
int comma_count = 0;
for (size_t buf_pos = 0; buf_pos < buf_len; buf_pos++) {
while (Serial.available() <= 0){
// wait
}
char c = Serial.read();
in_buf[buf_pos] = c;
if (c == ']'){
if(!start_found)
return 256; // no start character before end character
if(comma_count != 2)
return 512; // wrong legth of array, should be [0,0,0]
// Do we still have waiting messages? Discard and report error
while(Serial.available()){
c = Serial.read();
if(c!='\n' && c!='\r' && c!='\0')
return 8; // trailing characters in message
}
return 0;
}
if (c == '['){
start_found=true;
start_pos = buf_pos;
}
if (c == ','){
comma_count++;
}
}
return 128; // buffer full
}
void loop() {
loop_count++;
memset(in_buf,0,buf_len+1);
memset(out_buf,0,buf_len+1);
// receive from serial
size_t start_pos;
int receive_error = await_message(start_pos);
if(receive_error){
return_last_values(receive_error);
return;
}
// parse
pc_to_device_message pc_msg;
if(pc_msg.fromChars(in_buf+start_pos)==0) {
return_last_values(1024); // Parse error
return;
}
// do stuff
device_to_pc_message out_msg;
// fill out_msg with data..
int len = out_msg.toChars(out_buf);
Serial.write(out_buf, len);
Serial.send_now();