Mileage reset for RICOH SP 150SUw

I do not believe in the conspiracy theory of manufacturers (electronics) to reduce the resource of the product, it is just looking for the best price / quality ratio for a specific market segment. But there is one moment from which I wildly burns just below the belly. These are the printer manufacturers. Not only are the prices for consumables such that they can be justified only by the presence of a virgin-born baby in the blood, but also by the widespread "chipping" of cartridges, which has already passed into not the most budgetary laser units. But what really "bombed" me from the following product: RICOH SP 150SUw. This device was purchased due to the move from the Moscow region to Minsk and the need to copy / print a large number of documents to obtain a work / residence permit.



The choice was based on the following requirements: MFP is not expensive, compact, network printing, availability of drivers for Linux (Fedora / Mint / OpenSUSE). The hero of the article possessed all these characteristics, but one nuance was not noticed. Curved resource counter. If you wonder how to cure and



(Memo for grammar-nazi from a person with mild dysgraphia)
, . . / , . , . «», . , , , / . , , , , . , , . , —



Welcome!



The thing is,that the MFP does not count the amount of used toner, but the number of printed pages. If I printed the starter cartridge with a sufficiently large degree of filling, then an incident happened to the second one. The fact is that my wife quickly joined the local community of Angloms and part of the work related to the provision of printing fell on us. So, all sorts of contour pictures for cutting / coloring / origami are filled with toner, at most, 1% of the sheet. As a result, I found myself "empty" on the counter, but in fact - a full 70 percent of the cartridge. No, I'm not so poor as not to afford to buy a new one, not so environmentally educated to experience moral torment, throwing a serviceable product into a landfill, but from the feeling that you were legally bred - the chair began to smoke, which was an impulse to action ...



Honestly, I didn’t hope to become a “mother’s hacker”, given that such hardware protection is very difficult to break. I counted, at least, on decent encryption algorithms and OTP memory (once programmable). But the reality turned out to be much more commonplace. Fortunately, there were many instructions on the request to reset the cartridge, and the "protection chip" turned out to be quite a widespread I2C EEPROM AT24C01 , soldered with a minimum "body kit" not a board. By and large, in the next picture, you can finish the article:





Original video



With any programmer, we read the contents of the microcircuit, "zero" the cells outlined in red frames and change the last couple of digits of the serial number. Please note that the serial is a text string ending with a space, so you need to change it in the range 0 ... 9 (0x30 ... 0x39). The physical address of the microcircuit soldered on the board is 0x03. But ... Meet a shoemaker without boots. There is no universal programmer, so we take PIC16F819 and PICKit 3, no, for promotion to the masses - Arduino UNO / Nano, a pair of 4.7k resistors (from 3k to 10k for this task - it will do), a MGTF meter or your favorite wire and assemble the next one "Scheme":







Assembled it looks like this:







I removed the "protection" board from the cartridge in order to make sure that the pinout given in the manual corresponds to reality and to obtain the physical address of the microcircuit on the bus:





Contact pads, from left to right: GND, + 5V, SCL. SDA.



You don't have to be smart with the manufacture of the adapter, but solder the wires directly to the board without removing it from the cartridge. Next, copy my shitty code to the Arduino IDE:



Shitcode
#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
}

      
      







(About the shit code - yes, it did not optimize either by memory (all data is read into an array), or by performance (reading and writing is performed byte by byte), or by functionality, but for such a simple task - and so it will do!)



We flash Arduinka, open any terminal (I18n, 9600 baud), the built-in Arduino IDE will do, reset the board, press any button:







After that, the contents of the EEPROM will be read, modified, but not written. If the procedure proceeded without errors, reset the board again, press m, after which all the steps will be performed and the modified data will be written. If something went wrong, check the diagram again and try again. After successful zeroing, we solder the wires and install the cartridge in place. The toner level should be 100%.



I apologize for the "water", the instruction of 3 lines - very little for a post. I hope the information was useful, and also - the author is not responsible for possible damage to equipment, deprivation of warranty service, everything you do is at your own peril and risk ...



Another update, a dump received from my cartridge:



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 

      
      






All Articles