La elección se basó en los siguientes requisitos: MFP no es caro, compacto, impresión en red, disponibilidad de controladores para Linux (Fedora / Mint / OpenSUSE). El héroe del artículo poseía todas estas características, pero no se notó un matiz. Contador de recursos curvo. Si te preguntas como curar y
(Nota para Grammar Nazi de una persona con disgrafía leve)
, . . / , . , . «», . , , , / . , , , , . , , . , —
¡Bienvenidos!
La cosa es,que el MFP no cuenta la cantidad de tóner usado, sino el número de páginas impresas. Si imprimí el cartucho inicial con un grado de llenado suficientemente grande, entonces ocurrió un incidente con el segundo. El caso es que mi esposa se unió rápidamente a la comunidad local de Angloms y parte del trabajo relacionado con la provisión de impresión recayó sobre nosotros. Entonces, todo tipo de imágenes de contorno para cortar / colorear / origami se llenan con tóner, como máximo, el 1% de la hoja. Como resultado, me encontré "vacío" en el mostrador, pero en realidad, un 70 por ciento del cartucho. No, no soy tan pobre como para no permitirme comprar uno nuevo, no estoy tan educado ambientalmente como para experimentar un tormento moral, arrojando un producto útil a un vertedero, pero desde la sensación de que lo tiraron legalmente, la silla comenzó a humo, que fue un impulso a la acción ...
Honestamente, no esperaba convertirme en un "hacker de madre", dado que esa protección de hardware es muy difícil de romper. Conté, al menos, con algoritmos de cifrado decentes y memoria OTP (una vez programable). Pero la realidad resultó ser mucho más común. Afortunadamente, había muchas instrucciones en la solicitud para restablecer el cartucho, y el "chip de protección" resultó ser un I2C EEPROM AT24C01 bastante extendido , soldado con un "kit de carrocería" mínimo, no una placa. En general, en la siguiente imagen, puedes terminar el artículo:
Video original
Con cualquier programador, leemos el contenido del microcircuito, "ponemos a cero" las celdas delineadas en cuadros rojos y cambiamos el último par de dígitos del número de serie. Cabe señalar que la serie es una cadena de texto que termina con un espacio, por lo que debe cambiarla en el rango 0 ... 9 (0x30 ... 0x39). La dirección física del microcircuito soldado en la placa es 0x03. Pero ... Conoce a un zapatero sin botas. No hay un programador universal, por lo que tomamos
ensamblado se ve así:
Quité la placa de "protección" del cartucho para asegurarme de que el pinout dado en el manual corresponde a la realidad y para obtener la dirección física del microcircuito en el bus:
Pads de contacto, de izquierda a derecha: GND, + 5V, SCL . SDA.
No tiene que ser inteligente con la fabricación del adaptador, pero suelde los cables directamente a la placa sin quitarla del cartucho. A continuación, copie mi código de mierda en el IDE de Arduino:
Código de mierda
#include <stdint.h>
#include <Wire.h>
//----------------------------------------------------------------
#define EERROM_SZ (0x80)
#define EERROM_PHY_ADDR (0x03)
#define EERROM_HEAD (0x50)
#define PRN_BUFF_SZ (0x08)
#define SER_START_ADDR (0x20)
#define SER_END_ADDR (0x2B)
#define SER_MOD_ADDR0 (0x2A)
#define SER_MOD_ADDR1 (0x29)
#define SER_MOD_ADDR2 (0x28)
//----------------------------------------------------------------
static uint8_t eeprom_data[EERROM_SZ];
static bool erased;
static bool z_filled;
//----------------------------------------------------------------
static uint8_t ee_read(uint8_t phy_addr, uint8_t addr)
{
uint8_t res;
Wire.beginTransmission(EERROM_HEAD | phy_addr);
Wire.write(addr);
Wire.endTransmission();
Wire.requestFrom(EERROM_HEAD | phy_addr, 1);
res = Wire.read();
return res;
}
//----------------------------------------------------------------
static void ee_write(uint8_t phy_addr, uint8_t addr, uint8_t data)
{
Wire.beginTransmission(EERROM_HEAD | phy_addr);
Wire.write(addr);
Wire.write(data);
Wire.endTransmission();
delay(5);
}
//----------------------------------------------------------------
static void read_data(uint8_t phy_addr)
{
uint8_t addr;
uint8_t data;
erased = true;
z_filled = true;
Serial.print("Read from phy addr ");
Serial.print(phy_addr);
for (addr = 0; addr < EERROM_SZ; addr++)
{
if (0 == (addr & 0x03))
{
Serial.print(".");
}
data = ee_read(phy_addr, addr);
eeprom_data[addr] = data;
if (0xFF != data)
{
erased = false;
}
if (0x00 != data)
{
z_filled = false;
}
}
Serial.println("Ok");
}
//----------------------------------------------------------------
static void write_data(uint8_t phy_addr)
{
uint8_t addr;
Serial.print("Write to phy addr ");
Serial.print(phy_addr);
for (addr = 0; addr < EERROM_SZ; addr++)
{
if (0 == (addr & 0x03))
{
Serial.print(".");
}
ee_write(phy_addr, addr, eeprom_data[addr]);
}
Serial.println("Ok");
}
//----------------------------------------------------------------
static bool check_data(uint8_t phy_addr)
{
uint8_t addr;
uint8_t data;
Serial.print("Check from phy addr ");
Serial.print(phy_addr);
for (addr = 0; addr < EERROM_SZ; addr++)
{
if (0 == (addr & 0x03))
{
Serial.print(".");
}
data = ee_read(phy_addr, addr);
if (eeprom_data[addr] != data)
{
Serial.println("FAILED");
return false;
}
}
Serial.println("Ok");
return true;
}
//----------------------------------------------------------------
static void print_data(void)
{
uint16_t addr;
char prn_buff[PRN_BUFF_SZ];
for(addr = 0; addr < EERROM_SZ; addr++)
{
if (0x00 == (addr & 0x0F))
{
snprintf(prn_buff, PRN_BUFF_SZ, "%4X: ", addr);
Serial.print(prn_buff);
}
snprintf(prn_buff, PRN_BUFF_SZ, "%2X ", eeprom_data[addr]);
Serial.print(prn_buff);
if (0x0F == (addr & 0x0F))
{
Serial.print("\n\r");
}
}
Serial.print("\n\r");
}
//----------------------------------------------------------------
static void prn_serial(void)
{
Serial.print("Serial #: ");
Serial.write(&eeprom_data[SER_START_ADDR], 1 + SER_END_ADDR - SER_START_ADDR);
Serial.print("\n\r");
}
//----------------------------------------------------------------
static void mod_serial(void)
{
eeprom_data[SER_MOD_ADDR0]++;
if (eeprom_data[SER_MOD_ADDR0] > '9')
{
eeprom_data[SER_MOD_ADDR0] = '2';
}
eeprom_data[SER_MOD_ADDR1]++;
if (eeprom_data[SER_MOD_ADDR1] > '9')
{
eeprom_data[SER_MOD_ADDR1] = '3';
eeprom_data[SER_MOD_ADDR2]++;
if (eeprom_data[SER_MOD_ADDR2] > '9')
{
eeprom_data[SER_MOD_ADDR2] = '1';
}
}
}
//----------------------------------------------------------------
static void reset_mileage(void)
{
uint8_t i;
for (i = 0x12; i <= 0x1F; i++)
{
eeprom_data[i] = 0;
}
for (i = 0x2C; i <= 0x7F; i++)
{
eeprom_data[i] = 0;
}
}
//----------------------------------------------------------------
static bool test_magic(void)
{
if (0x32 != eeprom_data[0]) return false;
if (0x00 != eeprom_data[1]) return false;
if (0x01 != eeprom_data[2]) return false;
if (0x03 != eeprom_data[3]) return false;
return true;
}
//----------------------------------------------------------------
void setup()
{
int key;
Serial.begin(9600);
Wire.begin();
Serial.println("\tSP 150 cartridge mileage resetter");
Serial.println("Connect like this:");
Serial.println(" TOP");
Serial.println("______________________________");
Serial.println("|o |GND| |+5V| |SCL| |SDA| <=");
Serial.println("| |GND| | 5V| | A5| | A4| ");
Serial.println("------------------------------");
Serial.println(" cartridge roller");
Serial.println("\n\r\n\r\tTo start, press 'm' or any button for test (not prog)...\n\r");
do
{
key = Serial.read();
}
while(-1 == key);
#if 0
for (uint8_t paddr = 0; paddr < 8; paddr++)
{
Serial.print("Scan phy ");
Serial.println(paddr);
for (uint8_t i = 0; i < 5; i++)
{
Serial.print("Read from ");
Serial.print(i);
Serial.print(".........");
Serial.println(ee_read(paddr, i));
}
}
return;
#endif
read_data(EERROM_PHY_ADDR);
Serial.println("Read:");
print_data();
if (true == erased)
{
Serial.println("ERROR! The EEPROM is erased or the connection / phy addr is incorrect.");
return;
}
if (true == z_filled)
{
Serial.println("ERROR! The EEPROM is Z filled.");
return;
}
if (false == test_magic())
{
Serial.println("ERROR! Invalid magic number.");
return;
}
prn_serial();
mod_serial();
reset_mileage();
Serial.println("\n\rModified:");
print_data();
prn_serial();
if ('m' != (char)key)
{
Serial.println("WARNING! The data was not modified in the EEPROM");
return;
}
write_data(EERROM_PHY_ADDR);
if (false == check_data(EERROM_PHY_ADDR))
{
return;
}
Serial.println("Fin");
}
void loop()
{
//do nothing
}
(Sobre el código de mierda, sí, no se optimizó ni por memoria (todos los datos se leen en una matriz), ni por rendimiento (la lectura y escritura se realiza byte a byte), ni por funcionalidad, pero para una tarea tan simple - y así lo hará)!
Nos FLASH Arduinka, abra cualquier terminal (I18n, 9600), la incorporada en el Arduino IDE va a hacer, restablezca el tablero, pulse cualquier tecla:
Después de eso, los será leído el contenido de la EEPROM, modificado, pero no escrito. Si el procedimiento continuó sin errores, reinicie la placa nuevamente, presione m, luego de lo cual se realizarán todos los pasos y se escribirán los datos modificados. Si algo salió mal, revise el diagrama nuevamente e intente nuevamente. Después de una puesta a cero exitosa, soldamos los cables e instalamos el cartucho en su lugar. El nivel de tóner debe ser del 100%.
Pido disculpas por el "agua", la instrucción de 3 líneas - muy poco para una publicación. Espero que la información haya sido útil, y además, el autor no es responsable de posibles daños al equipo, privación del servicio de garantía, todo lo que haga es bajo su propio riesgo y riesgo ...
Otra actualización, un volcado recibido de mi cartucho:
Read from phy addr 3................................Ok 0: 32 0 1 3 2 1 1 0 0 0 34 30 38 30 31 30 10: 16 5 4D 4D 1 2 11 70 0 0 0 0 14 14 5 21 20: 43 37 30 36 4D 39 30 33 31 39 35 20 0 45 0 0 30: 39 1 0 0 0 0 0 0 3E 4 0 0 0 0 0 0 40: 5 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50: 0 0 0 0 0 0 0 0 14 E 5 1B 14 E 5 1B 60: 0 0 0 0 0 0 0 0 77 2 0 0 0 0 0 0 70: C3 23 2A 0 16 0 0 55 0 0 0 0 0 0 0 0