Некоторые производители лазерных принтеров встраивают в картриджи чипы 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 скрипт на Питон-е, который, собственно, общается с картриджем и выдаёт, что в нём записано.
В общем, картридж подключаем к шине I2C.
Контакты на картридже подписаны…, прям как-будто специально, чтобы люди сами EEPROM перезаписывали!
Проблемой оказалось установить, что же конкретно за EEPROM чип установлен (т.к. по маркировке в Google ничего не находится). Пришлось действовать перебором всех I2C адресов. Этот конкретный чип откликается на адреса 25, 30 и 40. По адресу 40, по всей видимости, располагается номер страницы памяти, а по адресу 30 оказались сами данные. Прочитав данные 2 раза до и после печати тестовой страницы удалось установить, какие именно байты меняются при печати страниц.
К сожалению, в явном виде выяснить, в каком именно виде закодировано число страниц, не удалось: меняется сразу 4 байта на глаз довольно случайным образом. Пытаться расшифровывать — лень. Поэтому было решено просто сохранить прошивку еще “живого” картриджа, а при приближении к критическим 700 страницам прошивку откатывать до сохранённого значения.
Таким образом, замена картриджа оттягивается на, надеемся, неограниченное время!
#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.
}
}
#!/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