This file defines and documents all the available RESTful API resources and
configuration for cncserver.js
. RESTful practices are all HTTP
based and accessible by any system or devices that can access a web page.
METHODs are used to differentiate what is being done to a particular resource.
All resources should be requested with, and return JSON data, regardless of
status code. Though for non-GET requests, you can pass variables as either JSON,
form encoded, or any other well-known standard, as long as you set the
Content-Type
header to match.
In each request example below, the server is assumed to be added to the
beginning of each resource, E.G.: GET http://localhost:4242/v1/pen
will GET
the
status of the pen from a server plugged into the local computer, at the default
port of 4242
.
If you want to test any of these, try out Postman for Google Chrome. It allows for easy testing of any RESTful HTTP method to even remote servers.
An easy to use Postman JSON config file is now available in the repo
here.
This supplies all the current API resources in a simple click and send test
environment, just import, and setup two global variables cncserver-host
and
cncserver-port
. If running on just one computer, these will be by default
localhost
and 4242
respectively.
NOTE: Any comments visible in responses/JSON payload in the documentation below are just to help make it easier to understand what's being sent. Comments are not allowed in JSON data and will not exist in returned data.
The pen
resource is meant to act as the input/output for anything having
directly to do with drawing or interacting with the "pen". For the
WaterColorBot, it could be a paintbrush, or a pencil, or even an actual pen.
Gets the "current" pen status at the tip of the execution buffer (note that this
is not the known actual machine status if the buffer has any items in it).
This is a direct dump of the internal state of the pen, so it will include x,y
absolute step position, which can be used in conjunction with bot settings
maxArea
width and height to tell you exactly where the pen should be.
GET /v1/pen
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"x": 2344, // Coordinates of drawing position, in steps
"y": 281,
"state": 1, // Pen state is from 0 (Up/Off) to 1 (Down/On)*
"height": 12372, // The last sent servo position height value
"power": 0, // The amount of power given to the pen (not always supported)
"tool": color2, // Machine name of last tool
"lastDuration": 1288, // The duration of the last movement
"distanceCounter": 231, // Distance traveled in steps with pen down
"simulation": 0 // 0 = pen is real/ready, 1 = Pen is virtual/not connected to bot
}
- All values are reset with server and will not be kept or otherwise stored as runtime state.
- tool will default to first tool in toolset at server start, eg
color0
. - distanceCounter must be reset/managed via the client, otherwise it's just a handy realtime counter for steps when pen is down.
- Pen simulation mode of 1 means that either the serial connection to the bot
never worked, or has been lost.
PUT
a value of 0 to attempt to reconnect. - To restate: if there are items in the buffer, this will only represent the
very end of the buffer (the last action sent). To get the actual pen position,
add
?actual=1
to the query URI, or use the real-time event driven API detailed at the bottom of this document.
Allows for direct setting of state and position. Currently, you must set position and state in separate requests. See issue #16 for status.
PUT /v1/pen
Content-Type: application/json; charset=UTF-8
{
"state": 0.75 // Pen state is from 0 (Up/Off) to 1 (Down/On)
}
PUT /v1/pen
Content-Type: application/json; charset=UTF-8
{
"state": "wash" // OR use named presets for simpler use, see list below for more.
}
PUT /v1/pen
Content-Type: application/json; charset=UTF-8
{
"x": 13.2919, // Percentage of total width/height (0 to 100)
"y": 72.28124910
}
PUT /v1/pen
Content-Type: application/json; charset=UTF-8
{
"resetCounter": 1 // Should be 0 or 1
}
This will set the PWM output on B3 of the EBB
PUT /v1/pen
Content-Type: application/json; charset=UTF-8
{
"power": 0.75 // Pen power is a float from 0 (Full off) to 1 (Full on)
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
( RETURNS FULL PEN STATUS, SEE ABOVE EXAMPLE IN: GET /v1/pen RESPONSE )
- As noted above, pen x, y should be sent as a percentage of total canvas width & height respectively. This means that you can have your input canvas be any size, as long as the aspect ratio matches the output, you shouldn't get any stretching in the final image.
- Pen state supports any value from 0 to 1 (E.G.
0.75
) that sets the servo as a percentage of the range between the nominal use position, and "up". - Pen state named presets are also available as setup in the bot ini file. Included with watercolorbot.ini are:
up
, equivalent to a state of 0paint
, equivalent to a state of 1 (for painting and getting paint)wipe
, default equivalent to a state of ~0.9 (for wiping the brush on water dishes)wash
, default equivalent to a state of ~1.2, a height impossible to move to via the API without this named preset. Used to squash the bristles down to clean them.- Returns full pen status on success, no matter what was sent or changed.
- Tools can't be changed here. See
/tools
resource below. - Request will not complete until movement is actually complete, though you can send more requests through separate channels.
- Passing the variable
ignoreTimeout
as1
for x/y movements will finish the request immediately, even though it may still be moving to that position. In those cases, the response will return a202 Accepted
instead of a200 OK
. lastDuration
in return data can be used in conjunction with ignoreTimeout to allow the client to manage timings instead of waiting on the server.
Parks the pen, as you cannot move pen outside of draw area using PUT.
DELETE /v1/pen
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
( RETURNS FULL PEN STATUS, SEE ABOVE EXAMPLE IN: GET /v1/pen RESPONSE )
- Relies on the original parking position being correct in the first place, obviously.
The tools resource is meant to allow automated changing and listing of tools, or colors for the WaterColorBot. The server may not know what exact type of tool its using as tool sets can be changed out, as long as tool positions don't change, they can stay configured server-side.
Lists all tools machine names in the device's tool configuration list.
GET /v1/tools
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"tools" :
[
"water0",
"water1",
"water2",
"color1"
]
}
Sets the tool for the pen. Will make all required movements* for the given
device's tool change operation, request finishes when tool change is complete.
Will return a 404 Not Found
if tool machine name isn't valid.
PUT /v1/tools/color1
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"status": "Tool changed to color1"
}
- "required movements" for each tool change depend on server configuration for each tool. For example, WaterColorBot tool changes all follow exactly this pattern: "pen up, move to position, pen down, wiggle, pen up". The ability to change this is planned in issue #39
- Consecutive requests to the same tool will not act any differently than a request to change to a new tool, and will therefore repeat all required movements.
Provides low level access to stepper motor driver, and is placeholder for more low level functions in the future.
Turn off/unlock the motors. This allows for the motors to be moved by hand. After having moved programmatically, the motors are locked to that position until this is called.
DELETE /v1/motors
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"status": "Disabled"
}
- Motors will be enabled again as soon as any command that moves them is run.
Allows direct setting of motor details, currently only supports resetting motor offsets. Use after disabling the motors and parking by hand to ensure proper relative offset reset without needing to restart the server.
PUT /v1/motors
Content-Type: application/json; charset=UTF-8
{
"reset": 1
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"status": "Motor offset reset to park position"
}
The settings
resource gives you handy, low level access to all the INI and
command line options currently in use by CNC Server, giving you the ability to
change height presets, servo duration, tool settings and anything else controlled
via nconf.
Gets the list of available settings types, handing the relative URI over for each.
GET /v1/settings
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"global": "/v1/settings/global",
"bot": "/v1/settings/bot"
}
Gets a full dump of all settings for the given settings type, 404 not found if settings type not found. Listed values are pulled from environment, then ini file, then user set overrides.
GET /v1/settings/global
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"httpPort": "4242",
"httpLocalOnly": false,
"swapMotors": false,
"invertAxis": {
"x": false,
"y": false
},
"serialPath": "/dev/ttyACM0",
"bufferLatencyOffset": "50",
"debug": false,
"botType": "watercolorbot"
}
Set root or sub level values for any settings on the given type.
PUT /v1/settings/global
Content-Type: application/json; charset=UTF-8
{
"debug": true
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"httpPort": "4242",
"httpLocalOnly": false,
"swapMotors": false,
"invertAxis": {
"x": false,
"y": false
},
"serialPath": "/dev/ttyACM0",
"bufferLatencyOffset": "50",
"debug": true, // This value was previously false
"botType": "watercolorbot"
}
PUT /v1/settings/bot
Content-Type: application/json; charset=UTF-8
{
"tools": {
"inkwell": { // ALL tools will be replaced with this entry
"x": 0,
"y": 0,
"wiggleAxis": "y",
"wiggleTravel": 300,
"wiggleIterations": 4
}
}
}
PUT /v1/settings/bot
Content-Type: application/json; charset=UTF-8
{
"servo:duration": 450 // Only duration will be set when using nconf collapsed JSON format
}
PUT /v1/pen
Content-Type: application/json; charset=UTF-8
{
"servo:presets:foo": 42 // Creates new height preset named "foo" under servo:presets with a value of 42%
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
( RETURNS FULL SETTINGS VALUE OUTPUT, SEE ABOVE EXAMPLE IN: GET /v1/settings/{settings type} RESPONSE )
- This interface allows for changing every single setting, but in its current
implementation no callbacks are triggered for certain changes like
serialPath
orbotType
, therefore there will only be a real effect if CNC Server references the setting value in an operation after it's been changed. - Settings change callbacks could be added if there were a clear need with examples. Looking for something like this? Submit a pull request or an issue!
- All settings are reset to defaults/INI/environment on server restart.
- As illustrated in the latter examples, you must either use the compressed
JSON notation to replace sub-level items like
servo:duration
, or you can reference the exact structure, but doing so will replace the entire item and all its children. - If experimenting, make sure that the output returned matches your expectations
- There's currently no sanity checks for data ranges or variable types, and all storage through INI files defaults to strings, so play nice and double check the validity of your settings or you'll be chasing down very strange issues.
The buffer
resource gives you insight into the command buffer used to
internally queue work to be done by the hardware. Any commands meant to require
action from bot hardware write to the command buffer, other commands write to
the servers variables directly and are "instant". The most common use case for
this resource is to pause or resume command running on a low level.
Gets the current buffer status, length of buffer and even the low level items to be run next.
GET /v1/buffer
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"running": false, // Whether or not the buffer is currently processing
"paused": false, // True if Paused, false if ready
"count": 0, // Length of buffer
"buffer": [] // Full buffer output
}
- The buffer is checked every 10ms, and processes that with short timing intervals below a given threshold happen in an "instant" next-run blocking fashion to ensure they occur without jitters or gaps.
- The buffer output format is an array or low level serial commands specific to the bot. These may eventually be abstracted to allow importing of rendered commands directly into the buffer.
Set elements of the buffer state. Currently only supports paused
state.
PUT /v1/buffer
Content-Type: application/json; charset=UTF-8
{
"paused": true
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"running": false,
"paused": true,
"count": 0,
"buffer": []
}
- The data given is always the same as the GET method, but current as of the last change.
Create buffer specific items for the buffer. Currently only supports message
and callback
.
POST /v1/buffer
Content-Type: application/json; charset=UTF-8
{
"message": "Going and drawing something"
}
POST /v1/buffer
Content-Type: application/json; charset=UTF-8
{
"callback": "drawcomplete"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"status": "Message added to buffer"
}
- For clients that want event driven status messages, this adds the messages to
the same buffer as the other commands, so as the message item is reached during
buffer processing, it's sent out via the
message update
Socket.IO event. See socket/streaming section below for more info. - No filtering is done to this string, use with care.
Immediately clear the entire buffer of further commands.
DELETE /v1/buffer
Content-Type: application/json; charset=UTF-8
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{
"status": "Buffer cleared"
}
- No wait is given, buffer is immediately cleared and no waiting callbacks are called. This might have to change though...
The API has served the project incredibly well, but it was lacking in one important aspect: a remote client can receive no updates without asking first, no events or data without polling or some other kludge. That is now all completely fixed with full Socket.IO streaming support.
Though not technically ReSTful or required, CNC Server now supports streaming of server oriented events that allow for a far better buffer management, visualization and accuracy.
To use an event, just pass the named event type into your socket.on
function
like this:
socket.on('pen update', function(actualPen){
console.log('The pen just moved to x:' + actualPen.x + ' y:' + actualPen.y);
});
The following named Socket.IO event types and data are available:
Triggered whenever the actualPen object is changed, usually during serial command sends.
( RETURNS FULL PEN STATUS OBJECT, SEE ABOVE EXAMPLE IN: GET /v1/pen RESPONSE )
- Pen status object here is the
actualPen
object, so it should represent the current status, or at least the soon to be current status of the bot itself. - This is triggered as serial commands are being sent out. For movement,
the bot will only just start moving at the moment this is triggered, so you can
use the
lastDuration
key in the object to animate the movement linearly between the last location and the new location perfectly. See example application CNC Server Controller at the web root for an idea of how this works. - Unfortunately because of issues upstream, if being used as a node module with
a local callback
penUpdateTrigger
, these event updates will no longer be sent. This shouldn't need to be the case, but it ensures that commands are sent cleanly to the bot over repeating this data over the stream. - This event can trigger an update with no actual pen changes.
Triggered whenever the buffer object (or its associated variables) is changed, usually during serial command sends, or pausing/unpausing.
{
bufferList: ["hash1", "..."], // Countable array of hashes, in order.
bufferData: { hash1: {...}}, // Object keyed by hash, of each buffer action.
bufferRunning: bufferRunning, // Boolean: Buffer is currently processing/running?
bufferPaused: bufferPaused, // Boolean: Is the buffer paused?
bufferPausePen: bufferPausePen // Object: Last pen object set before paused
}
move =
{
type: "absmove",
x: 4200,
y: 1200,
}
height =
{
type: "absheight",
z: 19750,
state: "up",
}
message =
{
type: "message",
message: "This is a custom text message!"
}
- Buffer items are verbatim as used in the event loop. See above buffer object descriptions.
- If buffer command is not an object, it will be a string to be sent out as serial ASCII data with no extra metadata.
- This event can trigger an update with no actual buffer changes.
Triggered whenever a "message" item in the buffer is reached, added via the
/v1/buffer POST
.
{
message: "I never could get the hang of Thursdays.",
timestamp: "Thur Sep 1 2016 12:09:45 GMT-0700 (Pacific Standard Time)"
}
- These custom text messages are set simply to trigger this event, and intend to simply help inform users about where the bot might be or what it's intent is within a large set of operations.
Triggered whenever a "callbackname" item in the buffer is reached, added via the
/v1/buffer POST
.
{
name: "drawcomplete",
timestamp: "Thur Sep 1 2016 12:09:45 GMT-0700 (Pacific Standard Time)"
}
- Yet another custom text message, but with the sole intent to be used as a switch case in the event function to trigger events as "callbacks" at certain points in execution.