Продлеваем жизнь картриджа

в 4:22, , рубрики: Samsung, Блог компании Грамбо, картриджи, метки: ,

Некоторые производители лазерных принтеров встраивают в картриджи чипы EEPRОM памяти для хранения актуальной информации о состоянии картриджа. Конкретно здесь рассматривается Samsung MLT-D104X (на 700 страниц) или MLT-D104S (на 1500 страниц) для таких принтеров как Samsung ML-1665. Ситуация с данными картриджами следующая. В картридж встроен EEPROM чип с интерфейсом I2C… Картриджи стоят достаточно дорого (около 70$), поэтому есть вариант отнести картридж в неофициальный сервис, где его смогут перезаправить, а также сбросить счетчик страниц. При этом сброс счётчика стоит дороже самой заправки! Поэтому возникла идея научиться сбрасывать счетчик самим и отдавать картридж только на перезаправку (примерно за 10 баксов).

Итак, имеем:
Принтер Samsung ML-1665. Картридж Samsung MLT-D104X.

Хотим:
Сбросить счетчик страниц ну или продлить его работу иным способом.

Решение:
Неожиданно возникла идея применить нашу плату для прототипирования роботов Grambo Pi к, казалось бы, проблеме совсем иного рода!

Как уже упоминалось в нашей первой статье плата Grambo Pi имеет несколько стандартных аппаратных интерфейсов, в том числе I2C. Поскольку плата сама по себе программируемая, её можно временно превратить в преобразователь USB<->I2C. Сделать это оказалось неожиданно просто — около одной странички текста на встраиваемом скрипте. К этому дописан GUI скрипт на Питон-е, который, собственно, общается с картриджем и выдаёт, что в нём записано.

image

В общем, картридж подключаем к шине I2C.

image

Контакты на картридже подписаны…, прям как-будто специально, чтобы люди сами EEPROM перезаписывали!

image

Проблемой оказалось установить, что же конкретно за EEPROM чип установлен (т.к. по маркировке в Google ничего не находится). Пришлось действовать перебором всех I2C адресов. Этот конкретный чип откликается на адреса 25, 30 и 40. По адресу 40, по всей видимости, располагается номер страницы памяти, а по адресу 30 оказались сами данные. Прочитав данные 2 раза до и после печати тестовой страницы удалось установить, какие именно байты меняются при печати страниц.

К сожалению, в явном виде выяснить, в каком именно виде закодировано число страниц, не удалось: меняется сразу 4 байта на глаз довольно случайным образом. Пытаться расшифровывать — лень. Поэтому было решено просто сохранить прошивку еще “живого” картриджа, а при приближении к критическим 700 страницам прошивку откатывать до сохранённого значения.

Таким образом, замена картриджа оттягивается на, надеемся, неограниченное время!

Исходные код скрипта для платы Grambo Pi

#include <supply>

new ioBuffer[32]

read( i2cAddr, chipAddr, cnt )
{
	new wr[2]
	wr[0] = chipAddr

    // IO operation at I2C bus.
	setLed( 3 )
	new res = i2cIo( i2cAddr, wr, 1, ioBuffer, cnt, 500 )
	setLed( 0 )

    // Operation result.
	setIo( 1, res )
	new i
	for ( i=0; i<cnt; i++ )
	    setIo( i+2, ioBuffer[i] )

    return res
}

write( i2cAddr, chipAddr, cnt )
{
    ioBuffer[0] = chipAddr
    new i
    for ( i=0; i<cnt; i++ )
        ioBuffer[i+1] = io( i+4 )

    // IO operation at I2C bus.
    setLed( 3 )
    new res = i2cIo( i2cAddr, ioBuffer, cnt+1, ioBuffer, 0, 500 )
    setLed( 0 )

    return res
}

main()
{
    new led = 0
    // Reset command at the very beginning.
    setIo( 0, 0 )
    setI2cEn( 1 )
    for ( ;; )
    {
        new i2cAddr
        new devAddr
        new cnt
        new res
        // Check if read operation?
        if ( io( 0 ) == 1 )
        {
            i2cAddr = io( 1 )
            devAddr = io( 2 )
            cnt     = io( 3 )
            res     = read( i2cAddr, devAddr, cnt )
            setIo( 1, res )
            setIo( 0, 0 )
        }
        // Else check if write operation?
        else if ( io( 0 ) == 2 )
        {
            i2cAddr = io( 1 )
            devAddr = io( 2 )
            cnt     = io( 3 )
            res     = write( i2cAddr, devAddr, cnt )
            setIo( 1, res )
            // Report operation is completed
            setIo( 0, 0 )
        }
        // Else it is unrecognized and just reset!
        else
            setIo( 0, 0 )
        // Some delay.
        msleep( 50 )

        // Blinking LED number 1.
        setLed( led )
        if ( led == 0 )
            led = 1
        else
            led = 0
        // When IO takes place both LEDs should blink.
    }
}

Исходные код GUI скрипта на Python
#!/bin/python

import xmlrpclib
from Tkinter import *
from time import sleep
from supplyctrlusb import *

# Data obtained with pages count 580
# 16 32 3 255 <183> 254 <122 0   63>  255 <183> 254 51 16 32 3 0 0 0 0 0 0 0 0 0 0 0 8 37 15 232 16 0 0 0 0 0 0 0 0 16 0 0 0 0 0 0 0 0 0 0 28 133 101 101 0 160 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
# Data obtained with pages count 581
# 16 32 3 255 <151> 254 <138 255 201> 255 <151> 254 51 16 32 3 0 0 0 0 0 0 0 0 0 0 0 8 37 15 232 16 0 0 0 0 0 0 0 0 16 0 0 0 0 0 0 0 0 0 0 28 133 101 101 0 160 0 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
# After zerowing attempt.
# 16 32 3 255 124 254 35 255 189 255 124 254 51 16 32 3 0 0 0 0 0 0 0 0 0 0 0 8 37 15 232 16 

class Application(Frame):
    
    DELAY = 0.1
    TRIES = 50
    FRAME = 4
    
    def read( self, i2cAddr, devAddr, cnt ):
        print "read " + str(i2cAddr) + ", " + str(devAddr) + ", " + str(cnt)
        io = self.io
        io.setIo( 1, i2cAddr )
        io.setIo( 2, devAddr )
        io.setIo( 3, cnt )
        # Start reading data
        pp = io.io( 0 )
        print "pp = " + str( pp )
        io.setIo( 0, 1 )
        # Waiting for completion
        for t in range( self.TRIES ):
            finished = io.io( 0 )
            print "finished = " + str( finished)
            if ( finished == 0 ):
                res = io.io( 1 )
                print "res = " + str( res )
                if res != 0:
                    return (res, [])
                d = []
                for i in range(cnt):
                    v = io.io( 2+i )
                    d.append( v )
                print "data", d
                return ( res, d )
            sleep( self.DELAY )
        print "Read timeout"
            
    def write( self, i2cAddr, devAddr, data ):
        io = self.io
        io.setIo( 1, i2cAddr )
        io.setIo( 2, devAddr )
        cnt = len( data )
        print "write " + str(i2cAddr) + ", " + str(devAddr) + ", " + str(cnt)
        io.setIo( 3, cnt )
        for i in range( cnt ):
            io.setIo( 4+i, data[i] )
        # Start writing data.
        io.setIo( 0, 2 )
        # Waiting for completion
        for t in range( self.TRIES ):
            finished = io.io( 0 )
            print "finished = " + str( finished)
            if ( finished == 0 ):
                res = io.io( 1 )
                print "res = " + str( res )
                return res
            sleep( self.DELAY )
        print "Write timeout"
        
    def readI2c( self ):
        # Clear output
        self.text.delete( 1.0, END )

        i2cAddr = int( self.i2cAddr.get(), 2 ) # Binary number
        print "i2cAddr = " + str( i2cAddr )
        devAddr = int( self.devAddr.get() )
        print "devAddr = " + str( devAddr )
        cnt = int( self.bytesCnt.get() )
        print "bytesCnt = " + str( cnt )
        
        # Return before real device interaction.
        #~ return
        
        for i in range( 0, cnt, self.FRAME ):
            res, data = self.read( i2cAddr, devAddr+i, self.FRAME )
            if res > 0:
                print "res = " + str( res )
                return
            for k in range( len( data ) ):
                self.text.insert( END, str( data[k] ) + " " )
        
    def writeI2c( self ):
        i2cAddr = int( self.i2cAddr.get(), 2 ) # Binary number
        print "i2cAddr = " + str( i2cAddr )
        devAddr = int( self.devAddr.get() )
        print "devAddr = " + str( devAddr )
        cnt = int( self.bytesCnt.get() )
        print "bytesCnt = " + str( cnt )
        
        data = self.text.get( 1.0, END )
        print "data = ", data
        d = data.split()
        print "data = ", d
        data = []
        for i in range( len(d) ):
            data.append( int(d[i]) )
        print "data = ", data
        
        # Return before executing critical changes.
        #~ return

        for i in range( 0, cnt, self.FRAME ):
            d = []
            for k in range( self.FRAME ):
                d.append( data[i+k] )
            res = self.write( i2cAddr, devAddr+i, d )
            if res > 0:
                print "res = " + str( res )
                return
            print "Ready"
        
    def toString( self ):
        stri = self.text.get( 1.0, END )
        d = stri.split()
        stri = ""
        cnt = len( d )
        print "Characters cnt = " + str( cnt )
        for i in range( cnt ):
            ch = chr( int( d[i] ) )
            print "ch[" + str( i ) + "] = " + str( ch )
            stri += ch
        print stri

    def createWidgets(self):
        self.lblI2cAddr = Label( self, text='I2C addr:' )
        self.lblI2cAddr.grid( row=0, column=0, rowspan=1, columnspan=1 )
        
        self.i2cAddr = Entry( self )
        self.i2cAddr.insert( 0, "11110" )
        self.i2cAddr.grid( row=0, column=1, rowspan=1, columnspan=1 )

        self.lblDevAddr = Label( self, text='Dev addr:' )
        self.lblDevAddr.grid( row=0, column=2, rowspan=1, columnspan=1 )

        self.devAddr = Entry( self )
        self.devAddr.insert( 0, "0" )
        self.devAddr.grid( row=0, column=3, rowspan=1, columnspan=1 )

        self.lblCnt = Label( self, text='Bytes cnt:' )
        self.lblCnt.grid( row=0, column=4, rowspan=1, columnspan=1 )

        self.bytesCnt = Entry( self )
        self.bytesCnt.insert( 0, "128" )
        self.bytesCnt.grid( row=0, column=5, rowspan=1, columnspan=1 )


        self.i2cReadBtn = Button( self, text='Read I2C' )
        self.i2cReadBtn["command"] = self.readI2c
        self.i2cReadBtn.grid( row=1, column=0, rowspan=1, columnspan=1 )
        
        self.i2cWriteBtn = Button( self, text='Write I2C' )
        self.i2cWriteBtn["command"] = self.writeI2c
        self.i2cWriteBtn.grid( row=1, column=5, rowspan=1, columnspan=1 )
        
        self.text = Text( self )
        self.text.grid( row=2, column=0, rowspan=5, columnspan=6 )
    
        self.toTextBtn = Button( self, text = 'To string' )
        self.toTextBtn["command"] = self.toString
        self.toTextBtn.grid( row=8, column = 3 )
        
        

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()
        self.createWidgets()
        self.io = Supply()
        pp = self.io.io( 0 )
        print "initial pp = " + str( pp )
    
    #~ for a in range( 24, 127 ):
        #~ res, d = self.read( a, 0, 4 )
        #~ print "                                      addr = " + str( a ) + ", res = " + str( res )


root = Tk()
app = Application( master=root )
app.mainloop()

Автор: ysasha

Источник


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js