Help adding E-Ink display support

Posted on
of 2
/ 2
  • So it interesting. In your example the quick refresh works. But the buffer is transformed on the x plane? So the texted looks weird, well on my screen. So if you just uncomment out the fast flash the text goes back to normal?

    var screen = exports.connect(screenSettings);
    screen.hwReset().then(() => {
      return screen.init();
    }).then(() => {
      return screen.g.flip();
      return screen.setFastRefresh();
      console.log("last check");
      return screen.g.flip();
      console.log("did the flip");
  • So i added a command to set the position before it draws.

  • But with the above change sometimes the display freaks out. I see the busy pin toggle like crazy

  • Glad to hear. You lit a fire under me to get the whole BUSY thing working the way I wanted. In my haste, my original code just used timeouts for calls, which meant you had to be very careful when you called subsequent functions. So thanks for that! I think I'll fallback on timeouts for when the target device is a low-memory MCU (no Promises).

  • You can run Espruino on both of those. I just flashed the Watchy the other day and it works while plugged in (just drains the battery quickly). Pinetime is running an NRF52832 so it runs Espruino as well, but I think you have to open the case to get to the SWD pins (the default bootloader is not compatible with Espruino OTA update).

  • there may be other commands that need to be "waited" for. If you look at C source for these (heltec, waveshare) you'll see the spots where they wait for BUSY to clear. seeing BUSY go hi/lo a lot means your just sending commands, should not be a problem. But if the timing is off, you'll get weird effects like you've been seeing

  • This is Onyx Boox Note Air 2 Plus
    Features: E-ink, Android 11, perfect backlight, magnetic stylus included, etc.
    Would I buy this again now? Yes! Great tool 👍­rtYq-dow

    Nice review of this device 👆

  • You lit a fire under me

    Glad i could help. haha

    As for the command thing i can check that out. I have a logic analyzer hooked up. But when i first look at it. It seemed like the only few things that forced the bussy command down were Soft reset and telling it to update the screen.

  • @yngv126399

    Do you understand the difference's between display mode 1 and display mode 2?

    I would love the ability to do a long screen change and then a lot of small changes.

  • I'm pretty sure that's full refresh (mode 1) and partial refresh (mode 2). From the recommendations, it says if you use partial (which looks nicer and is faster), make sure that you occasionally do a full refresh to reset the display to known values. Also, power it off whenever you can.. not only does it save power, but the higher voltage can cause permanent damage.

    In my case, I'm setting up my desk clock to do a partial refresh every minute, and full refresh on the hour (like a visual "chime"). But I haven't powered it down (or put it to sleep) in 3 weeks! Guess I'd better get on that...

  • So i'm trying to figure out how to both.

    Have fast refreshing and full refreshing.

    But it seems like when i try Display mode 1. It reverts the Wave forms to a slow version.

  • So i tried using registers B1 or B9 to see if i could tell it to load the LUT table again. But didn't seem to work.

  • My understanding is you don't get both.. you get one or the other. Mode 1 for slow, but no ghosting and known state (you should always START with this), or Mode 2 for faster, some ghosting. And according to what I've read, make sure you revert to full refresh regularly to keep the screen "healthy"

    From the spec, I see parameters B1 and B9 for command 0x22... but I don't understand their use. I use CF for a Mode 2, and if you don't call the command it appears to default to Mode 1 (after reset anyway)

  • I'd share my code, but I don't feel it's ready.. I'd like to implement a queue for all commands, since it's possible that during a full refresh (which will return to the event loop) another command could be requested at a bad time. But then I can share a simple clock that updates every minute (fast refresh) an updates full every hour. I just have some other work ahead of that... :-/

  • I don't entirely understand what your trying to build?

    I'm guessing the end use would just make a "settimeout" function for the drawing function and from a list of object to draw to the screen. If its doing a full redraw then don't do anything for this itteration of the loop.

    But this queue mechanism is it built into the screen driver or ontop of it?

  • No setTimeout(); has to be a check for BUSY going low (or is already low). You can say "don't do anything" while it's doing a full redraw and right now that's exactly what I am doing, but someone else who may use this module may not heed that and see issues and not understand. So it's trying to foolproof the driver. In particular, I would override g.flip()/g.setPartial()/g.reset() so they all get queued and only perform in the intended order and when BUSY is low

  • I'm very interested to see what you end up doing. I'm interested to know what your actually queuing. Since i don't know if many microcontrollers can queue many image buffers.

    But i would recommend making this built on top of the driver instead of being part of the driver. Since this is a e-ink display. So there's is a high probability that there not refreshing the display fast at all.

  • So i haven't fully tested this, but this is the intended way i was going to get around the idea of somebody trying to draw something when the pin is busy.

    This will only stop one draw that was added too quickly. Since its still using the same image buffer. But its primarily there just so it doesn't crash the board.

    SSD16xx.prototype.flip = function() {
        if(this.unInit == true){
          return this.init().then(()=>{
            return this.flip();
          return new Promise((resolve)=>{
              setWatch(resolve, this.busyPin, { repeat:false, edge:'falling' });
             return this.flip();
        this.scd(0x4E, 0);
        this.scd(0x4F, [0,0]);
        if(this.partialTran == true){
          this.partialTran = false;
            this.scd(0x4E, 0);
            this.scd(0x4F, [0,0]);
        return this.refreshDisplay();
  • Memory is always and issue: queuing image buffers for partial update could quickly throw you into out-of-memory hell.

    Going w/ the queueing idea, I suggest the following when a) is possible:
    a) Have a full buffer for the EDispay
    b) Use the Graphics to update the buffer and queue the info about rectangle that has been changed
    c) Have a worker that selects and transfers by the queued information from the buffer to the EDisplay


    1. Regarding b) I don't know if the current Graphics implementation provides you with the rectangle information after a draw command. Depending of such implementation, it could feed that queue. May be on creation of the Graphics object, an additional parameter/option could be passed that provides that queue.
    2. Lot's of small overlapping things, such updating the second hand in an analog clock, could become inefficient. Therefore, the algorithm that selects the rectangle to display could be smarter then just take what is next in queue, and calculate a new rectangle containing multiple rectangles to achieve a higher overall-efficiency.
    3. Specific draw commands - specially for lines 'around' +45 and -45 degrees - could define multiple rectangles rather than one big one...

    I'm not sure if this thread of thought leads somewhere useful... ;-) ... :\ ... :(

    Quite some time ago when playing with a gps and a serially connected 320x240 260K color TFT, I ran into the issue of not having enough time between the 1s interval of inputs to update the screen... I resorted to store the gps info and only display where a changes was, and when I run out of time, to skip some of the changes: DIY Marine GPS using enhanced GPS Module, u-blox NEO-6M GPS receiver, and ILI9341 Module controlled 2.8" Color TFT LCD.

  • So if your trying to get the changed region from drawing you can do this.

    var changed =;
    var w = changed.x2 - changed.x1;
    var h = changed.y2 - changed.y1;

    This commands from the internal drawing library and shows you the region where the drawing library changed.

    First commit -partialFlip function

  • I posted my module here if you want to see what it looks like

    Merge request

    my repo here­/tree/SSD16XX

  • @user156811

    ...flip w/ non-falsy argument could be the partial flip.

    Question/Confirmation: Does a flip - full or partial - cover the changed region, does that reset changed? ...and how does a changed look like when nothing has changed?

    Btw, saw your other conversation of testing the setup of a Espruino doc page locally. It is something I never fully got working... of course it is a while back. I'm not tat much involved anymore as for some time in the past. I though still like to follow what is going on in the Espruino world.

  • A flip() send a complete buffer over. I don't have code to send over a partial buffer over to the display at the moment.

    A partial refresh is when you don't go through the entire process to change the pixels.

    What does a change look like when nothing has changed? So if you run flip it will currently just flash the entire screen again. So if your on full refresh. Then it will blink a lot. If your on partial refresh. Then it will do nothing in terms of visible changes. But that's not good for the display.

    So i made a video about what i've learned playing around with these displays. Not the best because i just wanted to get it done before the holidays.­yIk

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

Help adding E-Ink display support

Posted by Avatar for user156811 @user156811