Сброс пробега для RICOH SP 150SUw

от автора

Я не верю в теорию заговора производителей (электроники) о сокращении ресурса изделия, просто выполняется поиск оптимального соотношения цены / качества для определенного сегмента рынка. Но есть один момент, от которого у меня дико подгорает чуть ниже живота. Это производители принтеров. Мало того, что цены на расходники такие, что оправдать их может только наличие в составе крови младенца, рожденного девственницей, но и повсеместное «чипирование» картриджей, перешедшее уже в не самые бюджетные лазерные агрегаты. Но реально «бомбануло» меня от следующего изделия: RICOH SP 150SUw. Данный девайс был приобретен по причине переезда из Подмосковья в Минск и необходимостью копирования / печати большого количества документов для получения разрешения на работу / проживание. Выбор был основан на следующих требованиях: МФУ не дорогой, компактный, сетевая печать, наличие драйверов под Linux (Fedora / Mint / OpenSUSE). Герой статьи обладал всеми этими характеристиками, но один нюанс был не замечен. Кривой счетчик ресурса. Если интересно, как вылечить и

(Памятка для граммар-наци от человека с легкой степенью дисграфии)

Да, у меня есть проблемы с естественными языками. Я это осознаю. И предупреждаю / предполагаю, что в тексте допущено достаточно много орфографических и пунктуационных ошибок. Надеюсь, нет логических и технических. Это не желание возрождать «олбанию», просто так получается. Не могу позволить личного корректора, а написание статей мне не приносит прибыли, только отнимает время, но хочется поделиться идеями/мыслями с сообществом. На ошибки указывать не надо, не буду исправлять, так как к моменту исправления, с последующим вычитыванием, статья уже уйдет в глубины ленты Хабра и работа будет напрасной. Покорнейше прошу отнестись с пониманием, а если Вас передергивает от неправильно поставленной запятой, лучше не открывайте пост. Пожалейте свои и мои нервы, иначе —

добро пожаловать!

Дело в том,что МФУ считает не количество израсходованного тонера, а количество отпечатанных страниц. Если стартовый картридж я отпечатал с достаточно большой степенью заполнения, то со вторым случился казус. Дело в том, что жена достаточно быстро влилась в местное сообщество англомам и часть работы, касающаяся обеспечения полиграфией, легла на нас. Так вот, всякие там контурные картинки для вырезания / раскрашивания / оригами заполняют тонером, от силы, 1% листа. В результате у меня оказался «пустой» по счетчику, но фактически — полный процентов на 70 картридж. Нет, я не настолько беден, чтоб не позволить себе купить новый, не на столько экологически воспитан, чтоб испытывать моральные мучения, выкидывая исправное изделие на свалку, но от чувства, что тебя законно развели — кресло начало дымиться, что и было порывом к действию.

Честно, не надеялся стать «мамкиным хакером», учитывая что подобную аппаратную защиту очень сложно сломать. Рассчитывал, как минимум, на приличные алгоритмы шифрования и OTP память (однократно программируемая). Но реальность оказалась куда банальней. К счастью, по запросу обнуления картриджа, было много инструкций, а «защитным чипом» оказалась вполне себе распространенная I2C EEPROM AT24C01, распаянная с минимальным «обвесом» не плату. По большому счету, на следующей картинке, можно закончить статью:

Оригинальное видео

Любым программатором читаем содержимое микросхемы, «зануляем» ячейки, обведенные красными рамками и изменяем пару последних цифр серийного номера. Следует обратить внимание, что серийник — текстовая строка, заканчивающаяся пробелом, так что менять надо в диапазоне 0…9 (0x30…0x39). Физический адрес микросхемы, распаянной на плате — 0x03. Но… Встречайте сапожника без сапог. Нет универсального программатора, по этому берем PIC16F819 и PICKit 3, не, для продвижения в массы — Arduino UNO / Nano, пару резисторов на 4.7k (от 3k до 10k для этой задачи — сойдет), метр МГТФа или Вашего любимого провода и собираем следующую «схему»:

В сборе это смотрится так:

Плату «защиты» я снял с картриджа, чтобы удостовериться, что приведенная в мануале распиновка соответствует реальности и получения физического адреса микросхемы на шине:

Контактные площадки, слева направо: 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х строчек — очень мало для поста. Надеюсь, информация была полезной, и еще — автор не несет ответственности за возможные повреждения техники, лишение гарантийного обслуживания, все что Вы делаете — на Ваш страх и риск…

Еще один апдейт, дамп, полученный с моего картриджа:
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

ссылка на оригинал статьи https://habr.com/ru/post/541674/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *