- Serie completa AT89xx: AT89C51 AT89C52 AT89S52 AT89S53 AT89S54 AT89S58 AT89S64 AT89C1051 AT89C2051 AT89C4051 AT89C55 AT89C55WD AT89C5131A AT89C51WD AT89C51ED2 AT89C51CC01 AT89S51 AT89C51CC02 AT89C51CC03 AT89C51RB2 AT89C51RC AT89C51RD2 AT89C51RD-CM AT89C51RC2 AT89C51ID2 AT87C5101 AT89C1051U AT89C2051X2 AT89C5130AM AT89C5130A AT89C5131AL AT89C5131AM AT89C51AC3 AT89C5132 AT89C51AC2 AT89C51CC03C AT89C51SND1C AT89C51CC03U AT89C51IC2 AT89C51RE2 AT89C51SND2 AT89LP2051 AT89LP2052 AT89LP213 AT89LP214 AT89LP216 AT89LP4051 AT89LP4052 AT89LP828 AT89LP428 AT89LS51 AT89LS52 AT89LV51 AT89LS53 AT89LS8252 AT89LV52 AT89LV55 AT89S2051 AT89S4051 AT89S8252 AT89S8253 ...
- Serie completa AT90xx: AT90S1200 AT90S2323 AT90S2343 AT90S2331 AT90S4433 AT90S8515 AT90S8535 AT90S4414 AT90S4434 AT90S2313 90S1200 90S2323 90S2343 90S2331 90S4433 90S8515 90S8535 90S4414 90S4434 90S2313 ...
- Serie AT90CAN/PWM/USB/xx: AT90CAN32 AT90CAN64 AT90CAN128 AT90PWM2 AT90PWM216 AT90PWM2B AT90PWM3 AT90PWM316 AT90PWM3B AT90USB1286 AT90USB1287 AT90USB162 AT90USB646 AT90USB647 AT90USB82 ...
-
- Serie AT91SAMxx: AT91SAM9XE512 AT91SAM9XE256 AT91SAM9XE128 AT91SAM7S64B AT91SAM7S32B AT91SAM7SE512 AT91SAM7SE256 AT91SAM7SE32 AT91SAM7XC512 AT91SAM7XC256 AT91SAM7XC128 AT91SAM7X512 AT91SAM7X256 AT91SAM7X128 AT91SAM7S161 AT91SAM7S512 AT91SAM7S256 AT91SAM7S128 AT91SAM7S64 AT91SAM7S321 ...
- Serie ATFxx: ATF16V8B ATF16V8BL ATF16V8BQ ATF16V8BQL ATF16LV8C ATF16LV8CEXT ATF16V8C ATF16V8CEXT ATF16V8CZ ATF20V8B ATF20V8BL ATF20V8BQ ATF20V8BQL ATF22LV10C ATF22LV10CEXT ATF22LV10CUES ATF22LV10CZ ATF22LV10CQZ ATV22V10 ATF22V10B ATF22V10BQ ATF22V10BL ATF22V10BQL ATF22V10C ATF22V10CEXT ATF22V10CUES ATF22V10CZ ATF22V10CQZ ATF22V10CZUES ATF22V10CQZUES ATF1500A ATF1500ABV ATF1500ABVL ATF1500 ATF1500L ATF1502AS ATF1502ASL ATF1502ASV ATF1502ASVL ATF1504AS ATF1504ASVL ATF1508 ATF1508AS ATF1508ASV ATF2500C ATF2500CL ATF2500CQ ATF2500CQL ATF750C ATF750CEXT ATF750CL ATF750LVC ATF750LVCCEXT ATF750LVCEXT ATF750LVCL ATV2500 ATV2500H ATV2500L ATV2500B ATV2500BL ATV2500BQL ATV5000 ATV5000L ATV750 ATV750B ATV750BL ATV750L ...
- Serie AT88scxx/90scxx: AT88SC0104 AT88SC0104C AT88SC0204 AT88SC0204C AT88SC0404 AT88SC0404C AT88SC0808 AT88SC0808C AT88SC1003 AT88SC101 AT88SC102 AT88SC1281 AT88SC12816C AT88SC150 AT88SC153 ...
La función de inicio pone VDD en alto, espera, luego establece SDA según el bit de dirección, así como SCK. Espera, pone SCK en bajo, espera nuevamente y luego pone SDA en alto según la dirección. Esto se repite 2 veces más, excepto que en la cuarta vez, SDA se establece en bajo porque queremos leer los datos en las EEPROM, no escribir.
void epsnCartStart(unsigned char addr,unsigned char write){
char i = 0; // Usado para contar
char tmp = 0; // Variable de almacenamiento temporal
_ecVDDLat = 1; // Pone VDD en alto
DelayMS(1); // Espera a que las EEPROM estén listas
_ecRSTLat = 1; // Habilita las EEPROM para la comunicación
tmp = addr; // Copia la dirección en la variable temporal
if(write){ // Si queremos escribir
temp |= 0x08; //pone el 4º bit en alto ( '|' significa OR )
}
while(i<4){
_ecSDALat = (tmp&0x01); // Pone SDA en alto o bajo según el LSB
wait(); // Espera aproximadamente 50uS
_ecSCKLat = 1; // Pone el reloj en alto
wait(); // Espera otros 50uS
_ecSCKLat = 0; //Pone el reloj en bajo
wait(); // Espera otros 50uS
tmp>>=1; //Desplaza a la derecha todos los bits de tmp en uno
i++; // incrementa el contador en uno
}
}
La siguiente función a implementar es stop:
void epsnCartStop(){
_ecRSTLat = 0; //Pone RST en bajo
DelayMS(1); //Espera un poco
_ecVDDLat = 0; //Apaga las EEPROM
}
Y finalmente la función de lectura:
unsigned char epsnCartRead(){
char i=0;
char temp = 0x00;
while(i<8){
temp>>=1; // Desplaza a la derecha la variable temporal en uno
_ecSCKLat = 1; //pone SCK en alto
wait(); //Espera aproximadamente 50uS
if(_ecSDA){ // Si SDA está en alto
temp |= 0x80; // Entonces pone el bit 7 en alto
}
_ecSCKLat = 0; // Vuelve a poner SCK en bajo
wait(); //espera otros 50uS
i++; // Incrementa el contador en 1
}
return temp; // Devuelve lo que hemos leído
}
Ahora tenemos las funciones básicas que necesitamos para leer datos de los cartuchos de tinta; configuremos nuestro microcontrolador y establezcamos los resultados.

Inicie su IDE, en mi caso es MPLab, cree un nuevo proyecto para su chip y cree un programa "hola mundo" básico para asegurarse de que su chip funciona.
Nota: Mi programa "hola mundo" envía "Hello World!\r\n" por Serie a mi computadora a 38400bps
#include <p18f2550.h> // Archivo de inclusión específico para este chip
#include <usart.h> // Funciones USART
#include <stdio.h> // printf(format,...), sprintf(string, format, ...), etc...
#include "delay.h"
void main(){
PORTA = 0x00;
PORTB = 0x00;
PORTC = 0x00;
TRISA = 0x00;
TRISB = 0x00;
TRISC = 0x00;
ADCON1 = 0x0F; // Esto es un problemático, ¡nunca olvide volver a poner los pines analógicos en digitales! :P
TRISCbits.TRISC7 = 1; // Convierte el pin RX en una entrada (consulte la hoja de datos de su chip)
OpenUSART(USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ADDEN_OFF & USART_BRGH_HIGH & USART_CONT_RX & USART_EIGHT_BIT & USART_ASYNCH_MODE,78);
while(1){
putrsUSART("Hello World!\r\n"); // Envía un mensaje por Serie
LATAbits.LATA0 = ~LATAbits.LATA0; //Parpadea un LED activándolo y desactivándolo
DelayMS(500); // Una función de retardo que he creado
}
}
Esto se configuró con una fuente de alimentación de 3.3V porque los cartuchos de tinta funcionan a 3.3 Voltios.
¿Por qué un programa "hola mundo"? Bueno, cada vez que configuro un nuevo proyecto en una placa de pruebas, siempre quiero asegurarme de que los bits de configuración sean correctos y de que no estoy recibiendo basura en el terminal.
¡Creo que esto es una buena práctica y todo el mundo debería hacerlo! :P
Si su chip está haciendo algo simple como el mío, ¡Genial! Pasemos al siguiente paso.
Implementando nuestra biblioteca
Ahora queremos incluir la biblioteca que creamos para la interfaz del microcontrolador con el cartucho de tinta.
En la parte superior del archivo añada:
#include "epsnCart.h" // el nombre del archivo de cabecera que creamos anteriormente
Dado que este microcontrolador está actuando como host, controla las señales SCK, SDA y RST. Así que asegúrese de que sean salidas; añada esto con los otros registros TRIS dentro de main():
epsnCartInit();
El siguiente fragmento de código es el que usé dentro de while(1) {
Solicita una dirección comenzando en 0x00 y luego se incrementa en uno después de 32 bytes leídos:
void main(){
char addr = 0, i = 0;
char string[40];
...
epsnCartInit(); // Inicializa los pines usados
while(1){ // ¡Bucle infinito!
sprintf(string, "Leyendo cartucho con addr: 0x%02X\r\n",addr);
putsUSART(string); // imprime un mensaje como: Leyendo cartucho con addr: 0x03
epsnCartStart(addr,0); // Inicia enviando la dirección en modo lectura
i = 0;
while(i<32){ // sigue en bucle hasta que i ya no sea menor que 32
sprintf(string,"0x%02X,",epsnCartRead());
putsUSART(string);
i++;
}
epsnCartStop(); //pone RST en bajo
putrsUSART("\r\n");
addr++
if(addr>7){
addr = 0;
}
DelayMS(500);
}
}
Serie ATTinyxx: ATtiny4 ATtiny5 ATtiny10 ATtiny11 ATtiny12 ATtiny13 ATtiny15 ATtiny20 ATtiny22 ATtiny24 ATtiny25 ATtiny26 ATtiny261 ATtiny28 ATtiny2313 ATtiny40 ATtiny4313 ATtiny43 ATtiny44 ATtiny45 ATtiny461 ATtiny48 ATtiny84 ATtiny85 ATtiny861 ATtiny87 ATtiny88 ATtiny4A ATtiny5A ATtiny10A ATtiny11A ATtiny12A ATtiny13A ATtiny15A ATtiny20A ATtiny22A ATtiny24A ATtiny25A ATtiny26A ATtiny261A ATtiny28A ATtiny2313A ATtiny40A ATtiny4313A ATtiny43A ATtiny44A ATtiny45A ATtiny461A ATtiny48A ATtiny84A ATtiny85A ATtiny861A ATtiny87A ATtiny88A ATtiny4V ATtiny5V ATtiny10V ATtiny11V ATtiny12V ATtiny13V ATtiny15V ATtiny20V ATtiny22V ATtiny24V ATtiny25V ATtiny26V ATtiny261V ATtiny28V ATtiny2313V ATtiny40V ATtiny4313V ATtiny43V ATtiny44V ATtiny45V ATtiny461V ATtiny48V ATtiny84V ATtiny85V ATtiny861V ATtiny87V ATtiny88V ...
Serie ATMegaxx: ATmega16 ATmega162 ATmega164 ATmega165 ATmega168 ATmega169 ATmega128 ATmega1280 ATmega1281 ATmega2560 ATmega2561 ATmega328 ATmega48 ATmega32 ATmega324 ATmega325 ATmega3250 ATmega329 ATmega3290 ATmega64 ATmega640 ATmega645 ATmega6450 ATmega649 ATmega6490 ATmega8 ATmega88 ATmega8515 ATmega8535 ATmega16L ATmega162L ATmega164L ATmega165L ATmega168L ATmega169L ATmega128L ATmega1280L ATmega1281L ATmega2560L ATmega2561L ATmega328L ATmega48L ATmega32L ATmega324L ATmega325L ATmega3250L ATmega329L ATmega3290L ATmega64L ATmega640L ATmega645L ATmega6450L ATmega649L ATmega6490L ATmega8L ATmega88L ATmega8515L ATmega8535L ATmega16P ATmega162P ATmega164P ATmega165P ATmega168P ATmega169P ATmega128P ATmega1280P ATmega1281P ATmega2560P ATmega2561P ATmega328P ATmega48P ATmega32P ATmega324P ATmega325P ATmega3250P ATmega329P ATmega3290P ATmega64P ATmega640P ATmega645P ATmega6450P ATmega649P ATmega6490P ATmega8P ATmega88P ATmega16A ATmega162A ATmega164A ATmega165A ATmega168A ATmega169A ATmega128A ATmega1280A ATmega1281A ATmega2560A ATmega2561A ATmega328A ATmega48A ATmega32A ATmega324A ATmega325A ATmega3250A ATmega329A ATmega3290A ATmega64A ATmega640A ATmega645A ATmega6450A ATmega649A ATmega6490A ATmega8A ATmega88A ATmega8515A ATmega8535A ...
Probablemente esté mirando la línea "sprintf(string,"0x%02X,",epsnCartRead());" y pensando "¿Eh?"
sprintf es una función de formateo de cadenas, muy parecida a printf, excepto que guarda la cadena formateada en la variable string.
"0x%02X," devolverá una cadena con un valor hexadecimal legible, ej: 0xFE, y epsnCartRead() devuelve un valor que se leyó del cartucho de tinta.
Esto se configuró con una fuente de alimentación de 3.3V porque los cartuchos de tinta funcionan a 3.3 Voltios.
Programé esto en mi microcontrolador, desconecté el cabezal de impresión de la impresora para evitar interferencias.
Luego conecté los 3 cartuchos de tinta que tenía y lo encendí.
Nota: En este punto, si ejecutó este código por primera vez, espero que tenga problemas. Como yo, he revisado el código docenas de veces, cambiándolo aquí y allá para que funcione. Es normal que no funcione la primera vez. Es una gran experiencia de aprendizaje descubrir qué salió mal. :P


Uf, después de todo ese trabajo duro, ¡está vivo!!!
Esto es lo que obtuve en mi terminal:
Addr: 0x01
57,07,02,F2,00,00,00,00,00,50,98,6A,EB,D3,54,A3,39,50,14,15,31,93,E4,B4,24,96,57,04,35,F5,E4,14,
Addr: 0x02
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,
Addr: 0x03
03,00,02,F2,00,00,00,00,00,50,C8,1C,2A,A5,82,00,3B,50,16,17,31,93,E4,B4,24,96,57,04,35,F5,E4,34,
Addr: 0x04
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,
Addr: 0x05
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,
Addr: 0x06
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,
Addr: 0x07
03,00,02,F2,00,00,00,00,00,50,30,9F,74,81,D1,40,3B,50,1A,1B,31,93,E4,B4,24,96,57,04,35,F5,E4,74,
Ahora, observe aquí cómo solo las direcciones impares devolvieron datos, excepto 0x05.
Eso es porque 0x05 es la dirección del cartucho de tinta magenta, que lamentablemente no tengo :(
Rutinas de código de emulación
¡Ahora la parte divertida!
Necesito que la impresora piense que hay cuatro cartuchos de tinta instalados. Para hacer esto, primero debemos implementar algunas funciones de emulación. Nuevamente, basándome en conocimientos previos, he predefinido estas en el encabezado:
• #define epsnCartReady() (_ecRST)
• extern unsigned char espnCartGetAddr(void);
• extern void epsnCartOut(unsigned char data);
• extern unsigned char epsnCartIn(unsigned char data);
epsnCartReady es una macro que se usa para verificar si la impresora tiene RST en alto.
epsnCartGetAddr devuelve la dirección que está solicitando el cabezal de impresión.
epsnCartOut se usa para enviar datos al controlador Host.
epsnCartIn se usa para recibir datos del Host cuando está escribiendo.
Así que esta vez, en lugar de controlar SCK para que suba o baje, necesitamos esperar a que suba o baje, pero si el reset se pone en bajo, detener todo y retornar:
unsigned char espnCartGetAddr(void){
char i=0;
char temp = 0x00;
while((i<4) && _ecRST){ // Bucle mientras i < 4 y RST esté en ALTO
temp>>=1; // Desplaza Temp a la derecha en uno
while((!_ecSCK) && (_ecRST)); // Espera a que el reloj suba
if(_ecSDA){// solo si SDA está en alto
temp |= 0x08; // entonces establece el 4º bit
}
while((_ecSCK) && (_ecRST));// espera a que el reloj baje
i++; // incrementa el contador
}
if(_ecRST == 0){
return 0xFF;//si RST bajó, significa que el Host mató la señal
}
return temp; //devuelve la dirección y el bit de lectura/escritura
}
Y la función Out:
void epsnCartOut(unsigned char data){
char i = 0;
char temp = data;
_ecSDATRIS = 0; // configura SDA como salida
_ecSDALat = 0; // pone SDA en bajo
while((i<8) && _ecRST){ // Bucle mientras i < 8 y RST esté en alto
while(!_ecSCK && _ecRST); // Espera a que SCK suba
_ecSDALat = temp&0x01; // Pone SDA en alto o bajo según el primer bit
while(_ecSCK && _ecRST); // Espera a que SCK baje
temp>>=1; // Desplaza los datos a la derecha en uno
i++; // incrementa el contador en uno
}
_ecSDATRIS = 1; // Configura SDA como salida (nota: probablemente debería ser entrada)
_ecSDA = 1; // no estoy seguro de si esto es necesario
}
Y finalmente la función In:
unsigned char epsnCartIn(void){
unsigned char i=0;
char temp = 0x00;
while((i<8) && _ecRST){ // Bucle mientras i < 8 y RST esté en alto
temp>>=1; // Desplaza temp a la derecha en uno
while(!_ecSCK && _ecRST); // Espera a que SCK suba
if(_ecSDA){ // solo si SDA está en alto
temp |= 0x80; // Establece el 8º bit
}
while(_ecSCK && _ecRST);// Espera a que SCK baje
i++; // Incrementa el contador en uno
}
return temp; // devuelve lo que hemos recibido
}
Así que lo que sucede ahora es que necesitamos configurar nuestro microcontrolador para que contenga los datos que hemos volcado. Luego permitir que el host de la impresora lea y escriba en él.
El problema con esto es que no quiero que mis cartuchos de tinta falsos se gasten nunca, así que para hacer eso tenemos un volcado fijo que el microcontrolador cargará en la RAM cada vez que se inicie. Esto permitirá que la impresora no solo lea, sino que también escriba en él. Cuando el microcontrolador se apague y se vuelva a encender, los datos anteriores se restauran. (Doble beneficio)
Almacenando matrices de datos en el microcontrolador, coloque esto en algún lugar debajo de los includes, pero antes de void main():
Nota: Esto puede variar entre diferentes familias de microcontroladores, pero funciona bien con mi PIC18F.
ram unsigned char black[32] = {0x10,0x00,0x02,0xF2,0x00,0x00,0x00,0x00,0x00,0x50,0x90,0x6A,0x17,0xE3,0x54,0xA3,
0x39,0x50,0x14,0x15,0x31,0x93,0xE4,0xB4,0x24,0x96,0x57,0x04,0x35,0xF5,0xE4,0x14};
ram unsigned char cyan[32] = {0x4C,0x04,0x01,0xF2,0x00,0x00,0x00,0x00,0x00,0x50,0x90,0x5A,0x19,0xF0,0x94,0x03,
0x3B,0x50,0x16,0x17,0x31,0x93,0xE4,0xB4,0x24,0x96,0x57,0x04,0x35,0xF5,0xE4,0x34};
ram unsigned char yellow[32] = {0x4A,0x04,0x01,0xF2,0x00,0x00,0x00,0x00,0x00,0x50,0x90,0x6A,0x8B,0xC9,0xA4,0x41,
0x3B,0x50,0x1A,0x1B,0x31,0x93,0xE4,0xB4,0x24,0x96,0x57,0x04,0x35,0xF5,0xE4,0x74};
Nota: ¿Se ha dado cuenta de que no tengo Magenta? Eso es porque es el cartucho de tinta que no tengo.
El siguiente paso es escribir una rutina que verifique qué cartucho de tinta se está solicitando y permitir que el host lea y escriba en él.
Así es como lo hice:
void main(){
unsigned char* inkPtr;
char addr = 0x00;
...
While(1){
epsnCartStartListen();
while(epsnCartReady()==1); // Espera a que la señal Ready vuelva a bajo
while(epsnCartReady()==0);
addr = espnCartGetAddr();
if(addr&0x7 == 0x01){ // si los primeros 3 bits son iguales a 0x01
inkPtr = &black[0]; // Apunta a los datos del negro
}else if(addr&0x7 == 0x03){ // si los primeros 3 bits son iguales a 0x03
inkPtr = &cyan[0]; // Apunta a los datos del cian
}else if(addr[i]&0x7 == 0x05){
inkPtr = &cyan[0]; // Esto debería ser Magenta, pero no lo tengo
}else if(addr[i]&0x7 == 0x07){
inkPtr = &yellow[0];
}else{
continue: // Vuelve al principio del bucle principal porque no conocemos esta dirección
}
if(addr&0x08){//Modo Escritura – Comprobar si el Host quiere escribir
while(epsnCartReady()){//mantener el bucle hasta que RST baje
*inkPtr = epsnCartIn();
// los datos a los que apunta el puntero
// se sobrescriben con nuevos datos
inkPtr++; // Incrementa el Puntero
}
}else{//Modo Lectura
while(epsnCartReady()){
epsnCartOut(*inkPtr);
// Salida de los datos a los que apunta el Puntero
inkPtr++; // Incrementa el puntero
}
}
}
}
¡Éxito!
Después de programar este nuevo código y encender mi impresora, la impresora aceptó 4 cartuchos imaginarios gracias a mi microcontrolador. También me sorprende que aceptara los datos del cartucho cian también para el magenta.
Este proyecto completo es un ejemplo perfecto de cómo la ingeniería inversa puede desbloquear el potencial del hardware propietario. Al comprender el protocolo de comunicación, efectivamente realicé una extracción de firmware de los contenidos de la EEPROM, aunque los datos eran pequeños. En un sistema más complejo, uno podría necesitar volcar la memoria flash de un microcontrolador para analizar su código. La EEPROM en estos cartuchos actúa como un pequeño almacenamiento seguro, pero sin ningún bit de bloqueo o cifrado, es trivial leer los datos. Algunos cartuchos de impresora modernos incluyen un mecanismo de bloqueo que evita la reutilización, pero aquí la única protección era el protocolo oscuro. Esta oscuridad no es seguridad, como lo demostré al emular con éxito el cartucho faltante. La capacidad de duplicar la identidad del cartucho permitiría rellenados infinitos, que es exactamente lo que los fabricantes de impresoras intentan evitar. A menudo utilizan fusibles o bits programables de una sola vez para marcar un cartucho como vacío, pero esta EEPROM es completamente reescribible. Mi microcontrolador ahora actúa como un hombre en el medio, interceptando solicitudes de lectura y sirviendo datos almacenados. Esto es esencialmente una forma simple de recuperación de firmware para la impresora, ya que restauré la funcionalidad completa sin la pieza original. El código que escribí es pequeño pero efectivo, y podría adaptarse a otros dispositivos con EEPROM serie similares. A menudo me he preguntado si los fabricantes de impresoras dejan intencionalmente estas vulnerabilidades para permitir cartuchos de terceros. Independientemente, este proyecto me enseñó mucho sobre protocolos de hardware y sistemas embebidos. El MCU que usé, un PIC18F, fue más que capaz de manejar los requisitos de temporización. De hecho, podría haber usado un microcontrolador más pequeño, pero el PIC me dio suficiente espacio para futuras expansiones. Toda la configuración funciona a 3.3V, coincidiendo con el voltaje interno del cartucho. Un desafío fue asegurar que la línea SDA se manejara correctamente en ambas direcciones, ya que la impresora y mi MCU alternan el control. Tuve que gestionar cuidadosamente los registros TRIS para evitar conflictos en el bus. Este es un problema común en cualquier sistema multi-maestro, pero aquí es un solo host y un emulador esclavo. Los diagramas de tiempos de la patente fueron invaluables; me dieron las relaciones exactas de los flancos de reloj. Sin eso, habría pasado días adivinando. Esta experiencia subraya la importancia de la documentación, incluso si está oculta en patentes. También aprendí que a veces la solución más simple es la mejor: simplemente almacenar el volcado en RAM y actualizarlo al encender. Esto asegura que los niveles de tinta nunca disminuyan, lo cual es una ventaja adicional. Sin embargo, para un producto real, necesitaría considerar el desgaste y la protección contra pérdida de energía. Pero para un proyecto de hobby, esto es perfecto. Ahora confío en que puedo abordar tareas de ingeniería inversa más complejas en el futuro. El siguiente paso podría implicar espiar la comunicación entre la impresora y los controladores del motor del cabezal de impresión. Eso me permitiría controlar con precisión el movimiento XY para mi conversión a impresora 3D. Pero esa es una historia para otro instructable. Por ahora, estoy encantado de que mi emulación funcione perfectamente. Incluso lo he probado con múltiples ciclos de alimentación y conserva los datos cada vez. La matriz RAM se reinicializa con los valores codificados en cada arranque, por lo que el cartucho siempre aparece como nuevo. La impresora nunca se queja de cartuchos falsificados porque los datos son una copia perfecta de uno genuino. Esto es esencialmente una duplicación de la identidad del cartucho original. En un entorno comercial, esta técnica podría usarse para clonación no autorizada, pero aquí es para reutilización legítima. Tengo cuidado de usarlo solo para mis propios proyectos. El conocimiento adquirido en este ejercicio es aplicable a muchos otros productos electrónicos de consumo que utilizan memorias serie simples. Muchos dispositivos tienen EEPROM similares que almacenan datos de calibración o números de serie. Al leerlos, puede hacer copias de seguridad y restaurar información crítica. Esa es una forma de extracción de firmware en sí misma, incluso si el firmware es solo datos de configuración. He visto proyectos donde la gente descifra el firmware de un medidor inteligente usando métodos similares. Pero eso implica criptografía mucho más compleja y a menudo un bit de bloqueo para evitar la lectura. Aquí no hay tal bloqueo, así que está completamente abierto. Algunos podrían argumentar que esto es una falla de seguridad, pero para mí es una oportunidad. Ya he comenzado a planificar una versión que usa un MCU ATtiny más pequeño para ahorrar espacio. El tamaño del código es pequeño, por lo que podría caber en un encapsulado de 8 pines. Incluso podría agregar una característica para simular diferentes tipos de cartuchos mediante un interruptor DIP. Eso sería útil para probar varias tintas. Las posibilidades son infinitas. También estoy considerando compartir mi código en GitHub para que otros lo adapten. Este enfoque de código abierto fomenta la colaboración y la innovación. Muchos hackers ya han hecho cosas similares con otras marcas de impresoras. La comunidad es muy activa en la ingeniería inversa de consumibles para reducir el desperdicio y ahorrar dinero. Es un beneficio mutuo para el medio ambiente y el usuario. Espero que mi instructable inspire a otros a mirar debajo del capó de sus propios dispositivos. Con un analizador lógico y un poco de paciencia, puede descubrir protocolos ocultos. La clave es comenzar poco a poco, como hice con esta simple EEPROM. Una vez que domine lo básico, puede pasar a objetivos más desafiantes. Espero con ansias el día en que pueda imprimir en 3D con mi impresora convertida. Este es solo el primer hito. El siguiente gran paso es interactuar con los controladores del cabezal de impresión y construir el marco mecánico. Pero eso es un proyecto para otra temporada. Por ahora, estoy saboreando esta victoria. Es una prueba tangible de que la ingeniería inversa no es solo para hackers; es una habilidad valiosa para cualquier ingeniero. Al comprender cómo funcionan las cosas en el nivel más bajo, realmente puede hacerlas suyas. Ese es el espíritu de hacer y experimentar. Así que adelante, tome una impresora vieja y comience a sondear. Se sorprenderá de lo que puede lograr. Y recuerde, siempre documente sus hallazgos para que otros puedan aprender de su viaje. Así es como nuestra comunidad crece y evoluciona. Con eso, concluiré esta sección. Disfrute del resto del instructable y ¡feliz hacking!