| 195 | |
| 196 | template<typename SerialIn> |
| 197 | fl::optional<fl::string> readSerialStringUntil(SerialIn& serial, char delimiter, char skipChar, fl::optional<u32> timeoutMs) { |
| 198 | // Follows Arduino Serial.readStringUntil() API - blocks until delimiter found |
| 199 | fl::sstream buffer; |
| 200 | |
| 201 | u32 startTime = fl::millis(); |
| 202 | |
| 203 | // Read characters until we find delimiter or timeout |
| 204 | while (true) { |
| 205 | // Check timeout (only if timeout is set) |
| 206 | if (timeoutMs.has_value()) { |
| 207 | if (fl::millis() - startTime >= timeoutMs.value()) { |
| 208 | // Timeout occurred - return nullopt |
| 209 | return fl::nullopt; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | // Try to read next character |
| 214 | int c = serial.read(); |
| 215 | |
| 216 | // Handle -1 (no data available) like Arduino's timedRead(): |
| 217 | // Keep trying until timeout (or forever if no timeout set) |
| 218 | if (c == -1) { |
| 219 | // Brief 1us yield instead of 1ms delay for fast USB CDC polling. |
| 220 | fl::delayMicroseconds(1); |
| 221 | continue; |
| 222 | } |
| 223 | |
| 224 | // Found delimiter - complete |
| 225 | if (c == delimiter) { |
| 226 | break; |
| 227 | } |
| 228 | |
| 229 | // Skip specified character (e.g., '\r' for cross-platform line endings) |
| 230 | if (c == skipChar) { |
| 231 | continue; |
| 232 | } |
| 233 | |
| 234 | // Valid character - add to buffer |
| 235 | buffer << static_cast<char>(c); |
| 236 | } |
| 237 | |
| 238 | // Convert to string and trim whitespace |
| 239 | fl::string result = buffer.str(); |
| 240 | result.trim(); |
| 241 | return result; |
| 242 | } |
| 243 | |
| 244 | template<typename SerialOut> |
| 245 | void writeSerialLine(SerialOut& serial, const fl::string& str) { |
no test coverage detected