9 years, 10 months ago.

E-Badge-Python-Utilities

Hi, Mr Liu.

I seek out your project to get display a image on my E-ink Display Shield. Especially, your image hex data shown bellow URL was the only success for my shield to display entire image data on it!. http://mbed.org/users/allankliu/code/LPC1114_EBadge/file/5032c106eea1/SSD1606/rom_image.h

But there are no converter, image data or documents on format( also SSD1606 's maker, E-Ink, and seeedstudio, etc). If you teach or notice me that how to make a graphics data from ordinaly BMP or JPG to 3096 2bytes array.

This image is evidence... /media/uploads/cobweb/--_-6-.jpg (sorry for incombinient to see Mirror image)

Sincerly Katsumi Imaizumi

Question relating to:

E-Paper Device (EPD) based MiniNote module, powered by mbed on LPC1114FBD48. Shared in public domain with enclosure in 3D step format, hardware interface compatible with microBUS interface. Anyone can contribute … EBadge, EPD, LPC1114, MiniNote

1 Answer

9 years, 10 months ago.

Hi, Mr. Kat Ima,

Welcome to our mysterious world of EPD. Although your image is slow to load, I can identify that image, which is used as ESL demo project in China.

The EPD manufacturer is OED, Guangzhou, China. To be frank, I am not sure about its controller manufacurer, the original code indicates as "SPD2701". However I can not find it. I used "SSD1606" since I found it is quite similiar to solomon's description regarding SSD1606, and its hardware is similiar to SSD1306.

All of the manufacturers will ask you to sign "NDA" first to get the specification. And I got different specifications from them because I signed different versions of NDA. None of them are complete.

Yes, you have to contact OED to get full specification.

Basically I reused the demo code and ported it to ARM, now to mbed C++ API. So far so good. It works on mbed platform.

I have to admit, NDA is a roadblock. That's why I decide to develop an open source projects to allow the makers concentrate on high level applications.

Display Buffer

  • OED 2.1" EPD is 172*72, with 2bpp, i.e. one byte for 4 pixels. (consider width=172)
  • Its scans directions are: scanning from pixel (0,0) to (0,72), then from (1,0 to 1,72). That's so-called MODE 3. You can find MODE 1/2/4 with different scanning directions.
  • By different regiser setup, you can get vertical and horizontal mirrored image.
  • Most of the fonts scanning direction is horizontal, so you have to rotate everything or use vertical scan directions.

A very simple method can help you to figure out how it works. By using different bit patterns, for example, B/W in half, Grey in quad, Checkbox, line and strip. You can get from my code.

Another way is rotating the raw image. like this one.

/media/uploads/allankliu/merged-172x72-rotate.png

Then it can show on screen as this.

/media/uploads/allankliu/dscf0442-processed.jpg

So far I am working on mbed API, you have depend on yourself to make it work on Arduino.

I can show you my previous working Python code, the new version is still under development.

The Python code will print byte array on screen, just copy and past them into C/C++ code.

pil_demo_qrcode_basic.py

import Image
import ImageEnhance
import ImageOps

# More detail on PIL, please visit
# http://effbot.org/imagingbook/image.htm {{/media/uploads/allankliu/merged-172x72-rotate.png}} 
# Allan K Liu

class panel:
    def __init__(self,width=172,height=72,color="L",bpp=2,scan="v",endian="msb"):
        width = width
        height = self.height
        color = self.color
        bpp = self.bpp
        scan = self.scan
        endian = self.endian

    def getRatio(self):
        ratio = float(self.width)/float(self.height)
        return ratio

def loadPanelParas():
    pass
    
def readRawImage():
    pass
    
def rotate(img):
    pass
def resize(img):
    pass
def move(img):
    pass
def crop(img):
    pass
def enhance(img):
    pass
def merge(img):
    pass
def codeGen(img):
    pass

   
def printscreen(img):
    iml = list(img.getdata())
    
    for i in range(len(iml)):
        val = iml[i]
        print "%02X"%val,
    print
    
def printscreen(img,x,y):
    iml = list(img.getdata())
    
    for i in range(y):
        for j in range(x):
            print "%02X"%(ol[j+i*x]),
        print
'''
    main loop: 
        read panel params, 
        read raw image for dimension, histogram(dynamic range), 
        enhance(contract), rotate, resize, crop, merge
        save it as *.bin file, with codegen in different coding style (c.*.asm)
'''    
def main():
    
    bin = []
    
    im = Image.open("merged-172x72-rotate.png")
    
    panel = panel.Panel(172,72,"L",2,"v","msb")
    
    #print im.format, im.size, im.mode
    # Save it as a temp BMP file
    # and invoke system default image viewer to show it 
    #im.show()

    (x,y) = im.size
    ratio2 = float(x)/float(y)
    ratio3 = float(y)/float(x)
    #print ratio2
    #print ratio3

    
    dither =im.convert(mode='P', dither=Image.FLOYDSTEINBERG)
    #dither.show()
    
    di = dither.convert('L')
    bw = ImageOps.mirror(di)
    #bw.show()
    (b,p) = bw.getextrema()
    print "B&W image ranges : %02X - %02X"%(b,p)
    
    bw.point(lambda i: (0xFF-i)&0xC0).show()
    
    #enh = ImageEnhance.Contrast(bpp2)
    #enh.enhance(2).show()
    
    (x,y)=bw.size
    ol =  list(bw.getdata())
    #print "x=%d,y=%d,len=%d"%(x,y,len(ol))
    
    print ">> co-ordinate"
    for i in range(y):
        for j in range(x):
            print "%02X%02x"%(i,j),
        print ""
     
    print ">> value"
    for i in range(y):
        for k in range(x):
            print "%02X"%(ol[k+i*72]),
        print ""
    '''    
    '''
    print ">> array"
    for i in range(len(ol)):
        #print "%02X"%(ol[i]>>5),
        print "%02X"%(ol[i]),
    
    for i in range(len(ol)/4):
        val0= ol[i*4]&0xC0
        val1= (ol[i*4+1]&0xC0)>>2
        val2= (ol[i*4+2]&0xC0)>>4
        val3= (ol[i*4+3]&0xC0)>>6
        val = val0+val1+val2+val3
        #print "%02X=%02X:%02X:%02X:%02X(%02X:%02X:%02X:%02X)"%(val,val0,val1,val2,val3,ol[i],ol[i+1],ol[i+2],ol[i+3])
        bin.append(val)
        
    #print "len=%d"%(len(bin))
    
    '''
    for i in range(len(bin)):
        print "%02X"%(bin[i]),
    '''
    
    #for i in range(len(bin)):
    for i in range(172):
        for j in range(18):
            #print "0x%02X:(%02X,%02X,%02X),"%(bin[j+i*18],i,j,j+i*18),
            print "0x%02X,"%(bin[j+i*18]),
        print
    
        
    
if __name__=='__main__' :main()

Accepted Answer

Hi, Mr Liu. Thank you very much for your answer and your suggestion on NDA. I got it. If I use your code above, I process maker's NDA by myself. You told above means just this? (If it is wrong, please notice) I had an experiment by myself with ImageMagick and osx hexdump command. That was succeeded, but Avrdude explained "verification error; content mismatch". I guess that 0xff too much continued. But I had luck, could wrote down code into arduino again. verification mismatch occurs if I use displayable image hex code, so white pixels continuation are halmful I think. If you have any comment about this issue, please open it. If I make it solve, post that how to solve it later :-).

posted by kat ima 24 Jun 2014

Hi, Kat Ima,

Yes, you have to contact OED to get full specification first. Or simply reusing source code from Seeedstudio and mbed.

I don't have a Mac, so I have no idea about hexdump.

Because EPD is only 2bpp for 4 grey level, so b00, b01, b10, b11 is valid for a pixel. 0xFF stands for full white for 4 pixels.

If you think continous 0xFF is harmful for checksum verfication, which may not be its root cause I guess. Athouth it has been reported by someone.

In order to test/verify that, you can use 0x00, 0x55, 0xAA, 0xFF for 3096 Bytes to have a smoke testing. Those stand for full black, grey 1, grey 2 and full white for whole screen.

Otherwise, you can randomly change 0xFF into any other values like 0xEE... every 128/256Byte. Single spark of pixel can be ignored from image.

Maybe you can try different Arduino IDE, even switch to Linux/Windows as cross-checking.

Yes. keep trying different ways to challange it.

posted by Kai Liu 24 Jun 2014

Hi, Mr. Liu. I have a success to all of the image. I did 2 chars into one short. Simply 0xff >> 8 | 0x00ff. So, avrdude wrote that of all, and I wrote ino sketch to write two chars from short int. On Int, 0xffff => -9999, endof record => -10000, Int array was defined as short int. Thank you for your suggestion and teach indeed.

posted by kat ima 25 Jun 2014