Дело в том,что МФУ считает не количество израсходованного тонера, а количество отпечатанных страниц. Если стартовый картридж я отпечатал с достаточно большой степенью заполнения, то со вторым случился казус. Дело в том, что жена достаточно быстро влилась в местное сообщество англомам и часть работы, касающаяся обеспечения полиграфией, легла на нас. Так вот, всякие там контурные картинки для вырезания / раскрашивания / оригами заполняют тонером, от силы, 1% листа. В результате у меня оказался пустой по счетчику, но фактически полный процентов на 70 картридж. Нет, я не настолько беден, чтоб не позволить себе купить новый, не на столько экологически воспитан, чтоб испытывать моральные мучения, выкидывая исправное изделие на свалку, но от чувства, что тебя законно развели кресло начало дымиться, что и было порывом к действию.
Честно, не надеялся стать мамкиным хакером, учитывая что подобную аппаратную защиту очень сложно сломать. Рассчитывал, как минимум, на приличные алгоритмы шифрования и OTP память (однократно программируемая). Но реальность оказалась куда банальней. К счастью, по запросу обнуления картриджа, было много инструкций, а защитным чипом оказалась вполне себе распространенная I2C EEPROM AT24C01, распаянная с минимальным обвесом не плату. По большому счету, на следующей картинке, можно закончить статью:
Оригинальное видео
Любым программатором читаем содержимое микросхемы, зануляем ячейки, обведенные красными рамками и изменяем пару последних цифр серийного номера. Следует обратить внимание, что серийник текстовая строка, заканчивающаяся пробелом, так что менять надо в диапазоне 0...9 (0x30...0x39). Физический адрес микросхемы, распаянной на плате 0x03. Но Встречайте сапожника без сапог. Нет универсального программатора, по этому берем
В сборе это смотрится так:
Плату защиты я снял с картриджа, чтобы удостовериться, что приведенная в мануале распиновка соответствует реальности и получения физического адреса микросхемы на шине:
Контактные площадки, слева направо: GND, +5V, SCL. SDA.
Можно не мудрить с изготовлением переходника, а подпаять провода непосредственно на плату, не снимая ее с картриджа. Далее копируем мой говнокод в среду Arduino IDE:
#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}
(Про говнокод да, не оптимизировал ни по памяти (читаются все данные в массив), ни по производительности (чтение и запись выполняется побайтно), ни по функционалу, но для такой простой задачи и так сойдет!)
Прошиваем Ардуинку, открываем любой терминал (I18n, 9600бод), подойдет встроенный в Arduino IDE, сбрасываем платку, жмем любую кнопку:
После этого содержимое EEPROM будет прочитано, модифицировано, но не записано. Если процедура произошла без ошибок снова сбрасываем плату, нажимаем m, после чего выполнятся все этапы и модифицированные данные будут записаны. Если что пошло не так, повторно сверьтесь со схемой и повторите попытку. После удачного обнуления отпаиваем провода и устанавливаем картридж на место. Уровень тонера должен быть 100%.
Прошу прощения за воду, инструкция из 3х строчек очень мало для поста. Надеюсь, информация была полезной, и еще автор не несет ответственности за возможные повреждения техники, лишение гарантийного обслуживания, все что Вы делаете на Ваш страх и риск