I2C LCD: code inside
-
Hi all,
I'm new on this forum, and with LoPy also.
I want to share my experience with 0.91" 128x32 I2C OLED Display, I forced it to work with LoPy.
Its not mentioned clearly in the manual, i2c pins are G16,G17 on the prototype board (P9-P10 on the LoPy).So, it works :)

[0_1479599353875_DSC_0609.JPG]
(It looks like inserting screenshots is not working here btw :) )Code here: https://github.com/dmitryelj/LoPyI2CLCD
Its only a prototype, will be improved later
PS: Update from 17.12.2016: SPI version added too.
-
Strings support added:

Usage is easy, example:
import sys, machine # Types: 1 - 128x32 I2C OLED Display, 2 - 128x64 I2C OLED Display displayType = 1 initialize(displayType) set_contrast(128) # 1-255 displayOn() clearBuffer() addString(0, 0, sys.platform + " " + sys.version) addString(0, 1, "---") addString(0, 2, "CPU: {} MHz".format(machine.freq()[0]/1000000)) drawBuffer() -
@DVE Awesome!, thanks for sharing :-)
-
@DVE Great! What about 128x64 version, because I saw some references in the code? Is it working?
-
128x64 display support was added to the source code, but I don't have a real display to test. So, you can try, but no guarantee :)
-
@DVE OK, thanks. I've just ordered one so I'll have the chance to check and let you know.
-
@DVE said in I2C LCD: code inside:
Strings support added:

Usage is easy, example:
import sys, machine initialize() set_contrast(128) # 1-255 displayOn() clearBuffer() addString(0, 0, sys.platform + " " + sys.version) addString(0, 1, "---") addString(0, 2, "CPU: {} MHz".format(machine.freq()[0]/1000000)) drawBuffer()@DVE Thanks for the code! I ordered a 128x96 display from Adafruit last week and expect to get it on Wednesday. When I have it in-hand, I will test your code and send a pull request over if I can get working. =)
-
@brotherdust said in I2C LCD: code inside:
@DVE said in I2C LCD: code inside:
Strings support added:

Usage is easy, example:
import sys, machine initialize() set_contrast(128) # 1-255 displayOn() clearBuffer() addString(0, 0, sys.platform + " " + sys.version) addString(0, 1, "---") addString(0, 2, "CPU: {} MHz".format(machine.freq()[0]/1000000)) drawBuffer()@DVE Thanks for the code! I ordered a 128x96 display from Adafruit last week and expect to get it on Wednesday. When I have it in-hand, I will test your code and send a pull request over if I can get working. =)
ahem Sorry. 128x64.
-
@DVE So, I tried your code on my 128x64. It just shows up as a bunch of random noise on the display. =( I dug through the MP codebase and found this: https://github.com/pycom/pycom-micropython/blob/master/drivers/display/ssd1306.py
Lo and behold! It's for the same display driver as what we have! Wait.. no framebuf module.
Twiddled with the make configurations and managed to make a build with framebuf included. Trying it now. It's late so I might not report back until tomorrow.
-
@brotherdust Waiting for the news. :o)
-
@brotherdust, thanks for the link. I can test this driver also.
-
@rskoniec said in I2C LCD: code inside:
@brotherdust Waiting for the news. :o)
Sorry! I was hacking on NyQuil last night. I got the framebuf code to compile and loaded it up on to one of my LoPy's. Tried the driver code, but all I got was the same result: a screen full of static. Same result as when I used @DVE 's code. Maybe my module is defective? Not sure. Anyway, tried a few things, couldn't get it to do anything different, then rage-quit because I wasn't clear-headed enough to figure out what the LoPy was telling me on the console. heh. Either of you have any luck?
-
Here's what I'm putting in:
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces from micropython import const import time import framebuf # register definitions SET_CONTRAST = const(0x81) SET_ENTIRE_ON = const(0xa4) SET_NORM_INV = const(0xa6) SET_DISP = const(0xae) SET_MEM_ADDR = const(0x20) SET_COL_ADDR = const(0x21) SET_PAGE_ADDR = const(0x22) SET_DISP_START_LINE = const(0x40) SET_SEG_REMAP = const(0xa0) SET_MUX_RATIO = const(0xa8) SET_COM_OUT_DIR = const(0xc0) SET_DISP_OFFSET = const(0xd3) SET_COM_PIN_CFG = const(0xda) SET_DISP_CLK_DIV = const(0xd5) SET_PRECHARGE = const(0xd9) SET_VCOM_DESEL = const(0xdb) SET_CHARGE_PUMP = const(0x8d) class SSD1306: def __init__(self, width, height, external_vcc): self.width = width self.height = height self.external_vcc = external_vcc self.pages = self.height // 8 self.buffer = bytearray(self.pages * self.width) self.framebuf = framebuf.FrameBuffer1(self.buffer, self.width, self.height) self.poweron() self.init_display() def init_display(self): for cmd in ( SET_DISP | 0x00, # off # address setting SET_MEM_ADDR, 0x00, # horizontal # resolution and layout SET_DISP_START_LINE | 0x00, SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 SET_MUX_RATIO, self.height - 1, SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 SET_DISP_OFFSET, 0x00, SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, # timing and driving scheme SET_DISP_CLK_DIV, 0x80, SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, SET_VCOM_DESEL, 0x30, # 0.83*Vcc # display SET_CONTRAST, 0xff, # maximum SET_ENTIRE_ON, # output follows RAM contents SET_NORM_INV, # not inverted # charge pump SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, SET_DISP | 0x01): # on self.write_cmd(cmd) self.fill(0) self.show() def poweroff(self): self.write_cmd(SET_DISP | 0x00) def contrast(self, contrast): self.write_cmd(SET_CONTRAST) self.write_cmd(contrast) def invert(self, invert): self.write_cmd(SET_NORM_INV | (invert & 1)) def show(self): x0 = 0 x1 = self.width - 1 if self.width == 64: # displays with width of 64 pixels are shifted by 32 x0 += 32 x1 += 32 self.write_cmd(SET_COL_ADDR) self.write_cmd(x0) self.write_cmd(x1) self.write_cmd(SET_PAGE_ADDR) self.write_cmd(0) self.write_cmd(self.pages - 1) self.write_data(self.buffer) def fill(self, col): self.framebuf.fill(col) def pixel(self, x, y, col): self.framebuf.pixel(x, y, col) def scroll(self, dx, dy): self.framebuf.scroll(dx, dy) def text(self, string, x, y, col=1): self.framebuf.text(string, x, y, col) class SSD1306_I2C(SSD1306): def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): self.i2c = i2c self.addr = addr self.temp = bytearray(2) super().__init__(width, height, external_vcc) def write_cmd(self, cmd): self.temp[0] = 0x80 # Co=1, D/C#=0 self.temp[1] = cmd self.i2c.writeto(self.addr, self.temp) def write_data(self, buf): self.temp[0] = self.addr << 1 self.temp[1] = 0x40 # Co=0, D/C#=1 self.i2c.start() self.i2c.write(self.temp) self.i2c.write(buf) self.i2c.stop() def poweron(self): pass from machine import I2C i2c = I2C(0, I2C.MASTER, baudrate=1000000) display = SSD1306_I2C(width=128, height=64, i2c=i2c, addr=0x3d)The 128x64 display has hardware address of 0x3d. Anyway, here's what comes out:
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 109, in __init__ File "<stdin>", line 37, in __init__ File "<stdin>", line 64, in init_display File "<stdin>", line 89, in show File "<stdin>", line 119, in write_data AttributeError: 'I2C' object has no attribute 'start'The display turns on, but it's full of what looks like random noise. I looked through the source code for the I2C module and the start method is there:
https://github.com/pycom/pycom-micropython/blob/master/esp32/mods/machine_i2c.c#L348STATIC mp_obj_t machine_i2c_start(mp_obj_t self_in) { machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_hal_i2c_start(self); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_start_obj, machine_i2c_start);>>> help(machine.I2C) <class 'I2C'>object is of type type init -- <function> scan -- <function> readfrom -- <function> readfrom_into -- <function> writeto -- <function> readfrom_mem -- <function> readfrom_mem_into -- <function> writeto_mem -- <function> MASTER -- 0I don't get why it's not present in the list of available methods for this class. =/
-
@DVE Update on using the built-in framebuffer and SSD1306 driver:
The driver was using I2C "primitives" that were disabled in the esp32 version of machine_i2c. The recent commits I've made on my relevant github repo:
https://github.com/brotherdust/pycom-micropython/commits/i2c-framebufI've made progress; but still learning how to use this stuff!
-
It works! Yay! Code later. Sleep now.
-
@brotherdust Its great :)
I ordered 128x64 display too on Ebay, will be able to test next week.
@brotherdust said in I2C LCD: code inside:
The driver was using I2C "primitives" that were disabled in the esp32 version of machine_i2c
How it works? Do you need to reload a firmware with enabled primitives?
-
@brotherdust Any chance for a code and answers for @DVE questions?
-
@rskoniec said in I2C LCD: code inside:
@brotherdust Any chance for a code and answers for @DVE questions?
@rskoniec, my apologies. I thought that I had answered this and didn't give it any more thought.
The commit I referenced enables the I2C primitives. You'll need to recompile the firmware and upload it. Then the code will work.
After reviewing the code a bit more, I think it can be refactored not to use the primitives (I2C.start, stop, etc). The primitives are already a part of other, more high-level routines. But the easiest way to get the driver going was just to enable those functions. =) I'm too lazy to re-factor and go through all the pull requests and justifications to get it included upstream.
-
@brotherdust Thank you very much for quick reply. :o)
-
Got the 128x64 display this week. Stupid chinese seller sent SPI version instead of I2C.
But I was able to add SPI support as well :)

Will update code soon.
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login