Overview

This is a working draft for the 3rd generation protocol for the Sanguino to drive a RepRap machine.  There are a few major criteria that should be met:

1. lightweight
2. robust
3. completely eliminate line-segment pausing
4. support multiple toolheads / extruders
5. expandable / hackable

Development Activity

Firmware
Sanguino firmware development is happening in the RepRap subversion repository at:
/reprap/trunk/reprap/firmware/Sanguino/Sanguino3G

Host
ReplicatorG development is happening in the ReplicatorG subversion repository at: /replicatorg/trunk/src/replicatorg/app/drivers/Sanguino3GDriver.java

Topics To Be Discussed / Decided


1. Stepper Interrupt Timing Values

MK: I would expect it to be enough with 16 bits for the dda speed. Even if we go down to 100 steps/mm, we're talking ~0.15 mm/second.
ZH: I think I am switching back to advocating the direct mapping to atmega168 timer1 parameters.  Please take a look at the code located at:
http://svn.reprap.org/trunk/reprap/firmware/Arduino/GCode_Interpreter_Experimental/timer1_interrupt_routines.pde

In case you don't read that link, here is the gist:

We can generate an interrupt with Timer1 by setting it to generate an interrupt when it counts up from 0 to the number we specify.  This interrupt can contain arbitrary code (such as a DDA algorithm.)  The timer has two main parts:  The prescaler, and the actual timer variable.  The prescaler determines the speed or resolution of the counter, and the timer value determines the frequency, (or the time between) of the interrupts.

For example, here are the prescaler values:

    //from table 15-5 in that atmega168 datasheet:
    // we're setting CS12 - CS10 which correspond to the binary numbers 0-5
    // 0 = no timer

    // 1 = no prescaler (tick size = 0.0625 usecs)
    // 2 = clock/8 (tick size = 0.5 usecs)
    // 3 = clock/64 (tick size = 4 usecs)
    // 4 = clock/256 (tick size = 16 usecs)
    // 5 = clock/1024 (tick size = 64 usecs)

Then, the timer1 variable is just an int16 (aka what number do we trigger an interrupt at when we count up to it at the prescaler speed)

What this means is that we can set the precision and handle a large range of timings and have both large and small scales... all in 3 bytes:  one for the prescaler, and two for the timer variable.

As my final argument: this protocol is specific to the Sanguino hardware, so we should write it to take advantage of the low level hardware as much as possible.  Additionally, it is fairly trivial to convert from this format to microseconds, and vice versa.  The code is already written, and it would be easy to code into the driver, port to another architecture (16 bit timers w/ prescalers are pretty common, i imagine), and provides good resolution.
MK: I dropped out for a week (went on vacation), but back in business now. Just before I left, I wrote the code for converting from microseconds to interrupt parameters, and it's pretty straight-forward (not tested though). I thus still feel that these prescaler/tick parameters are a bit obfuscating (see below for code).
AM: if you have code for this, I'm happy to go back to the 32-bit microsecond version.  I'm changing it back in the protocol for now.
ZH: Well, it looks like this is not settled yet, lol.  Okay, here's my counterpoints:
1. This is a Sanguino specific protocol.  We should take full advantage of the Sanguino hardware.
2. The sanguino runs at 16Mhz, which means the timer1 resolution is 62.5 nanoseconds.  If we use microseconds as our lowest level unit, then we are only getting 1/16th of the precision.
3. The code is really really simple.  We'll document it completely.  It will be trivial to convert from a prescaler/ticks value to microseconds and vice versa.  We've spent more time discussing than it would take to code up a solution.  PS: its already coded!
4. Sending direct prescaler/ticks means one less step for the microcontroller to take during time-sensitive line segment changes.  I know its not a lot, but the better we make it, the better the prints turn out.  I feel that reducing the delay between segments should take higher priority than code complexity, especially when the code is really not too difficult (and especially when its a question of: put it on the host, or put it on the micro)

3. Timeouts.
RN: we need to add some timing information.  How long should the sender wait for a response before resending the packet?  We should also point out that if the sender is trying to send a mal-formed packet, the reprap may send a response packet at any time, and if it gets one, it should stop sending.

ZH: Yes, we'll be implementing some sort of timeout / retry system shortly.

Also, what happens when the action command buffer fills up?  According to the Response packet definition below, action commands have an empty payload, so there's no way to say that the buffer overflowed. 

ZH: When the action command buffer fills up, the reply packet returns a 'action buffer full' response.  There are two different buffers at work here:  the top level serial buffer, and a 'action' buffer for received and buffered commands.  It will be up to the host to ensure that the serial buffer is never too full. (128 bytes).  If the action buffer is too full, then the host simply waits and tries again.

Network Overview

The network can be divided into two separate parts:  The Host Computer <-> Master uC and Master uC <-> Slave uC portions.  There is only one Master uC but there can be many Slave uCs, all listening on the same hardware line.

The two networks both share the same protocol, but it is important to note that they are separate, and in many cases the Master/Slave network commands are sent through the Host/Master network and are then passed on to the relevant slave.  The Master may also call certain Slave commands directly (ie: Slave #3, are you ready?)

Protocol Syntax

Protocol Overview

Commands will be sent in packets. All commands are query/response.  The master in each pair will always initiate communications, never the slave.  All packets are synchronous; they will wait for a response from the reprap before sending the next packet.  The firmware will continue rendering buffered commands while receiving new commands and replying to them.

Command Buffering

To ensure smooth motion, as well as to support print queueing, we'll want certain commands to be queued in a buffer.  This means we won't get immediate feedback from any queued command.  To this end we will break commands down into two categories: action commands that are put in the command buffer, and query commands that require an immediate response.  In order to make it simple to differentiate the commands on the firmware side, we will break them up into two sets: commands numbered 0-127 will be query commands, and commands numbered 128-255 will be action commands to be put into the buffer.  The firmware can then simply look at the highest bit to determine which type of packet it is. 

Every packet gets precisely one response packet.  Action commands can be batched and sent as a single packet, which will get a single response (error) code.   Query commands must be sent in their own packet.  Only action or query commands can be sent in a single packet.  The first byte of the command payload will determine the nature of the entire packet.  Thus, you cannot mix query and action commands in a single packet!

Command Types

Range
Type
Description
0-127
Query Commands
Commands that can be executed immediately, and thus do not need to be queued.  Generally return pre-calculated information and should be fast.
128-255
Action Commands
Commands that must be executed one at a time, and in a particular order.  These commands should be buffered for smooth operation.  Many of there commands may take up a large amount of time to complete.

Packet structure

Command packets are sent from the Master to the Slave.  This can either be from the Host CPU to the Master uC or from the Master uC to the Slave uC.  It consists of a two-byte header, a variable-length payload, and a 1 byte CRC footer.


The packet consists of:

 

Index
Name
Details
0
Start byte
This byte always has the value 0xD5 and is used to ensure synchronization.
1
Length
This byte indicates the length of the payload (excluding the header and CRC) in bytes.
2...
Payload
The packet payload.  This is appended to the buffer and either immediately executed and removed (if a query type) or buffered for execution (if an action type).
Length+2
CRC
This is the 8-bit iButton/Maxim CRC of the payload.



Command length is implicit in the command structure; no explicit separator is needed.

Command structure

The payload of a packet consists of one or more commands.  Each command contains a header, as follows:


 

Index
Name
Details
0
Command
The command code to send to the device.
1-N
Arguments
Arguments to this command (details below as 'command payload').


Response Packets 

Response packets look just like command packets.  The only difference is the payload is always guaranteed to contain a response code as the first byte, as described below.

Response Packet Payload Structure


Index
Name
Details
0
Response Code
This is a standard response code that is included with all packets and is described below.
1-N
Response Data
This contains response-specific data.  Length is implicit based on the command being responded-to.

If the command was a non-request command, the response data is always empty.

Response Code Values


Response Code
Interpretation
0
Generic error, packet discarded.
1
Success.
2
Action buffer overflow, entire packet discarded.
3
CRC mismatch, packet discarded.
4
Query packet too big, packet discarded.
5
Command not supported.
6
Success; expect more packets.  Used when a single reponse packet cannot contain the entire message to be retrieved.


Data Formats

The protocol is a byte-oriented protocol.  Payloads may contain various information in a variety of formats, but will generally be limited to the formats described below.  Multi-byte datatypes will always be transmitted in Little-endian mode.  Remember, this means that the least significant byte ("littlest") byte is sent first!  For a more in-depth discussionof little-endian storage, see: http://en.wikipedia.org/wiki/Endianness#Little-endian

Various Data Types

Type
Range
uint8
0 to 255
uint16
0 to 65,535
int16
-32767 to 32,767
uint320 to 4,294,967,295
int32
-2,147,483,647 to 2,147,483,647

    Master Microcontroller Commands (3-axis controller)

    0 - Get Version - Query firmware for version information

        This command allows the host and firmware to exchange version numbers.  It also allows for simple automated discovery of the firmware.  Version numbers will always be stored as a single number, Arduino / Processing style.  Thus, the versions will be 0 to 65535.

        Payload (2 bytes) - host version
            uint16: Host Version.

        Response (2 bytes) - firmware version

            uint16: Firmware Version

    1 - Init - Initialize firmware to boot state

       
    Initialization consists of:
      • resetting current position to 0,0,0
      • clearing command buffer
      • setting range to eeprom value (if it exists)  otherwise its 0.

        Payload (0 bytes)
        Response (0 bytes)
     
    2 - Get Available Buffer Size - Determine how much free memory we have for buffering.

        This command will let us know how much buffer space we have available for action commands.  It can be used to determine if and when the buffer is available for writing.  If we are writing to the SD card, it will generally always report the maximum number of bytes available.

        Payload (0 bytes)
      
        Response (4 bytes)
            uint32: number of bytes available in the command buffer.

    3 - Clear Buffer - Empty the command buffer

       
    This command will empty our buffer, and reset all pointers, etc to the beginning of the buffer.  If writing to an SD card, it will reset the file pointer back to the beginning of the currently open file.  Obviously, it should halt all execution of action commands as well.

        Payload (0 bytes)
        Response (0 bytes)

    4 - Get Position - Get the current position of the tool

        Useful for determining current position of the toolhead.  It is up to the host software to add or subtract the toolhead offset to determine the actual position of the toolhead.  It will also return the status of the various endstops for diagnostic information.

        Payload (0 bytes)

        Response (13 bytes)

            int32: current x position, in steps

            int32: current y position, in steps
            int32: current z position, in steps
            uint8: bits marked for endstop status: (7-0) : | N/A | N/A | z max | z min | y max | y min | x max | x min |

    5 - Get Range - Get the maximum range of travel on all axes.
        
       
    When find axes maximums is called, it will record the maximum as the range.  internally, it will keep track of its range and this function will simply report that internal value. We cannot assume that there are endstop switches for all axes at both min and max positions. The firmware will know this, so the "Find Axes min/max" commands may be partially no-ops in this case. The firmware should always respect this range and never go beyond it.

        Payload (0 bytes)
       
        Response (12 bytes)
            uint32: x range, units in steps
            uint32: y range, units in steps
            uint32: z range, units in steps

    6 - Set Range - Set the maximum range of travel on all axes.

       
    This command tells the firmware what its maximum range of travel is.  The firmware should always respect this. This should be recorded into the eeprom for future usage.

        Payload (12 bytes)
            uint32: x range, units in steps
            uint32: y range, units in steps
            uint32: z range, units in steps
       

        Response (0 bytes)

    7 - Abort Immediately - Stop Machine, Shut Down Job Permanently

        This function is intended to be used to terminate a print during printing.  Extruder and accessories off, steppers disabled, everything shuts down.

        Payload (0 bytes)
        Response (0 bytes)

    8 - Pause / Unpause - Halt Execution Temporarily

        This function is intended to be called infrequently by the end-user in order to make build-time adjustments immediately and easily.  No buffers or other run-time variables are changed.  The pause command should also prompt the firmware to send a pause command to the toolhead.

        On pause, it stops all movement, stops extrusion, moves up a 'safe z' amount, and waits for new commands.
        On unpause, it lowers to original Z, optionally restarts extrusion, and resumes movement.
        agm: Currently we do not do the "safe Z" backoff.  I'd like to introduce that as a parameter instead.

        Payload (0 bytes)
        Response (0 bytes)

    9 - Probe - Move in Z axis (negative) until probe hits something

       
    This command will wait until the buffer is clear to avoid conflicts.  It will also prevent any buffered commands from executing for the duration of the probe.

        Payload (6 bytes)
            uint32: feedrate (in microseconds between steps) (max 71.58 minutes)
            uint16: timeout (in seconds before abort)  (max = 18 hours) (default = 1 minute)

        Response (4 bytes)
            uint32: Z position when probe has been triggered.

    10 - Tool Query - Query a tool for information

        This command is for sending a query to the tool.  The master firmware will then pass the query along to the appropriate tool, as well as passing the response back as well.  This allows the tool specific commands to be developed independently between.

        Payload (2 + N bytes)
            uint8: the index of the tool to query
            uint8: the query command for the tool.
            N bytes: command specific payload, variable size.

        Response (0-N bytes)

            0-N bytes: the command specific response, if any.
     
    11
    - Is Finished - See if the machine is currently doing anything

        This command lets us know if the machine is finished executing its current command queue.

        Payload (0 bytes)
      
        Response (1 byte)
            uint8: 0 if still in progress; 1 if finished.


    12 - Read from EEPROM
       
    Read the specified number of bytes from the given offset in the EEPROM and return it in the response packet.  The maximum read size is 32 bytes.

        Payload (3 bytes)
            uint16: the offset of the read
            uint8: the number of bytes to return, N.  N <= 16.

        Response (N bytes)
            N bytes: the data read from the EEPROM.

    13 - Write to EEPROM
       
    Write the given bytes to the EEPROM starting at the specified location.  The maximum payload size is 16 bytes.  WARNING: this operation can be potentially slow (about 3.3ms * N) and will block all other operations.  It's not recommended to write to the EEPROM while a build is in progress.

        Payload (3+N bytes)
            uint16: the offset of the write
            uint8: the length of the data
            N bytes: the data to write

        Response (1 byte)
            uint8: the number of bytes successfully written.

    14 - Capture to file
        Capture all subsequent commands up to the next "end capture" command to a file with the given name on the SD card.  The file will be stored in the root of the fat16 filesystem on the SD card.  The maximum file name length permitted is 12 characters, including the '.' and file name extension.  Welcome to the brave new world of MS-DOS 2.0!

        Payload (N bytes)
            N bytes: the name of the file in ascii, terminated with a null
        Response (1 byte)
            uint8: response code
    • 0 if operation was successful
    • 1 if no SD card was present
    • 2 if SD card init failed
    • 3 if partition table could not be read
    • 4 if filesystem could not be opened
    • 5 if root directory could not be opened
    • 6 if SD card is locked

    15 - End capture
        Complete an ongoing file capture.

        Payload (0 bytes)
        Response (4 bytes)

            uint32: the total size of the capture in bytes

    16 - Playback capture
        Play back a file containing captured data to the makerbot.  The maximum file name length permitted is 12 characters, including the '.' and file name extension.  While the makerbot is in playback mode, it will only respond to pause, unpause, and stop commands.

        Payload (N bytes)
            N bytes: the name of the file in ascii, terminated with a null
        Response (1 byte)
            uint8:
    response code
    • 0 if playback successfully initiated
    • 1 if no SD card was present
    • 2 if SD card init failed
    • 3 if partition table could not be read
    • 4 if filesystem could not be opened
    • 5 if root directory could not be opened
    • 7 if file was not found
    17 - Reset
        Reset the board remotely.  Useful for reprogramming the board without having to fiddle with switches.  The board will reset immediately after sending the response.
        Payload (0 bytes)
        Response (0 bytes)

     

    18 - Get next filename
        Retrieve the next valid filename from the SD card.  If a non-zero value is passed to the "restart" parameter, the file list will begin again from the start of the directory.  The file list state will be reset if any other SD operations are performed.
        Payload (1 byte)
            uint8: restart (0 to continue reading, 1 to begin anew)
        Response (N+1 bytes)
            uint8: an SD response code, one of the following:
    • 0 if operation was successful
    • 1 if no SD card was present
    • 2 if SD card init failed
    • 3 if partition table could not be read
    • 4 if filesystem could not be opened
    • 5 if root directory could not be opened
            N bytes: the name of the file in ascii, terminated with a null.  If the operation was unsuccessful, this will be the empty string.  Even if there was an SD card error, the null string should be returned.



    Buffered Commands:

    This command has been eliminated in favor of absolute point queueing.
    128 - Queue point, Incremental

        This point adds a point to the point queue.  It sends the step information as a relative position from the current position aka a delta.  This allows us to send the points as 2 byte data types in order to save space. Since it is relative and incremental, we can move the toolhead to any position we desire with a series of commands.


        Payload
    (10 bytes)
           int16
    : x delta
    (units are steps, always relative)
           int16
    : y delta (units are steps, always relative)

           int16
    : z delta (units are steps, always relative)

            uint32: dda speed (in microseconds between steps on the max delta)
           //uint8: timer1 prescaler value (as specified in the atmega644p datasheet)
          //uint16: timer1 count-up-to value (number of ticks to count up to before triggering interrupt)


    129 - Queue point, Absolute

        This queues a point to move to, but it is an absolute position.  This may make it easier to implement the host software, but it is much larger than the incremental points which will reduce your available command buffer size.

        Payload
    (16 bytes)
            int32: x coordinate
    (units are steps, always absolute)
            int32: y
    coordinate (units are steps, always absolute)
            int32: z
    coordinate (units are steps, always absolute.)
            uint32: dda speed (in microseconds between steps on the max delta)

    130 - Set Position

       
    This command sets the current position of the toolhead. Useful for a variety of things.

        Payload (12 bytes)
            int32: x position, in steps
            int32: y position, in steps
            int32: z position, in steps

    131 - Find Axes Minimums - Blocking seek to hardware min switches, w/ timeout

        This function will find the hardware minimum. It will home all axes at the same time, so it is up to the host software to generate two separate homing commands if you want to home the Z axis separately.

        Payload (7 bytes)
            uint8: axes flags, where bits 2-0 represent which axes to home: | N/A | N/A | N/A | N/A | N/A | Z | Y | X |
            uint32: feedrate (in microseconds between steps) (max 71.58 minutes)
            uint16: timeout (in seconds before abort)  (max = 18 hours) (default = 5 minutes)

    132 - Find Axes Maximums - Blocking seek to hardware max switches w/ timeout

        This function will find the hardware maximum, or position overflow (2^32), whichever is first.
        It should also record this to the eeprom for future use. 
    This function fill move all axes simulataneously, so it is important to generate two separate commands if you want to seek the Z axis separately.

        Payload (7 bytes)
            uint8: axes flags, where bits 2-0 represent which axes to home: | N/A | N/A | N/A | N/A | N/A | Z | Y | X |
            uint32: feedrate (in microseconds between steps) (max 71.58 minutes)
            uint16: timeout (in seconds before abort)  (max = 18 hours) (default = ??)
     

    133 - Delay - Simply stop and wait.

       
    Exactly what it says.

        Payload (4 bytes)
            uint32: delay period (in microseconds) (max 71.58 minutes)

    134 - Change Tool - Switch to a new tool

        This will trigger a tool change.  If the firmware supports automated tool changing, it will initiate its tool-changing routine.  If it doesn't, it simply updates its currently selected tool and moves on.

        Payload (1 byte)
            uint8: index/address of the desired tool to select

    135 - Wait For Tool Ready - Wait until a tool is ready before proceeding

        This command is used to tell the machine to wait (for example for the extruder to heat up, etc.)  It will poll the device with the 'are you ready' command until it responds affirmative

        Payload (5 bytes)
            uint8: the index of the tool to wait for before printing.
            uint16: the delay between query packets sent to the tool head in milliseconds (default: 100 milliseconds)
            uint16: the timeout before continuing without tool ready in seconds (default: 1 minute)


    136 - Tool Action Command - Send an action command to a tool for execution

        This command is for sending an action to the command.  The master firmware will then pass the query along to the appropriate tool, as well as passing the response back as well.

        Payload (3 + N bytes)
            uint8: the index of the tool to query
            uint8: the command for the tool.
            uint8: the length of the command payload (N)
            N bytes: command specific payload, variable size.

    137 - Enable/Disable Axes - Explicitly power or depower steppers

        This command is
    used to explicitly power steppers on or off.  Generally, it is used to shut down the steppers after a build to save power and avoid generating excessive heat.

        Payload (1 byte)
            uint8: bitfield
                bit 7: 1 to enable, 0 to disable
                bit 6,5,4,3: ignored
                bit 2: Z axis
                bit 1: Y axis
                bit 0: X axis

    Slave Microcontroller Commands (Extruder)

    0 - Get Version

        This command allows the master and slave to exchange version numbers.  It also allows for simple automated discovery of the firmware.  Version numbers will always be stored as a single number, Arduino / Processing style.  Thus, the versions will be 0 to 65535.

        Payload (2 bytes) - host version
            uint16: Host Version

        Response (2 bytes) - firmware version

            uint16: Firmware Version

    1 - Init - Initialize firmware to boot state

       
    Initialization consists of:
      • setting desired temperature to zero
      • turning off all devices (fan, coolant, etc.)
      • detaching all servo devices
      • setting desired RPM / PWM to zero

        Payload (0 bytes)
        Response (0 bytes)


    2 - Get Temperature

       
    This returns the last recorded temperature.  It's important for speed purposes that it does not actually trigger a temperature reading, but rather returns the last reading.  The slave firmware should be constantly monitoring its temperature and keeping track of the latest readings.

        Payload (0 bytes)

        Response (2 bytes)
            int16:
    Current Temperature in Celsius

    3 - Set Target Temperature
      
       
    This sets the desired temperature for the heating element.  The slave firmware will then attempt to maintain this temperature as closely as possible.

        Payload (2 bytes)
           int16:
    Desired Target Temperature in Celsius

    4 - Set Motor 1 Speed (PWM)

       
    This sets the motor speed as a PWM value.  Motor 1 generally corresponds to the extruder motor (M101-M103, M108 GCodes). It should not actually enable the motor until the motor enable command is given.

        Payload (1 byte)
            uint8: Desired PWM speed

    5 - Set Motor 2 Speed (PWM)

        
    This sets the motor speed as a PWM value.  Motor 2 generally corresponds to the spindle motor (??? GCodes). It should not actually enable the motor until the motor enable command is given.

        Payload (1 byte)
            uint8: Desired PWM speed

    6 - Set Motor 1 Speed (RPM)

       
    This sets the motor speed as an RPM value. 
    Motor 1 generally corresponds to the spindle motor (M101-M103, M108 GCodes). It should not actually enable the motor until the motor enable command is given.  For this command to actually work, a quadrature encoder, or tachometer must actually be attached to the motor.
       
        Payload (4 bytes)
            uint32: speed in microseconds between complete rotations.

    7 - Set Motor 2 Speed (RPM)
      
       
    This sets the motor speed as an RPM value.  Motor 1 generally corresponds to the spindle motor (??? GCodes). It should not actually enable the motor until the motor enable command is given.  For this command to actually work, a quadrature encoder, or tachometer must actually be attached to the motor.

        Payload (4 bytes)
            uint32: speed in microseconds between complete rotations.

    8 - Set Motor 1 Direction

        This sets the desired rotation direction of the motor.  It does not turn the motor on or off.

        Payload (1 byte)
            uint8:
    0 = reverse / counter clockwise, 1 = forward / clockwise

    9 - Set Motor 2 Direction

        This sets the desired rotation direction of the motor.  It does not turn the motor on or off.

        Payload (1 byte)
             uint8:
    0 = reverse / counter clockwise, 1 = forward / clockwise

    10 - Toggle Motor 1 (on/off)

        This turns the motor on or off.  It is possible to also set the direction of the motor as well.  This makes the command fairly efficient, since you can turn the motor on while setting the direction as well.  When the motor is enabled, it will move at the previously specified speed (either RPM or PWM).

        Payload (1 byte)
             uint8:
    The bottom two bits of this command determine direction and enable status:

    Bit
    7
    6
    5
    4
    3
    2
    1
    0
    Name
    N/A
    N/A
    N/A
    N/A
    N/A
    N/A
    DIR
    ENABLE

    ENABLE - HIGH means motor is enabled, LOW means motor is disabled.
    DIR - HIGH means motor rotates Clockwise, LOW means motor rotates Counter Clockwise 

    11 - Toggle Motor 2 (on/off)

            This turns the motor on or off.  It is possible to also set the direction of the motor as well.  This makes the command fairly efficient, since you can turn the motor on while setting the direction as well.  When the motor is enabled, it will move at the previously specified speed (either RPM or PWM).

        Payload (1 byte)
             uint8:
    The bottom two bits of this command determine direction and enable status:

    Bit
    7
    6
    5
    4
    3
    2
    1
    0
    Name
    N/A
    N/A
    N/A
    N/A
    N/A
    N/A
    DIR
    ENABLE

    ENABLE - HIGH means motor is enabled, LOW means motor is disabled.
    DIR - HIGH means motor rotates Clockwise, LOW means motor rotates Counter Clockwise

    12 - Toggle Fan (on/off)

       
    This turns the fan, or the output connected to the fan output on or off.

        Payload (1 byte)
             uint8:
    The bottom bit of this command determine enable status:

    Bit
    7
    6
    5
    4
    3
    2
    1
    0
    Name
    N/A
    N/A
    N/A
    N/A
    N/A
    N/A
    N/AENABLE

    ENABLE - HIGH means fan is enabled, LOW means fan is disabled.

    13 - Toggle Valve (on/off)
       
        
    This opens the valve, or the output connected to the valve output on or off.

        Payload (1 byte)
             uint8:
    The bottom bit of this command determine enable status:

    Bit
    7
    6
    5
    4
    3
    2
    1
    0
    Name
    N/A
    N/A
    N/A
    N/A
    N/A
    N/A
    N/AENABLE

    ENABLE - HIGH means valve is enabled (open), LOW means valve is disabled (closed).

    14 - Set Servo 1 Position

        
    This is basically just a wrapper to the Arduino hardware Servo library on pins 9 & 10.  It supports full servo angles, and can also do continuous rotation: http://arduino.cc/en/Reference/Servo

        Payload (1 byte)
             uint8:
    desired angle (from 0 to 180)

    15 - Set Servo 2 Position

        This is basically just a wrapper to the Arduino hardware Servo library on pins 9 & 10.  It supports full servo angles, and can also do continuous rotation: http://arduino.cc/en/Reference/Servo

        Payload (1 byte)
             uint8:
    desired angle (from 0 to 180)

    16 - Filament Status

        Payload (0 bytes)

        Response (1 byte)
           uint8: status: 0 - empty, 255 = full, number between 0-255 represents percentage of filament remaining.

    17 - Get Motor 1 Speed (RPM)

        Payload (0 bytes)

        Response
            uint32:
    speed in microseconds between complete rotations.

    18 - Get Motor 2 Speed (RPM)

        Payload (0 bytes)

        Response
            uint32: speed in microseconds between complete rotations.

    19 - Get Motor 1 Speed (PWM)

        Payload (0 bytes)

        Response (1 byte)
            uint8:
    current PWM value for the motor.

    20 - Get Motor 2 Speed (RPM)

        Payload (0 bytes)

        Response (1 byte)
            uint8: current PWM value for the motor.

    21 - Select Tool


        Tell a tool that it has been selected as the default tool.  The tool may or may not choose to do anything with that information.

        Payload (0 bytes)
        Response (0 bytes)

    22 - Is Tool Ready?

        Asks the tool if it is ready to be used.  Examples of ready states might be: spindle up to speed, temperature at target, etc.

        Payload (0 bytes)
        Response (1 byte)
            uint8: 0 for not ready, 1 for ready.

    23 - Pause/Unpause - Halt Execution Temporarily

        Behaves in a similar fashion to the pause/unpause functionality of the motherboard.

        Payload (0 bytes)
        Response (0 bytes)

    24 - Abort - Terminate all operations and reset

        Behaves in a similar fashion to the abort functionality of the motherboard.

        Payload (0 bytes)
        Response (0 bytes)

    25 - Read from EEPROM
       
    Read the specified number of bytes from the given offset in the EEPROM and return it in the response packet.  The maximum read size is 32 bytes.

        Payload (3 bytes)
            uint16: the offset of the read
            uint8: the number of bytes to return, N.  N <= 16.

        Response (N bytes)
            N bytes: the data read from the EEPROM.

    26 - Write to EEPROM
       
    Write the given bytes to the EEPROM starting at the specified location.  The maximum payload size is 16 bytes.  WARNING: this operation can be potentially slow (about 3.3ms * N) and will block all other operations.  It's not recommended to write to the EEPROM while a build is in progress.

        Payload (3+N bytes)
            uint16: the offset of the write
            uint8: the length of the data
            N bytes: the data to write

        Response (1 byte)
            uint8: the number of bytes successfully written.


    Errata


    Homing. There appears to be multiple approaches to homing. I'll explain ours. If you do the same or know about other common approaches, we should find a good solution (MK):

      • When starting a print, we first home to hardware switches. The firmware knows in which direction to home; it will home in the negative direction in X/Y and in positive direction in Z. Positive Z means at it's maximum height about the base plane. Homing is done with a fast feedrate until the optical switches hit. Then we'll reverse direction and move very slowly until the optical switches are clear again. This will give us a very accurate absolute reference position. ZH: Yes, this is exactly what the homing command will do.
    MK: We don't yet have a "homing command", only "find axes min/max". Idea: We could create a new command, "Goto Reference", which will home to the defined reference position (which may be a combination of min and max for the three axes). The find axes min/max would then only be needed to record the working area (range), and could then be joined into a separate command (Find Range). The range could of course be (partially) hardcoded into the firmware if the necessary switches are not present. Find Range would take a while to execute, but that's ok. We could even save the current range in EEPROM.  ZH: I'd really like to keep the firmware as simple as possible, and move this type of complicated zero-point initialization into the host side of things.  Really, all we're talking about here is something that could easily be represented as a series of commands (range: find min x/y/z, find max x/y/z, get range)  or (goto reference: home xyz, goto X, Y, Z)  additionally, the logic on these types of things is something that could change from machine to machine, and people will want to experiment with themselves.  putting it on the host means we're much more flexible (ie: we could have a <reference> block of gcode that is user-editable for setting reference points.
    MK: I'm still leaning towards putting more of this in firmware. The firmware still needs to be configured somehow (pin mapping, supported features, endstop positions, axis layoutt; which direction is positive vs. negative), and I think the reference configuration belongs together with this and not in run-time configuration sent from the host. We could make all this configurable over serial and store the configuration in EEPROM. It's a bit scary though (it's to easy to destroy hardware with software if someone presses the wrong button). I suggest that we investigate this by testing out different approaches in practice on our respective hardware setups by supporting multiple approaches in the initial phases and discuss this further later on.
    agm: I really like the idea of storing the range in EEPROM.  We could have a retrieve range that returns null if it has not been set yet, and a calibrate command that would find and store the range.  (A subsequent retrieve would get the updated data.)