Better doc on EPSON-CTRL
This commit is contained in:
222
README.md
222
README.md
@@ -510,9 +510,15 @@ printer = EpsonPrinter(hostname="192.168.1.87")
|
||||
pprint.pprint(printer.status_parser(printer.fetch_snmp_values("1.3.6.1.4.1.1248.1.2.2.1.1.1.4.1")[1]))
|
||||
```
|
||||
|
||||
### Byte sequences
|
||||
## END4 EPSON-CTRL commands over SNMP
|
||||
|
||||
Header:
|
||||
END4 commands (totally undocumented) might be a limited set of bidirectional remote commands that can be sent without establishing a D4 connection.
|
||||
|
||||
END4 EPSON-CTRL commands can be converted into OIDs and sent via SNMP.
|
||||
|
||||
Ref. excellent analysis from [ciprian](https://codeberg.org/atufi/reinkpy/issues/12#issuecomment-1660026) and [dger](https://codeberg.org/atufi/reinkpy/issues/12#issuecomment-1661250).
|
||||
|
||||
OID Header:
|
||||
|
||||
```
|
||||
1.3.6.1.4.1. [SNMP_OID_ENTERPRISE]
|
||||
@@ -522,9 +528,55 @@ Header:
|
||||
1.
|
||||
```
|
||||
|
||||
Full header sequence: `1.3.6.1.4.1.1248.1.2.2.44.1.1.2.1.`
|
||||
Full OID header sequence: `1.3.6.1.4.1.1248.1.2.2.44.1.1.2.1.`
|
||||
|
||||
Read EEPROM (EPSON-CTRL), after the header:
|
||||
Subsequent digits:
|
||||
|
||||
- Two ASCII characters that identify the command (e.g., "st", "ex"). These are command identifiers of the END4 EPSON-CTRL messages (Remote Mode)
|
||||
- 2-byte little-endian length field (gives the number of bytes in the parameter section that follows)
|
||||
- payload (a block of bytes that are specific to the command).
|
||||
|
||||
END4 commands partially overlap with Epson’s Remote Mode bi-directional printer-control language, though they are not strictly equivalent. Comprehensive, unified documentation for Epson’s Remote Mode commands does not exist: support varies by model, and command references are scattered across service manuals, programming guides and third-party sources (for example, the [Developer's Guide to Gutenprint](https://gimp-print.sourceforge.io/reference-html/x952.html) or [GIMP-Print - ESC/P2 Remote Mode Commands](http://osr507doc.xinuos.com/en/OSAdminG/OSAdminG_gimp/manual-html/gimpprint_37.html)). Some END4 instructions implement subsets of Epson’s Remote Mode protocol, while others are proprietary extensions and lie outside Epson’s published command set.
|
||||
|
||||
The following is the list of END4 commands supported by the XP-205.
|
||||
|
||||
Two-bytes|Description | Notes | Parameters
|
||||
:--:| ---------------------------------------------- | ----------------| -------------
|
||||
\|\| | EEPROM access | Implemented in this program, not supported by new printer firmwares | (A, B); see examples below
|
||||
cd | | | (0)
|
||||
cs | | | (0 or 1)
|
||||
cx | | |
|
||||
di | Device Identification ("di" 01H 00H 01H) | Implemented in this program | (1)
|
||||
ei | | | (0)
|
||||
ex | Set Vertical Print Page Line Mode, Roll Paper Mode | - EX BC=6 00 00 00 00 0x14 xx (Set Vertical Print Page Line Mode. xx=00 is off, xx=01 is on. If turned on, this prints vertical trim lines at the left and right margins).<br> - EX BC=6 00 00 00 00 0x05 xx (Set Roll Paper Mode. If xx is 0, roll paper mode is off; if xx is 1, roll paper mode is on).<br> - EX BC=3 00 xx yy (Appears to be a synonym for the SN command described above.) |
|
||||
fl | Firmware load. Enter recovery mode | |
|
||||
ht | Horizontal tab | |
|
||||
ia | List of cartridge types | Implemented in this program | (0)
|
||||
ii | List cartridge properties | Implemented in this program | (1 + cartridge number)
|
||||
ot | Power Off Timer | Implemented in this program | (1, 1)
|
||||
pe | (paper ?) | | (1)
|
||||
pj | Pause jobs (?) | |
|
||||
pm | Select control language ("PM" 02H 00H 00H m1m1=0(ESC/P), 2(IBM 238x Plus emulation) | | (1)
|
||||
rj | Resume jobs (?) | |
|
||||
rp | (serial number ? ) | | (0)
|
||||
rs | Initialize | | (1)
|
||||
rw | Reset Waste | Implemented in this program | (1, 0) + Serial SHA1 hash (20 bytes)
|
||||
st | Get printer status ("st" 01H 00H 01H) | Implemented in this program | (1)
|
||||
ti | Set printer time | (" TI" 08H 00H 00H YYYY MM DD hh mm ss) |
|
||||
vi | Version Information | Implemented in this program | (0)
|
||||
xi | | | (1)
|
||||
|
||||
### Examples for EEPROM access
|
||||
|
||||
#### Read EEPROM
|
||||
|
||||
- 124.124: "||" = Read EEPROM (EPSON-CTRL)
|
||||
- 7.0: Two-byte payload length = 7 bytes
|
||||
- two bytes for the read key
|
||||
- 65: 'A' = read
|
||||
- 190: Take the bitwise NOT of the ASCII value of 'A' = read, then mask to the lowest 8 bits. The result is 190.
|
||||
- 160: Shift the ASCII value of 'A' (read) right by 1 and mask to 7 bits, then OR it with the highest bit of the value shifted left by 7. The result is 160.
|
||||
- two bytes for the EEPROM address
|
||||
|
||||
```
|
||||
124.124.7.0. [7C 7C 07 00]
|
||||
@@ -535,7 +587,17 @@ Read EEPROM (EPSON-CTRL), after the header:
|
||||
|
||||
Example: `1.3.6.1.4.1.1248.1.2.2.44.1.1.2.1.124.124.7.0.73.8.65.190.160.48.0`
|
||||
|
||||
Write EEPROM, after the header:
|
||||
#### Write EEPROM
|
||||
|
||||
- 124.124: "||" = Read EEPROM (EPSON-CTRL)
|
||||
- 16.0: Two-byte payload length = 16 bytes
|
||||
- two bytes for the read key
|
||||
- 66: 'B' = write
|
||||
- 189: Take the bitwise NOT of the ASCII value of 'B' = write, then mask to the lowest 8 bits. The result is 189.
|
||||
- 33: Shift the ASCII value of 'B' (write) right by 1 and mask to 7 bits, then OR it with the highest bit of the value shifted left by 7. The result is 33.
|
||||
- two bytes for the EEPROM address
|
||||
- one byte for the value
|
||||
- 8 bytes for the write key
|
||||
|
||||
```
|
||||
7C 7C 10 00 [124.124.16.0.]
|
||||
@@ -548,6 +610,8 @@ Write EEPROM, after the header:
|
||||
|
||||
Example: `7C 7C 10 00 49 08 42 BD 21 30 00 1A 42 73 62 6F 75 6A 67 70`
|
||||
|
||||
#### Returned data
|
||||
|
||||
Example of Read EEPROM (@BDC PS):
|
||||
|
||||
```
|
||||
@@ -557,6 +621,154 @@ EE: = EEPROM Read
|
||||
AC = Value
|
||||
```
|
||||
|
||||
### Related API
|
||||
|
||||
#### epctrl_snmp_oid()
|
||||
|
||||
`self.epctrl_snmp_oid(two-char-command, payload)` converts an END4 EPSON-CTRL Remote command into a SNMP OID format suitable for use in SNMP operations.
|
||||
|
||||
**Parameters**
|
||||
|
||||
* `command` (`str`):
|
||||
A two-character string representing the EPSON Remote Mode command.
|
||||
|
||||
* `payload` (`int | list[int] | bytes`):
|
||||
The payload to send with the command. It can be:
|
||||
|
||||
* An integer, representing a single byte-argument.
|
||||
* A list of integers (converted to bytes)
|
||||
* A `bytes` object (used as-is)
|
||||
|
||||
It returns a SNMP OID string to be used by `self.printer.fetch_oid_values()`.
|
||||
|
||||
`self.epctrl_snmp_oid("ei", 0)` is equivalent to `self.epctrl_snmp_oid("ei", [0])` or `self.epctrl_snmp_oid("ei", b'\x00')`.
|
||||
|
||||
`self.epctrl_snmp_oid("st", [1, 0, 1])` is equivalent to `self.epctrl_snmp_oid("ei", b'\x01\x00\x01')`.
|
||||
|
||||
#### fetch_oid_values()
|
||||
|
||||
`self.fetch_oid_values(oid)` fetches the oid value. When oid is a string, it returns a list of a single element consisting of a tuple: data type (generally 'OctetString') and data value in bytes.
|
||||
|
||||
To return the value of the OID query: `self.fetch_oid_values(oid)[0][1]`.
|
||||
|
||||
### Testing END4 remote commands
|
||||
|
||||
Open the *epson_print_conf* application, set printer model and IP address, test printer connection. Then: Settings > Debug Shell.
|
||||
|
||||
The following are examples of instructions to test the END4 commands:
|
||||
|
||||
```python
|
||||
# cs
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("cs", 0))[0][1]
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("cs", 1))[0][1]
|
||||
|
||||
# cd
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("cd", 0))[0][1]
|
||||
|
||||
# ex
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("ex", [0, 0, 0, 0, 25, 0]))[0][1]
|
||||
|
||||
from datetime import datetime
|
||||
now = datetime.now()
|
||||
data = bytearray()
|
||||
data = b'\x00'
|
||||
data += now.year.to_bytes(2, 'big') # Year
|
||||
data += bytes([now.month, now.day, now.hour, now.minute, now.second])
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("ti", data))[0][1]
|
||||
|
||||
# Firmware load. Enter recovery mode
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("fl", 1))[0][1]
|
||||
|
||||
# ei
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("ei", 0))[0][1]
|
||||
|
||||
# pe
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("pe", 1))[0][1]
|
||||
|
||||
# rp (serial number ? )
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("rp", 0))[0][1]
|
||||
|
||||
# xi (?)
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("xi", 1))[0][1]
|
||||
|
||||
# Print Meter
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("pm", 1))[0][1]
|
||||
|
||||
# rs
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("rs", 1))[0][1]
|
||||
|
||||
# Detect all commands:
|
||||
ec_sequences = [
|
||||
decoded
|
||||
for i in range(0x10000)
|
||||
if (b := i.to_bytes(2, 'big'))[0] and b[1]
|
||||
and (decoded := b.decode('utf-8', errors='ignore')).encode('utf-8') == b
|
||||
]
|
||||
for i in ec_sequences:
|
||||
if len(i) != 2:
|
||||
continue
|
||||
r = self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid(i, 0))
|
||||
if r[0][1] != b'\x00' + i.encode() + b':;\x0c':
|
||||
print(r)
|
||||
```
|
||||
|
||||
Examples of commands ("CH" = Clean Heads and "NC" = Nozzle Check) that are not included in the END4 set, so they have to be delivered via TCP (port 9100 or port 515) and cannot be mapped to SNMP.
|
||||
|
||||
- CH BC=2 00 xx Perform a head cleaning cycle. "00" cleans all heads
|
||||
|
||||
| Name | Bytes (hex) | Purpose |
|
||||
| ----------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| EXIT\_PACKET\_MODE | `00 00 00 1B 01 40 45 4A 4C 20 31 32 38 34 2E 34 0A 40 45 4A 4C` | Exit packet mode. |
|
||||
| ENTER\_REMOTE\_MODE | `1B 40 1B 40 1B 28 52 08 00 00 52 45 4D 4F 54 45 31` | Put the device into "remote" control mode. |
|
||||
| SET\_CLOCK | `54 49 08 00 00 07 E9 06 08 17 0E 22` | "TI": Write the internal real-time clock to 2025-06-08 17:14:34. |
|
||||
| CLEAN\_HEADS | `43 48 02 00 00 00` | "CH": trigger print-head cleaning cycle. |
|
||||
| EXIT\_REMOTE\_MODE | `1B 00 00 00` | Exit remote control mode. |
|
||||
| JOB\_END | `4A 45 01 00 00` | Finalize the maintenance job. |
|
||||
|
||||
- NC BC=2 00 00 Print a nozzle check pattern
|
||||
|
||||
| Name | Bytes (hex) | Purpose |
|
||||
| ------------------------ | ---------------------------------------- | -------------------------------------------------------------- |
|
||||
| EXIT\_PACKET\_MODE | `00 00 00 1B 01 40 45 4A 4C 20 31 32 38 34 2E 34 0A 40 45 4A 4C` | Exit packet mode. |
|
||||
| ENTER\_REMOTE\_MODE | `1B 40 1B 40 1B 28 52 08 00 00 52 45 4D 4F 54 45 31` | Put the device into "remote" control mode. |
|
||||
| PRINT\_NOZZLE\_CHECK | `4E 43 02 00 00 00` | "NC": issue nozzle-check print pattern. |
|
||||
| EXIT\_REMOTE\_MODE | `1B 00 00 00` | Exit remote control mode. |
|
||||
| JOB\_END | `4A 45 01 00 00` | Finalize the maintenance job. |
|
||||
|
||||
|
||||
## ST2 Status Reply Codes
|
||||
|
||||
ST2 Status Reply Codes that are decoded by *epson_print_conf*; they are mentioned in various Epson programming guides:
|
||||
|
||||
Staus code | Description
|
||||
:---------:|-------------
|
||||
01 | Status code
|
||||
02 | Error code
|
||||
03 | Self print code
|
||||
04 | Warning code
|
||||
06 | Paper path
|
||||
07 | Paper mismatch error
|
||||
0c | Cleaning time information
|
||||
0d | Maintenance tanks
|
||||
0e | Replace cartridge information
|
||||
0f | Ink information
|
||||
10 | Loading path information
|
||||
13 | Cancel code
|
||||
14 | Cutter information
|
||||
18 | Stacker(tray) open status
|
||||
19 | Current job name information
|
||||
1c | Temperature information
|
||||
1f | Serial
|
||||
35 | Paper jam error information
|
||||
36 | Paper count information
|
||||
37 | Maintenance box information
|
||||
3d | Printer I/F status
|
||||
40 | Serial No. information
|
||||
45 | Ink replacement counter (TBV)
|
||||
46 | Maintenance_box_replacement_counter (TBV)
|
||||
|
||||
Many printers return additional codes whose meanings are unknown and not documented.
|
||||
|
||||
## API Interface
|
||||
|
||||
### Specification
|
||||
|
||||
@@ -932,7 +932,7 @@ class EpsonPrinter:
|
||||
MIB_OID_ENTERPRISE = "1.3.6.1.4.1"
|
||||
MIB_EPSON = MIB_OID_ENTERPRISE + ".1248"
|
||||
OID_PRV_CTRL = "1.2.2.44.1.1.2"
|
||||
D4_TO_OID = f'{MIB_EPSON}.{OID_PRV_CTRL}.1'
|
||||
EPSON_CTRL_TO_OID = f'{MIB_EPSON}.{OID_PRV_CTRL}.1'
|
||||
|
||||
MIB_INFO = {
|
||||
"Model": f"{MIB_MGMT}.1.25.3.2.1.3.1",
|
||||
@@ -2448,67 +2448,8 @@ class EpsonPrinter:
|
||||
|
||||
def epctrl_snmp_oid(self, command, payload):
|
||||
"""
|
||||
Build the full OID based on EPSON-CTRL D4 (END4) encapsulation
|
||||
(EPSON’s Remote Mode encapsulated into an SNMP OID)
|
||||
http://osr507doc.xinuos.com/en/OSAdminG/OSAdminG_gimp/manual-html/gimpprint_37.html
|
||||
|
||||
Implemented commands: rw, ot, ||, vi 0, di 1, ia 0, st 1, ii
|
||||
|
||||
Other commands:
|
||||
|
||||
cx
|
||||
ht
|
||||
|
||||
# set timer 08H 00H 00H YYYY MM DD hh mm ss
|
||||
ti
|
||||
|
||||
ex (Set Vertical Print Page Line Mode, Roll Paper Mode)
|
||||
|
||||
# Firmware load. Enter recovery mode
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("fl", 1))
|
||||
|
||||
# cs (?)
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("cs", 0))
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("cs", 1))
|
||||
|
||||
# cd (?)
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("cd", 0))
|
||||
|
||||
# ei (?)
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("ei", 0))
|
||||
|
||||
# pe (paper ?)
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("pe", 1))
|
||||
|
||||
# rp (serial number ? )
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("rp", 0))
|
||||
|
||||
# xi (?)
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("xi", 1))
|
||||
|
||||
# Print Meter
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("pm", 1))
|
||||
|
||||
# rs (?)
|
||||
self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid("rs", 1))
|
||||
|
||||
# Pause and resume jobs
|
||||
pj:00
|
||||
rj
|
||||
|
||||
# Detect all commands:
|
||||
ec_sequences = [
|
||||
decoded
|
||||
for i in range(0x10000)
|
||||
if (b := i.to_bytes(2, 'big'))[0] and b[1]
|
||||
and (decoded := b.decode('utf-8', errors='ignore')).encode('utf-8') == b
|
||||
]
|
||||
for i in ec_sequences:
|
||||
if len(i) != 2:
|
||||
continue
|
||||
r = self.printer.fetch_oid_values(self.printer.epctrl_snmp_oid(i, 0))
|
||||
if r[0][1] != b'\x00' + i.encode() + b':;\x0c':
|
||||
print(r)
|
||||
Convert END4 EPSON-CTRL messages into OID
|
||||
(EPSON’s Remote Mode)
|
||||
"""
|
||||
assert len(command) == 2
|
||||
if isinstance(payload, int):
|
||||
@@ -2516,7 +2457,7 @@ class EpsonPrinter:
|
||||
elif isinstance(payload, list):
|
||||
payload = bytes(payload)
|
||||
cmd = command.encode() + struct.pack('<H', len(payload)) + payload
|
||||
return self.D4_TO_OID + "." + ".".join(
|
||||
return self.EPSON_CTRL_TO_OID + "." + ".".join(
|
||||
str(int(i)) for i in cmd
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user