Help adding E-Ink display support #6872
Replies: 1 comment
-
Posted at 2023-11-17 by user156811 So this page loads the pcd8544 from a javascript version of the library Here's the code With the list of other modules being here. Posted at 2023-11-17 by @gfwilliams Hi - so this is a GDEH0154D67 display? I don't think we have any driver for that unfortunately. Basically, you have two options:
The vast majority of display drivers for Espruino are actually written in JS, for example all the ones at https://www.espruino.com/Graphics#graphics-drivers You can just develop the driver in the Web IDE, and then if you want to you can contribute it back to https://github.com/espruino/EspruinoDocs/tree/master/devices (which would be great!) - there's a tutorial at https://www.espruino.com/Writing+Modules and you could one of those existing graphics drivers listed above as a base. Honestly, the JS module is the way I'd say was preferable - Espruino's Graphics library can usually do all the heavy lifting for you, and then all you have to do is write the relevant JS code to push the graphics buffer you get out over SPI to the display. For epaper especially the slightly reduced speed over native won't be a massive issue as the update takes a while anyway. If you have issues I'm happy to try and help with the code you'd need to get it working Posted at 2023-11-17 by user156811 @gfwilliams, Thanks for the info. I'm currently working on making a JS module right now. But I'm stuck because it says the module can't be found. My setup
I'm i suppose to just put the code in the main file? I saw export is not supported. Posted at 2023-11-17 by @thyttan Just to make sure:
If that wasn't it:
or
My reasoning for these proposals is based on these links: Posted at 2023-11-17 by user156811 My bad. In my code it is require("eink") I did try eink.js, but that didn't solve anything. The function require will look for any file that is either .min.js or .js. I found this that i'm going to try. This section talks about the proper way to test a module. Posted at 2023-11-17 by @gfwilliams I'd actually create a new tab in the IDE for your eink file, and choose to save to storage as a file called 'eink'. Then you can upload, and go to the other tab and then upload that one to test. Or... if I'm just wanting to iterate quickly, sometimes I just put At least then you can just change+upload+test very quickly. Posted at 2023-11-20 by yngv126399 I'm currently working on some e-ink screens, and I have working JS code for the Waveshare 1.54 3-color and a Weact 2.19" (very similar: SSD168x drivers). I think it should work with a watchy screen. LMK how you're progressing and I can share my code (still not a module, just need some time). BTW: I'm using my own board file for an NRF52832 breakout board, so it doesn't have any Bangle code built-in. Posted at 2023-11-20 by user156811 So this is what my module looks like I have the exact same displays. Posted at 2023-11-20 by user156811 I'm using the NRF52840 dev board. So you'll most likely going to have to swap the SPI pins Posted at 2023-11-21 by yngv126399 By partial, do you mean "partial update" (updating a subsection of the screen) or partial (i.e. "fast") refresh? You've got your partialFlip() implementing a little of both. I've never used partial screen refresh as it's a lot of work to save a few milliseconds while the screen still takes 2 sec to display. As for a "partial refresh", you need to set it up first with a few commands (reinitialize, set the partial LUT), then when you flip, you include the extra command (0x22, 0xcf) before update (0x20). Looks like your partial update is requesting the updated dimensions of the buffer, but it seems to return the entire buffer area. Therefore, you're recreating the entire buffer in JS and sending it, so it takes quite a bit of time. I'm working on getting partial refresh put into your sample. It seems my Watchy is not honoring the busy pin well... Posted at 2023-11-21 by yngv126399 I've got some code that may be helpful. I'm able to do a partial refresh on my Watchy using this, although the LUT is not quite right (seems very dark compared to regular full refresh). First of all, I extended some of your commands.. I didn't see you calling cmd(0x12) (software reset) so I created a fullReset():
I also use this to send commands WITH data, then wait (if necessary) for the RST pin (some commands are so fast that a watch on RST falling doesn't register):
This sets up using partial refresh by uploading a special LUT:
It sets an internal flag (partial) so the flip() command knows what to send:
That much works for me on the Watchy. Hope it helps. Like I said, LUTs are tricky things, so your results may be better/worse. I'm still trying to figure the out... Posted at 2023-11-21 by user156811 This is awesome! Thanks so much. I was wondering if you could post the entire thing into a github Gist. For some reason i'm getting some weird results when i try to compile it on my end. Also i'm excited your working with a watchy as well. What are you trying to do with it. Posted at 2023-11-22 by Serj E-ink is cool! Posted at 2023-11-22 by user156811
Very cool Posted at 2023-11-22 by user156811 So I updated the code here with your changes and this is the result i got. Now the display updates without a full refresh, but only partly. Does yours look perfect. Also how did you find that LUT table and some of the other info. The datasheets are extremely bad. For me this video gave me a lot more info on how these work. https://gist.github.com/brendena/154afdffb2355c1cc845b67c6c258dbe Attachments: Posted at 2023-11-22 by @gfwilliams This looks great! When you're happy with this I'd love to try and pull the module in to the main Espruino repo - I guess it could replace the SSD1606 module as well? Posted at 2023-11-22 by user156811
Thats kind of the goal. It "should/want" it to work with many of the SSD16xx devices in a general form. But i'm guessing it would be best to leave the SDD1606 up for backwards support. I plan to make a pull request when i have all my e-ink screens work properly. I also need to figure out the best way to show known configurations. In the SDD1606 library it was planed to have all the configurations in the library code itself. But the LUT tables can be fairly large, like 150+ bytes. That's potentially for each different display driver. So i don't know if its best to put that into the library as code or have like a readme where they can paste there configurations for there given display. Posted at 2023-11-22 by yngv126399 Here is a gist of what works on my Watchy: it runs through your original demo, then does a partial refresh demo. It needs to be cleaned up before becoming a module. Not sure if this is compatible with SSD1606 or not, I don't have one to try. I know it's not compatible with IL3829 (an older 1.54 screen). It's not perfect, no. On my screen the regular update creates faint, but crisp edges for text and shapes, whereas the partial refresh thickens everything up (it's easier to read, just not as precise). I'm still trying to figure this stuff out. As for the LUT, it's from the Heltec site where my first 1.54 came from: Screens vary a LOT from what I understand, so maybe try a Waveshare source file too? Posted at 2023-11-22 by yngv126399 As for the Watchy, I wanted an e-ink watch to develop with. But I eschew the big compile, push full firmware approach for just tweaking watch faces, so I'm a huge fan of Espruino everywhere. Fortunately, it's an ESP32 so I just pushed a existing build to it and it ran OOB. It also ran out of battery in 30 minutes... as ESP32s need a lot of power management to last on a battery. I hope to tweak the firmware to do what the Watchy code does: shutdown completely and wake on RTC or button press. Some day... Posted at 2023-11-22 by @gfwilliams
Interesting about the LUTs - yes, best keep them out of the library if we're targeting multiple devices. Maybe we could have separate library files with them in? For example:
One other thing to add - if you've got a big Uint8Array you can use Posted at 2023-11-22 by yngv126399 What tablet is that? I think I need one... Posted at 2023-11-22 by yngv126399 I get the same thing here... there's some trouble with the Promises somewhere. Did you try running my gist? I've got it working on both Watchy and a 52832 breakout board with a WaveShare. I can't look into it right now though... Posted at 2023-11-22 by user156811
Thats my thought as well. I have a watchy watch and a pine time watch. Both have a OS that is compiled with all the apps. So it a pain to work with. Espruino seems like the best thing for this. Posted at 2023-11-22 by user156811 So the text seem to drawing seemed to be transformed on the X axis with your Gist. So i added the calls to set the position after you refreshed the screen. Now the text looks great Posted at 2023-11-23 by user156811 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?
Posted at 2023-11-23 by user156811 So i added a command to set the position before it draws. Posted at 2023-11-23 by user156811 But with the above change sometimes the display freaks out. I see the busy pin toggle like crazy Posted at 2023-11-23 by yngv126399 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). Posted at 2023-11-23 by yngv126399 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). Posted at 2023-11-23 by yngv126399 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 Posted at 2023-11-24 by Serj This is Onyx Boox Note Air 2 Plus https://youtu.be/St5bZDCWRpE?si=tUdnQ-TcrtYq-dow Posted at 2023-11-26 by user156811
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. Posted at 2023-11-27 by user156811 @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. Posted at 2023-11-27 by yngv126399 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... Posted at 2023-11-29 by user156811 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. Posted at 2023-11-29 by user156811 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. Posted at 2023-11-30 by yngv126399 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) Posted at 2023-11-30 by yngv126399 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... :-/ Posted at 2023-11-30 by user156811 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? Posted at 2023-12-01 by yngv126399 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 Posted at 2023-12-01 by user156811 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. Posted at 2023-12-01 by user156811 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.
Posted at 2023-12-02 by @allObjects 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: Caveats:
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. Posted at 2023-12-02 by user156811 So if your trying to get the changed region from drawing you can do this.
This commands from the internal drawing library and shows you the region where the drawing library changed. First commit -partialFlip function Posted at 2023-12-21 by user156811 I posted my module here if you want to see what it looks like Merge request my repo here Posted at 2023-12-21 by @allObjects @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. Posted at 2023-12-22 by user156811 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. https://www.youtube.com/watch?v=MSgwCyRCyIk |
Beta Was this translation helpful? Give feedback.
-
Posted at 2023-11-16 by user156811
I'm trying to add a eink display to the libs/graphics module.
My goals are to
First have this excisable from just a normal to normal microcontrollers.
Second possible port this to the bangleJS library. There's a watch that uses the same exact display i'm working with. https://watchy.sqfmi.com/
My first question is how do you use a display without using BangleJS software suit.
I see this example
https://www.espruino.com/Onboard+LCD
I see the lcd_pcd8544.h/c files but i don't understand how they get accessed from the javascript side. There's no binding javascript wrapper comment code?
Beta Was this translation helpful? Give feedback.
All reactions