Skip to content
firepick1 (localhost) edited this page Sep 29, 2015 · 1 revision

Flow Control

In general, FireStep uses a simple line protocol for flow control. A JSON request occupies a single line. The corresponding JSON response is also a single line by default (except when pretty-printed) and is sent when FireStep is ready for the next request. FireStep operates on a "one command at a time" basis. You can make those commands large, but multiple commands are not buffered. In fact, any serial input received while a command is executing actually aborts command execution. During normal operation, clients are therefore expected to send a command and wait for the response, which will be sent after command execution completes.

Each FireStep JSON request is terminated by a line feed (i.e., "\n" or "\r\n"). JSON request are executed immediately upon receipt of the line feed and will execute till done or cancelled by serial input. Note that JSON request are not buffered--they are actually terminated by new serial input. Upon request completion or cancellation, FireStep will send a JSON response patterned after the JSON request. For normal operation, host computers should therefore wait for the JSON response from FireStep before sending the following request. Since empty request are ignored, this means that a request can be cancelled at any time by sending a line feed.

FireStep is much less "chatty" than other stepper drivers, which require a multitude of tiny requests (e.g., G0X1) to laboriously specify movement in vast detail. In contrast, FireStep supports complex motion with single requests (e.g., dvs) that rely on the flexibility of JSON to specify extensible parameters. This design frees the MCU from the need for complex path planning and serial input buffering, allowing it to focus on its primary job of sending stepper pulses quickly and steadily.

To illustrate how FireStep eliminates command chattiness, here is the FireStep command to auto-calibrate FirePick Delta with a six hexagonal Z-probes:

{"pgmx":"cal-fpd-home-medium"}

If you are implementing a FireStep driver, there are some additional considerations documented below.

Single-Line Response Protocol

The easiest flow control for drivers to implement is the single-line response protocol. This protocol intentionally avoids multi-line responses generated by commands such as {"sysjp":true} or {"msg":"hello"}. It's great for simple projects.

Interruptable Single-Line Response Protocol

Clients can interrupt executing FireStep commands by sending a single linefeed (LF) in a request. FireStep echoes interrupting linefeeds as well, and the client should consume the echoed LF to stay in sync.

Interruptable Multi-line Response Protocol

Some FireStep commands such as {"msg":"hello"} result in multiple lines of output. If a client expects only a single line of response for each command sent, it will get out of sync with multi-line responses. To handle multi-line responses, FireStep always sends a three character }-SPACE-LF sequence at the end of a final response. Multi-line response clients should therefore keep reading serial response lines until detecting the }-SPACE-LF final three character sequence.

Here is the code from FireStepSerial.cpp that handles interruptable multi-line responses with a 40-second timeout:

int FireStepSerial::executeCore(const std::string &request, std::string &response) {
	int rc = 0;
	usb.write(request);
	response = usb.readln(msResponse);
	if (request.compare("\n") == 0) { // simple LF should be echoed
		for (int i=0; response.size() == 0 && i<4; i++) {
			LOGDEBUG1("FireStepClient::console() wait %ldms", (long) msResponse);
			response = usb.readln(msResponse);
		}
	} else { // JSON response should end with }-SPACE-LF
		int nRead = 0;
		for (int i=0; response.find("} \n")==string::npos && (nRead != response.size() || i<4); i++) {
			nRead = response.size();
			LOGDEBUG1("FireStepClient::console() wait %ldms", (long) msResponse);
			response += usb.readln(msResponse);
		}
	}

	return rc;
}