Data Logger Web Server API (1.0.1)

Download OpenAPI specification:

HTTP/HTTPS web server (?command=) API for Campbell Scientific data loggers.

Campbell Scientific data loggers run an on-board HTTP web server. The supported models are the CR1000X(e), CR6, Granite 9, Granite 10, Granite 6, CR310, CR300, and CR350. Every model serves HTTP/1.1; HTTP/2 is available on the CR6, CR1000X(e), and GRANITE models only (see Transport). External clients — browsers, apps, and integrations — send commands to this server.

Supported models and OS versions

This documentation describes the API as implemented in the current released operating system for each model, or newer. Version numbers are from campbellsci.com.

Model Current OS version Released
CR1000X, CR1000Xe 8.5.1 or newer 2026-04-17
CR6 14.5.1 or newer 2026-04-17
Granite 9 3.2.0 or newer 2025-12-23
Granite 10 3.2.0 or newer 2025-12-23
Granite 6 3.2.1 or newer 2025-04-07
CR300, CR310 11.03.00 or newer 2025-12-23
CR350 1.9.0 or newer 2025-09-18

Note: The API and documentation may change without notice. Validate behavior against the specific firmware version in use.

Note: This documentation is derived from, and audited against, the data logger firmware implementation.

Getting started

The data logger exposes an HTTP API for data access and control. This section covers the request syntax common to every command and how access is secured.

Your first request

Every request is a GET to the data logger. It uses a ?command= query string and HTTP Basic credentials (a data logger user account — see Enabling security). For example, to read the two most-recent records of a table as JSON:

curl --user user:pass \
  'http://<datalogger>/?command=DataQuery&uri=dl:Public&mode=most-recent&p1=2&format=json'

The JSON response uses CSIJSON and has two parts. The head block describes the table: its environment and the schema for each field (name, type, units, and process). The data rows follow. Each row has a timestamp (time), a record number (no), and the field values (vals):

{ "head": { "environment": { "…": "…" }, "fields": [ "…" ] },
  "data": [ { "time": "2026-08-06T13:44:00", "no": 12, "vals": [ "…" ] } ] }

Next: use DataQuery to retrieve records (five modes, all formats), and BrowseSymbols to list the tables and fields you can query. The sections below cover request syntax, response formats, security, and error handling.

Command syntax

Commands generally follow the form:

<URL>?command=<CommandName>&uri=<DataSource>&<arguments>
  • Arguments are appended with &; optional arguments use a default.
  • uri / the dl: scheme — the data source. When querying the data logger directly it is the top-level source, so uri=dl: (or omitting uri) is assumed. A table or field is addressed as dl:<table> or dl:<table>.<field>.
  • Credentials in the URL — a username/password may be supplied inline, for example http://admin:12345@192.168.24.62/?command=ClockCheck.

Note: Any reserved character used in a value in the URL — an argument value, a uri, a file name, or inline credentials — must be percent-encoded. See URL escape codes.

Note: Operations below are shown with placeholder paths (for example, /DataQuery) because OpenAPI cannot put a query string in a path. The real request is always against / with ?command=<CommandName> — see each operation's request sample for the actual URL.

Transport

  • Default ports: HTTP on 80, HTTPS on 443. Every model serves HTTP/1.1. HTTP/2 runs on the CR6, CR1000X(e), Granite 6, Granite 9, and Granite 10 only; the CR300, CR310, and CR350 use HTTP/1.1 only. Where it is supported, HTTP/2 runs only over TLS (port 443) — port 80 is always HTTP/1.1. TLS uses mbedTLS and supports session resumption. You can set the number of concurrent HTTPS connections in Device Configuration Utility.
  • Methods: GET, POST, PUT, and DELETE. POST carries HTML form submissions; PUT/DELETE operate on /{drive}/{path} files.
  • The server is reachable over any active interface — Ethernet, Wi-Fi, cellular, or USB (RNDIS) — on both IPv4 and IPv6. Address the data logger by DNS/host name (for example, datalogger.local), IPv4 address, or IPv6 address. In a URL, wrap an IPv6 literal in square brackets — for example, http://[fe80::1]/?command=ClockCheck. USB (RNDIS) connections are treated as trusted for access-control purposes.
  • Responses use either Transfer-Encoding: chunked or a Content-Length header, always carry Cache-Control: no-cache, and may be gzip-compressed (Content-Encoding: gzip). The server does not serve range/partial requests (Accept-Ranges: none) — always request whole files.
  • File uploads (PUT) accept either a Content-Length body or Transfer-Encoding: chunked. A Transfer-Encoding header that is not chunked is rejected with 500; an over-size Content-Length returns 413.

Response format

Most data commands accept a format argument that selects the response format:

format Content-Type Notes
html (default) text/html; charset=UTF-8 Human-readable
json application/json; charset=UTF-8 CSI JSON
xml text/xml; charset=UTF-8 CSI XML
toa5 text/csv; charset=UTF-8 ASCII table (TOA5)
tob1 binary/octet_stream Binary table (TOB1)

Unrecognized values fall back to html.

toa5 and tob1 are record-table formats supported only by data-retrieval commands (DataQuery). Every other command supports html, json, and xml only. Each operation below lists the formats it supports.

Enabling security

Two combinable mechanisms:

  1. Numeric security code (legacy) — set in the Status table via SetSecurity / Security() to 165534. Weak; if set with no user accounts it must be supplied.
  2. Account Manager (HTTP Basic auth) — user accounts created in Device Configuration Utility. When enabled, the PakBus/TCP password and numeric code are not used for HTTP.

Access levels. Every request is authorized at one of four levels (the numeric code is what CheckAuthorization reports):

Level code What it allows
All 1 Everything Read/Write allows, plus viewing, retrieving, stopping, deleting, and installing programs, FileControl, and file upload/delete.
Read/Write 2 Everything Read-Only allows, plus changing the clock (ClockSet) and writable variables (SetValueEx). Programs still cannot be viewed, retrieved, stopped, or deleted.
Read-Only 3 (4 when no accounts are configured) Data collection is unrestricted — query tables (DataQuery), browse symbols, read the clock, and list/download data files. The clock and writable variables cannot be changed, and programs cannot be viewed, retrieved, stopped, or deleted.
None 0 Access denied. This level is never served — the data logger returns 401 to force a sign-in.

Anonymous and failed sign-ins. A request with no Authorization header is treated as the built-in anonymous user. When no user accounts exist, anonymous and failed sign-ins both get Read-Only access. If a request resolves to None, the data logger returns 401 to prompt for credentials instead of serving it. USB (RNDIS) connections are trusted (full access) unless you configure otherwise.

Error responses

On failure the server returns a standard HTTP status with a short HTML or header-only body:

Status Meaning
400 Bad Request Malformed request, or a missing/invalid required argument.
401 Authorization Required Authentication required or access level insufficient. The response carries WWW-Authenticate: Basic realm="<station name>".
404 Not Found The requested file — or a file matching a NewestFile expression — does not exist.
405 Method Not Allowed Operation not permitted on the target (for example, uploading .CSIPASSWD).
413 Payload Too Large An uploaded file is too large to store.
500 Internal Server Error An upload specified a Transfer-Encoding other than chunked (the only transfer encoding accepted for PUT).
507 Insufficient Storage No room for the upload, or the target file is locked / cannot be opened.

Successful command responses (200) also carry a per-operation outcome code in the body (for example, 1 = success for SetValueEx/ClockSet) — see each command for its meaning.

Authentication

Authorization checks.

Report the caller's access level

Returns the access level granted to the current credentials without taking any other action.

Authorizations:
basicAuth
query Parameters
command
required
string
Value: "CheckAuthorization"
anonymous
string
Enum: "true" "false"

If true, allow anonymous evaluation of the access level.

direct
string
Enum: "true" "false"

If true, report access based solely on whether this is a trusted USB (RNDIS) connection.

format
string
Default: "html"
Enum: "html" "json" "xml"

Response representation. Defaults to html.

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=CheckAuthorization&format=html'

Response samples

Content type
<!DOCTYPE html>
<html><head><title>CheckAuthorizationResponse</title></head><body><h1>CheckAuthorization</h1>
<table border="1"><tr><td>authorization</td><td>1</td></tr></table></body> </html>

Data

Reading measurement data and browsing the symbol/table namespace.

Data tables. Measurements are stored in data tables. Each table is a fixed-size ring buffer of records. When a table fills up, the oldest records are overwritten. So you can retrieve only a limited, moving window of history. That window is held in CPU RAM. A memory card, onboard flash, or SSD can extend it further back. Each record has:

  • a record number (no) — it increases by one per record and is not reset in normal operation. Only a program recompile or table re-initialization resets it. It is a 32-bit unsigned counter, so treat it as an opaque serial number that can wrap.
  • a timestamp (time) — the record's time on the logger's local clock, at sub-second resolution. No timezone offset is included.

Each field descriptor reports a type (for example xsd:float, xsd:unsignedInt, or xsd:string with string_len) and a process. The process is the CRBasic operation that produced the field: Smp (sample), Avg (average), Max, Min, and so on. Invalid or not-a-number floating-point values are written as the string "NAN".

Query records from a data table

Retrieves records from a data table. The mode argument selects the retrieval strategy; p1/p2/p3 supply its parameters.

mode p1 p2 p3
most-recent number of records (default 1)
since-record starting record number max records
since-time start timestamp
date-range start timestamp end timestamp
backfill seconds to back-fill (default 1)

Retrieval clamps, it does not reject. Because a table is a ring buffer with a limited history, a start (since-record/since-time/date-range) that is older than the oldest record still available is silently advanced to the oldest available record, an over-large p3 count is reduced, and a start in the future returns 0 records. The signal that a substitution happened is that the first record number returned differs from the one you asked for — so drive incremental collection ("everything since the last record I saw") from the returned record numbers, not from an assumed starting point.

Authorizations:
basicAuth
query Parameters
command
required
string
Value: "DataQuery"
uri
required
string
Example: uri=dl:onemin.ptemp

Table (and optional field) as dl:<table> or dl:<table>.<field>.

mode
required
string
Enum: "most-recent" "since-record" "since-time" "date-range" "backfill"
p1
string

First mode argument (see table).

p2
string

Second mode argument — end timestamp for date-range.

p3
string

Third mode argument — max record count for since-record.

transaction
integer <int64>

Client-supplied transaction id echoed back in the response.

headsig
integer

Header signature; lets the client skip the header block if unchanged.

format
string
Default: "html"
Enum: "html" "json" "xml" "toa5" "tob1"

Response representation. Defaults to html. toa5 (TOA5 ASCII table) and tob1 (TOB1 binary table) are record-table formats supported only by data-retrieval commands.

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=DataQuery&uri=dl:TestData&mode=most-recent&p1=2&format=html'

Response samples

Content type
Example
<!DOCTYPE html>
<HTML><HEAD><TITLE>Table Display</TITLE><meta http-equiv="Pragma" content="no-cache"><meta http-equiv="expires" content="0">
</HEAD><BODY>
<h1>Table Name: TestData</h1>
<table border="1" cellpadding="2" cellspacing="0">
<tr valign="middle" align="center">
<th nowrap>TimeStamp</th><th nowrap>Record</th><th nowrap>PTemp</th><th nowrap>PTemp_Avg</th>
<th nowrap>PTemp_Max</th><th nowrap>PTemp_Min</th><th nowrap>BattV</th><th nowrap>Counter</th><th nowrap>TestString</th>
</tr>
<tr valign="middle" align="center">
<td nowrap>2026-08-06 13:43:00.000</td><td nowrap>11</td><td nowrap>24.64038</td><td nowrap>24.6424</td>
<td nowrap>24.6442</td><td nowrap>24.64038</td><td nowrap>11.74362</td><td nowrap>137</td><td nowrap>&quot;hello&quot;</td>
</tr>
<tr valign="middle" align="center">
<td nowrap>2026-08-06 13:44:00.000</td><td nowrap>12</td><td nowrap>24.63583</td><td nowrap>24.63784</td>
<td nowrap>24.64008</td><td nowrap>24.63583</td><td nowrap>11.70022</td><td nowrap>149</td><td nowrap>&quot;hello&quot;</td>
</tr>
</table>
</BODY></HTML>

Enumerate tables and fields (symbols)

Lists the data logger's symbol namespace. With no uri, returns the set of tables. With uri=<table>, returns that table's fields. Each symbol reports name, uri, type, is_enabled, is_read_only, and can_expand. type is a numeric class code: 6 = table, 7 = array/expandable, 8 = scalar field.

Authorizations:
basicAuth
query Parameters
command
required
string
Value: "BrowseSymbols"
uri
string

Table (and optional field) whose children to enumerate. Omit to list tables.

format
string
Default: "html"
Enum: "html" "json" "xml"

Response representation. Defaults to html.

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=BrowseSymbols&uri=dl:&format=html'

Response samples

Content type
<!DOCTYPE html>
<html><head><title>BrowseSymbols Response</title></head><body>
<h1>BrowseSymbols Response</h1>
<table border="1">
<tr><th>name</th><th>uri</th><th>type</th><th>is_enabled</th><th>is_read_only</th><th>can_expand</th></tr>
<tr><td>Status</td><td>dl:Status</td><td>6</td><td>true</td><td>false</td><td>true</td></tr>
<tr><td>Public</td><td>dl:Public</td><td>6</td><td>true</td><td>false</td><td>true</td></tr>
<!-- ... more tables ... -->
</table></body> </html>

Variables

Setting public/variable values.

Set a public variable / table field

Sets a field to a value. The target is uri=<table>.<field> and the new value is value=. Requires read/write access; a read-only caller receives 401.

The response reports an outcome (1 = success, 0 = failure) and a description (for example, "success", "Read only", "Invalid table name", "Invalid field name", "Blocked by security").

Authorizations:
basicAuth
query Parameters
command
required
string
Value: "SetValueEx"
uri
required
string

Target field as <table>.<field> (optionally with an array index).

value
required
string

New value to write.

format
string
Default: "html"
Enum: "html" "json" "xml"

Response representation. Defaults to html.

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=SetValueEx&uri=dl:Public.TestFloat&value=12.34&format=html'

Response samples

Content type
<!DOCTYPE html>
<html><head><title>SetValueExResponse</title></head><body><h1>SetValueEx</h1>
<table border="1"><tr><td>outcome</td><td>1</td></tr><tr><td>description</td><td>success</td></tr></table></body> </html>

Clock

Reading and setting the data logger clock.

Read the data logger clock

Returns the current data logger time as an outcome (1 = success), time, and description.

Authorizations:
basicAuth
query Parameters
command
required
string
Value: "ClockCheck"
format
string
Default: "html"
Enum: "html" "json" "xml"

Response representation. Defaults to html.

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=ClockCheck&format=html'

Response samples

Content type
<!DOCTYPE html>
<html><head><title>ClockCheck Response</title></head><body>
<h1>ClockCheck Response</h1>
<table border="1">
<tr><td>outcome</td><td>1</td></tr>
<tr><td>time</td><td>2026-07-31 14:18:17.70</td></tr>
<tr><td>description</td><td>The clock was checked</td></tr>
</table> </body> </html>

Set the data logger clock

Sets the data logger time to time=. Requires read/write access (read-only callers receive 401). Returns the resulting outcome/time/description.

Authorizations:
basicAuth
query Parameters
command
required
string
Value: "ClockSet"
time
required
string

New time, for example, 2019-08-12T15:13:10.123.

format
string
Default: "html"
Enum: "html" "json" "xml"

Response representation. Defaults to html.

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=ClockSet&time=2026-07-30T16:15:31&format=html'

Response samples

Content type
<!DOCTYPE html>
<html><head><title>ClockSet Response</title></head><body>
<h1>ClockSet Response</h1>
<table border="1">
<tr><td>outcome</td><td>1</td></tr>
<tr><td>time</td><td>2026-07-31 14:18:17.82</td></tr>
<tr><td>description</td><td>The clock was set</td></tr>
</table> </body> </html>

Files

File operations — the ?command= file commands and the method-level PUT/GET/DELETE on /{drive}/{path}.

Drives. Files live on named drives, addressed as <drive>:<path> (for example CPU:program.cr1x). In a URL the first / becomes the :, so that path is /CPU/program.cr1x. The set of drives varies by model. Query the root (GET /?command=ListFiles) to see what a given logger has.

Drive Storage Notes
CPU: Internal flash Program and configuration; persistent.
USR: RAM disk Volatile — contents are lost on power-cycle/reset.
CRD: Removable memory card Present when a card is installed.
USB: External USB flash External module (CR6/CR1000X) or USB drive (Granite 9 and Granite 10).
SSD: SATA SSD Granite 9 and Granite 10 only.

All drives are FAT: file and drive names are case-insensitive (case-preserving), and long file names are supported. cpu:program.CR1X and CPU:PROGRAM.cr1x address the same file.

Perform a file-control action

Performs an action on a file or drive (compile and run a program, stop a program, delete/hide/move/copy a file, format a drive, or install an OS). Action is a numeric code; File (and File2 for move/copy) name the target(s).

Requires all-access (level 1); any lower access level is rejected with 401.

Action codes (BMP5 file-control set). Each action uses File, or both File and File2, as noted:

Action What it does
1 Compile and run the program named by File, and mark it as the program to run on power-up.
2 Mark the program named by File as the program to run on power-up. Does not start it.
3 Mark the file named by File as hidden.
4 Delete the file named by File.
5 Format the drive named by File (for example, CRD:). Erases everything on the drive.
6 Compile and run the program named by File without deleting existing data tables.
7 Stop the currently running program. File is not used.
8 Stop the currently running program and delete its associated data tables. File is not used.
9 Install the operating system (*.obj) named by File. The .obj file must already have been uploaded to the CPU: drive with a PUT request (see Upload (write) a file).
10 Compile and run the program named by File, without changing which program is marked to run on power-up.
11 Pause the currently running program. File is not used.
12 Resume the currently paused program. File is not used.
13 Stop the currently running program, delete its associated data tables, then compile and run the program named by File, and mark that same file to run on power-up.
14 Stop the currently running program, delete its associated data tables, then compile and run the program named by File, without changing which program is marked to run on power-up.
15 Move (rename) the file named by File2 to the name given in File.
16 Move (rename) File2 to the name given in File, stop the currently running program, delete its associated data tables, then compile and run File (the moved program) and mark it to run on power-up.
17 Move (rename) File2 to the name given in File, stop the currently running program, delete its associated data tables, then compile and run File (the moved program) without changing which program is marked to run on power-up.
18 Copy the file named by File2 to the name given in File.
19 Copy File2 to the name given in File, stop the currently running program, delete its associated data tables, then compile and run File (the new copy) and mark it to run on power-up.
20 Copy File2 to the name given in File, stop the currently running program, delete its associated data tables, then compile and run File (the new copy) without changing which program is marked to run on power-up.
Authorizations:
basicAuth
query Parameters
command
required
string
Value: "FileControl"
File
string

Target file (for example, CPU:program.CR6).

Action
required
integer [ 1 .. 20 ]

Numeric action code (see table).

File2
string

Second file, for move/rename (15/16/17) and copy (18/19/20).

format
string
Default: "html"
Enum: "html" "json" "xml"

Response representation. Defaults to html.

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=FileControl&File=CPU:old_data.dat&Action=4&format=html'

Response samples

Content type
<!DOCTYPE html>
<html><head><title>FileControl Response</title></head><body>
<h1>FileControl Response</h1>
<table border="1">
<tr><td>outcome</td><td>0</td></tr>
<tr><td>holdoff</td><td>0</td></tr>
<tr><td>description</td><td>File deleted</td></tr>
</table> </body> </html>

List files (scoped by URL path)

Lists files on the data logger. The listing is scoped by the URL path, not by a parameter:

  • At the root (GET /?command=ListFiles), it returns only the top-level drives (for example, CPU/, CRD/) as directory entries — not their contents.
  • Against a drive (GET /<drive>/?command=ListFiles), it returns the files inside that drive.

(Internally the server checks whether the path's 4th character is / — that is, a <drive>/ prefix — to decide whether to enumerate a drive's contents.)

Authorizations:
basicAuth
query Parameters
command
required
string
Value: "ListFiles"
format
string
Default: "html"
Enum: "html" "json" "xml"

Response representation. Defaults to html.

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=ListFiles&format=html'

Response samples

Content type
<!DOCTYPE html>
<html><head><title>ListFiles Response</title></head>
<body><h1>ListFiles Response</h1><table border="1">
<tr><td><b>Path</b></td><td><b>Is Directory</b></td><td><b>Size</b></td>
<td><b>Last Write</b></td><td><b>Run Now</b></td><td><b>Run On Power Up</b></td>
<td><b>Read Only</b></td><td><b>Paused</b></td></tr>
<tr><td>CPU/</td><td>true</td><td>3.13958e+07</td><td>2026-04-01T00:00:00</td><td>false</td><td>false</td><td>false</td><td>false</td></tr>
<tr><td>CRD/</td><td>true</td><td>1.62319e+10</td><td>2026-04-01T00:00:00</td><td>false</td><td>false</td><td>false</td><td>false</td></tr>
</table></body></html>

Retrieve the newest file matching an expression

Returns the contents of the newest file matching a file expression in expr= (for example, USR:*.jpg). The file is returned as raw bytes with Content-Type: application/octet-stream (unlike a GET /{drive}/{path} download, this command does not infer the type from the file extension).

Authorizations:
basicAuth
query Parameters
command
required
string
Value: "NewestFile"
expr
required
string

File expression / pattern to match (for example, USR:*.jpg).

Responses

Request samples

curl --user user:pass 'http://<datalogger>/?command=NewestFile&expr=CRD:*.dat' -o newest.dat

Download a file

Returns the contents of a file. Content-Type is inferred from the file extension (for example, .txttext/plain); a file whose extension the server does not recognize is returned as application/octet-stream. Requesting a bare drive (for example, GET /CPU/) returns that drive's INDEX.HTML / DEFAULT.HTML if present.

Authorizations:
basicAuth
path Parameters
drive
required
string
Enum: "CPU" "USR" "CRD" "USB" "SSD"

Data logger drive. (SSD is Granite 9 and Granite 10 only.)

path
required
string
Example: myfile.txt

File name on the drive. The request URL /{drive}/{path} maps internally to the data logger path <drive>:<path> (the first / becomes :).

Responses

Request samples

curl --user user:pass 'http://<datalogger>/CPU/program.cr1x' -o program.cr1x

Upload (write) a file

Uploads a file to the data logger with an HTTP PUT request. The request body is written to <drive>:<path>, replacing any existing file. Supports both Content-Length and Transfer-Encoding: chunked. Writes to the /cpu/, /crd/, or /usb/ directory and requires read/write access.

Special cases:

  • Uploading a CPU:*.OBJ file initiates an operating-system install.
  • Uploading a .CSIPASSWD file is rejected with 405.
Authorizations:
basicAuth
path Parameters
drive
required
string
Enum: "CPU" "USR" "CRD" "USB" "SSD"

Data logger drive. (SSD is Granite 9 and Granite 10 only.)

path
required
string
Example: myfile.txt

File name on the drive. The request URL /{drive}/{path} maps internally to the data logger path <drive>:<path> (the first / becomes :).

Request Body schema: application/octet-stream
required

The file contents. Send the raw bytes of the file as the request body (for example, curl -T <file>). Redoc shows this as string <binary>, meaning "the file's raw bytes."

string <binary>

Raw file bytes.

Responses

Request samples

curl -X PUT -T testing.cr6 --user user:pass 'http://<datalogger>/cpu/'

Response samples

Content type
text/html
<HTML><HEAD><TITLE>400 Bad Request</TITLE></HEAD><BODY>The request was malformed</BODY></HTML>

Delete a file

Deletes <drive>:<path> from the data logger.

Authorizations:
basicAuth
path Parameters
drive
required
string
Enum: "CPU" "USR" "CRD" "USB" "SSD"

Data logger drive. (SSD is Granite 9 and Granite 10 only.)

path
required
string
Example: myfile.txt

File name on the drive. The request URL /{drive}/{path} maps internally to the data logger path <drive>:<path> (the first / becomes :).

Responses

Request samples

curl -X DELETE --user user:pass 'http://<datalogger>/CPU/old.dat'

URL escape codes

Percent-encode these reserved characters wherever they appear in a value in the URL.

Character Escape code
space %20
< %3C
> %3E
# %23
% %25
+ %2B
{ %7B
} %7D
| %7C
\ %5C
^ %5E
~ %7E
[ %5B
] %5D
` %60
; %3B
/ %2F
? %3F
: %3A
@ %40
= %3D
& %26
$ %24

Revision history

The version shown at the top of this document tracks the documentation, not the firmware. Each firmware release also carries its own version (see Supported models and OS versions).

Version Date Summary
1.0.1 2026-08-17 Added a URL escape codes reference and a note on percent-encoding reserved characters in the URL.
1.0.0 2026-08-12 First release. Covers the HTTP web server commands (data, variables, clock, and files), the getting-started and security model, supported models and OS versions, and error responses.